use std::{borrow::{Borrow, Cow}, ffi::CString, fmt, io::{self, ErrorKind}, mem, os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, OwnedHandle, RawHandle}};
use bitflags::bitflags;
use nt_time::FileTime;
use windows::{Win32::{Foundation::HANDLE, System::Threading::{CreateEventA, SetEvent}}, core::{Error, PCSTR, PCWSTR}};
use crate::{AbsoluteTime, RelativeTime, TimerFd, error::{TimerErrorType, TimerResult}, map_portable_err, map_timer_err, timer_portable::{TimerExpMode, portable_error::TimerPortableErr, timer::{ModeTimeType, TimerId}}};
pub mod timer_fd_windows;
pub mod timer_poll;
impl From<RawHandle> for TimerId
{
fn from(value: RawHandle) -> Self
{
return Self(value as usize);
}
}
impl PartialEq<RawHandle> for TimerId
{
fn eq(&self, other: &RawHandle) -> bool
{
return self.0 == *other as usize;
}
}
#[derive(Debug)]
pub(crate) struct EventFd
{
handler: OwnedHandle
}
impl AsRawHandle for EventFd
{
fn as_raw_handle(&self) -> RawHandle
{
return self.handler.as_raw_handle()
}
}
impl AsHandle for EventFd
{
fn as_handle(&self) -> BorrowedHandle<'_>
{
return self.handler.as_handle();
}
}
impl EventFd
{
pub(crate)
fn new(label: Cow<'static, str>) -> TimerResult<Self>
{
let label_cstr =
CString::new(label.as_ref())
.map_err(|e|
map_timer_err!(TimerErrorType::Conversion, "CString error '{}' converting '{}'",
e, label)
)?;
let hndl =
unsafe
{
CreateEventA(None, false, false,
PCSTR::from_raw( mem::transmute(label_cstr.as_ptr())))
.map_err(|e|
map_timer_err!(TimerErrorType::EPoll(e), "CreateEventA failed")
)?
};
return Ok(Self{ handler: unsafe { OwnedHandle::from_raw_handle(hndl.0) } })
}
pub(crate)
fn write(&self, _val: usize) -> Result<(), Error>
{
return
unsafe
{
SetEvent(HANDLE(self.handler.as_raw_handle()))
};
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct timespec
{
pub tv_sec: i64,
pub tv_nsec: i64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct itimerspec
{
pub it_interval: i32,
pub it_value: i64,
}
impl From<timespec> for RelativeTime
{
fn from(time_spec: timespec) -> Self
{
return
Self::new_time(time_spec.tv_sec, time_spec.tv_nsec);
}
}
impl From<timespec> for AbsoluteTime
{
fn from(time_spec: timespec) -> Self
{
return
unsafe
{
Self::new_time_unchecked(time_spec.tv_sec, time_spec.tv_nsec)
};
}
}
#[allow(non_camel_case_types)]
#[repr(i32)]
#[derive(Debug)]
pub enum TimerType
{
CLOCK_REALTIME = 1,
CLOCK_MONOTONIC = 2,
}
bitflags! {
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct TimerFlags: i32
{
const TFD_NONBLOCK = 1;
const TFD_CLOEXEC = 2;
}
}
bitflags! {
#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
pub struct TimerSetTimeFlags: i32
{
const TFD_TIMER_ABSTIME = 1;
const TFD_TIMER_CANCEL_ON_SET = 2;
}
}
pub trait UnixFd: AsHandle + AsRawHandle + PartialEq<RawHandle> {}
impl UnixFd for TimerFd {}
impl PartialEq<RawHandle> for TimerFd
{
fn eq(&self, other: &RawHandle) -> bool
{
return self.as_raw_handle().eq(other);
}
}
impl AsHandle for TimerFd
{
#[inline]
fn as_handle(&self) -> BorrowedHandle<'_>
{
return self.get_timer().as_handle();
}
}
impl AsRawHandle for TimerFd
{
#[inline]
fn as_raw_handle(&self) -> RawHandle
{
return self.get_timer().as_raw_handle();
}
}
impl<TIMERTYPE: ModeTimeType> TryFrom<TimerExpMode<TIMERTYPE>> for itimerspec
{
type Error = TimerPortableErr;
fn try_from(value: TimerExpMode<TIMERTYPE>) -> Result<Self, Self::Error>
{
return (&value).try_into();
}
}
impl<TIMERTYPE: ModeTimeType> TryFrom<&TimerExpMode<TIMERTYPE>> for itimerspec
{
type Error = TimerPortableErr;
fn try_from(value: &TimerExpMode<TIMERTYPE>) -> Result<Self, Self::Error>
{
match value
{
TimerExpMode::None =>
return Ok(
itimerspec
{
it_interval: 0,
it_value: 0
}
),
TimerExpMode::OneShot{ timeout} =>
{
if TIMERTYPE::get_flags().intersects(TimerSetTimeFlags::TFD_TIMER_ABSTIME) == true
{
let it_value =
FileTime::from_unix_time(timeout.get_sec() as i64, timeout.get_nsec() as u32)
.map_err(|e|
map_portable_err!(Error::empty(), "cannot convert unix time to filetime, err: '{}'", e)
)?
.to_raw() as i64;
return Ok(
itimerspec
{
it_interval: 0,
it_value: it_value
}
);
}
else
{
return Ok(
itimerspec
{
it_interval: 0,
it_value: -((timeout.get_sec() * RelativeTime::MAX_NS + timeout.get_nsec()) / 100)
}
);
}
},
TimerExpMode::IntervalDelayed{ delay_tm, interv_tm } =>
{
if TIMERTYPE::get_flags().intersects(TimerSetTimeFlags::TFD_TIMER_ABSTIME) == true
{
let it_value =
FileTime::from_unix_time(delay_tm.get_sec() as i64, delay_tm.get_nsec() as u32)
.map_err(|e|
map_portable_err!(Error::empty(), "cannot convert unix time to filetime, err: '{}'", e)
)?
.to_raw() as i64;
return Ok(
itimerspec
{
it_interval:
(((interv_tm.get_sec() * RelativeTime::MAX_NS + interv_tm.get_nsec()) / 1_000_000) & 0xFFFFFFFF) as i32,
it_value:
it_value
}
);
}
else
{
return Ok(
itimerspec
{
it_interval:
(((interv_tm.get_sec() * RelativeTime::MAX_NS + interv_tm.get_nsec()) / 1_000_000) & 0xFFFFFFFF) as i32,
it_value:
-((delay_tm.get_sec() * RelativeTime::MAX_NS + delay_tm.get_nsec()) / 100)
}
);
}
},
TimerExpMode::Interval{ interv_tm } =>
{
if TIMERTYPE::get_flags().intersects(TimerSetTimeFlags::TFD_TIMER_ABSTIME) == true
{
let it_value =
FileTime::from_unix_time(interv_tm.get_sec() as i64, interv_tm.get_nsec() as u32)
.map_err(|e|
map_portable_err!(Error::empty(), "cannot convert unix time to filetime, err: '{}'", e)
)?
.to_raw() as i64;
return Ok(
itimerspec
{
it_interval:
(((interv_tm.get_sec() * RelativeTime::MAX_NS + interv_tm.get_nsec()) / 1_000_000) & 0xFFFFFFFF) as i32,
it_value:
it_value
}
);
}
else
{
return Ok(
itimerspec
{
it_interval:
(((interv_tm.get_sec() * RelativeTime::MAX_NS + interv_tm.get_nsec()) / 1_000_000) & 0xFFFFFFFF) as i32,
it_value:
-((interv_tm.get_sec() * RelativeTime::MAX_NS + interv_tm.get_nsec()) / 100)
}
);
}
}
}
}
}