1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! Timer for Apple OSes

use core::{time};

use crate::{TimerState, Timer};

use libc::{c_long, c_ulong, c_void, int64_t, uint64_t, uintptr_t};

#[allow(non_camel_case_types)]
mod ffi {
    use super::*;

    pub type dispatch_object_t = *const c_void;
    pub type dispatch_queue_t = *const c_void;
    pub type dispatch_source_t = *const c_void;
    pub type dispatch_source_type_t = *const c_void;
    pub type dispatch_time_t = uint64_t;

    pub const DISPATCH_TIME_NOW: dispatch_time_t = 0;
    pub const QOS_CLASS_DEFAULT: c_long = 0x15;

    extern "C" {
        pub static _dispatch_source_type_timer: c_long;

        pub fn dispatch_get_global_queue(identifier: c_long, flags: c_ulong) -> dispatch_queue_t;
        pub fn dispatch_source_create(type_: dispatch_source_type_t, handle: uintptr_t, mask: c_ulong, queue: dispatch_queue_t) -> dispatch_source_t;
        pub fn dispatch_source_set_timer(source: dispatch_source_t, start: dispatch_time_t, interval: uint64_t, leeway: uint64_t);
        pub fn dispatch_source_set_event_handler_f(source: dispatch_source_t, handler: unsafe extern "C" fn(*mut c_void));
        pub fn dispatch_set_context(object: dispatch_object_t, context: *mut c_void);
        pub fn dispatch_resume(object: dispatch_object_t);
        pub fn dispatch_suspend(object: dispatch_object_t);
        pub fn dispatch_release(object: dispatch_object_t);
        pub fn dispatch_source_cancel(object: dispatch_object_t);
        pub fn dispatch_time(when: dispatch_time_t, delta: int64_t) -> dispatch_time_t;
    }
}

unsafe extern "C" fn timer_handler(context: *mut c_void) {
    let state = context as *mut TimerState;

    (*state).wake();
}

unsafe extern "C" fn interval_handler(context: *mut c_void) {
    let state = context as *mut TimerState;

    (*state).wake_by_ref();
}

///Timer based on Apple APIs
pub struct AppleTimer {
    handle: ffi::dispatch_source_t,
    state: *const TimerState,
    //Suspension count. Incremented suspend, and decremented on each resume
    s_count: u8,
}

impl Timer for AppleTimer {
    fn new(state: *const TimerState) -> Self {
        let handle = unsafe {
            let queue = ffi::dispatch_get_global_queue(ffi::QOS_CLASS_DEFAULT, 0);
            ffi::dispatch_source_create(&ffi::_dispatch_source_type_timer as *const _ as ffi::dispatch_source_type_t, 0, 0, queue)
        };

        assert!(!handle.is_null());

        Self {
            handle,
            state,
            //Source is created with suspension count 1
            s_count: 1
        }
    }

    #[inline]
    fn reset(&mut self) {
        //If count is 0 (active) then we suspend it
        //in order to stop events
        if self.s_count == 0 {
            unsafe {
                ffi::dispatch_suspend(self.handle);
            }

            self.s_count += 1;
        }
    }

    fn start_delay(&mut self, timeout: time::Duration) {
        self.reset();

        unsafe {
            ffi::dispatch_source_set_event_handler_f(self.handle, timer_handler);
            ffi::dispatch_set_context(self.handle, self.state as *mut _);

            let start = ffi::dispatch_time(ffi::DISPATCH_TIME_NOW, timeout.as_nanos() as int64_t);
            ffi::dispatch_source_set_timer(self.handle, start, 0, 0);
            ffi::dispatch_resume(self.handle);
        }

        self.s_count -= 1;
    }

    fn start_interval(&mut self, interval: time::Duration) {
        self.reset();

        unsafe {
            ffi::dispatch_source_set_event_handler_f(self.handle, interval_handler);
            ffi::dispatch_set_context(self.handle, self.state as *mut _);

            let start = ffi::dispatch_time(ffi::DISPATCH_TIME_NOW, interval.as_nanos() as int64_t);
            ffi::dispatch_source_set_timer(self.handle, start, interval.as_nanos() as uint64_t, 0);
            ffi::dispatch_resume(self.handle);
        }

        self.s_count -= 1;
    }

    fn state(&self) -> &TimerState {
        match unsafe { self.state.as_ref() } {
            Some(state) => state,
            None => unreach!(),
        }
    }
}

impl Drop for AppleTimer {
    fn drop(&mut self) {
        unsafe {
            ffi::dispatch_source_cancel(self.handle);

            if self.s_count > 0 {
                //It is error to release while source is suspended
                //So we decrement it
                ffi::dispatch_resume(self.handle);
            }

            ffi::dispatch_release(self.handle);
        }
    }
}
unsafe impl Send for AppleTimer {}
unsafe impl Sync for AppleTimer {}