dispatcher.rs

  1#![allow(non_upper_case_globals)]
  2#![allow(non_camel_case_types)]
  3#![allow(non_snake_case)]
  4
  5use crate::{
  6    GLOBAL_THREAD_TIMINGS, PlatformDispatcher, RunnableMeta, RunnableVariant, THREAD_TIMINGS,
  7    TaskLabel, TaskTiming, ThreadTaskTimings,
  8};
  9
 10use async_task::Runnable;
 11use objc::{
 12    class, msg_send,
 13    runtime::{BOOL, YES},
 14    sel, sel_impl,
 15};
 16use std::{
 17    ffi::c_void,
 18    ptr::{NonNull, addr_of},
 19    time::{Duration, Instant},
 20};
 21
 22/// All items in the generated file are marked as pub, so we're gonna wrap it in a separate mod to prevent
 23/// these pub items from leaking into public API.
 24pub(crate) mod dispatch_sys {
 25    include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
 26}
 27
 28use dispatch_sys::*;
 29pub(crate) fn dispatch_get_main_queue() -> dispatch_queue_t {
 30    addr_of!(_dispatch_main_q) as *const _ as dispatch_queue_t
 31}
 32
 33pub(crate) struct MacDispatcher;
 34
 35impl PlatformDispatcher for MacDispatcher {
 36    fn get_all_timings(&self) -> Vec<ThreadTaskTimings> {
 37        let global_timings = GLOBAL_THREAD_TIMINGS.lock();
 38        ThreadTaskTimings::convert(&global_timings)
 39    }
 40
 41    fn get_current_thread_timings(&self) -> Vec<TaskTiming> {
 42        THREAD_TIMINGS.with(|timings| {
 43            let timings = &timings.lock().timings;
 44
 45            let mut vec = Vec::with_capacity(timings.len());
 46
 47            let (s1, s2) = timings.as_slices();
 48            vec.extend_from_slice(s1);
 49            vec.extend_from_slice(s2);
 50            vec
 51        })
 52    }
 53
 54    fn is_main_thread(&self) -> bool {
 55        let is_main_thread: BOOL = unsafe { msg_send![class!(NSThread), isMainThread] };
 56        is_main_thread == YES
 57    }
 58
 59    fn dispatch(&self, runnable: RunnableVariant, _: Option<TaskLabel>) {
 60        let (context, trampoline) = match runnable {
 61            RunnableVariant::Meta(runnable) => (
 62                runnable.into_raw().as_ptr() as *mut c_void,
 63                Some(trampoline as unsafe extern "C" fn(*mut c_void)),
 64            ),
 65            RunnableVariant::Compat(runnable) => (
 66                runnable.into_raw().as_ptr() as *mut c_void,
 67                Some(trampoline_compat as unsafe extern "C" fn(*mut c_void)),
 68            ),
 69        };
 70        unsafe {
 71            dispatch_async_f(
 72                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH.try_into().unwrap(), 0),
 73                context,
 74                trampoline,
 75            );
 76        }
 77    }
 78
 79    fn dispatch_on_main_thread(&self, runnable: RunnableVariant) {
 80        let (context, trampoline) = match runnable {
 81            RunnableVariant::Meta(runnable) => (
 82                runnable.into_raw().as_ptr() as *mut c_void,
 83                Some(trampoline as unsafe extern "C" fn(*mut c_void)),
 84            ),
 85            RunnableVariant::Compat(runnable) => (
 86                runnable.into_raw().as_ptr() as *mut c_void,
 87                Some(trampoline_compat as unsafe extern "C" fn(*mut c_void)),
 88            ),
 89        };
 90        unsafe {
 91            dispatch_async_f(dispatch_get_main_queue(), context, trampoline);
 92        }
 93    }
 94
 95    fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant) {
 96        let (context, trampoline) = match runnable {
 97            RunnableVariant::Meta(runnable) => (
 98                runnable.into_raw().as_ptr() as *mut c_void,
 99                Some(trampoline as unsafe extern "C" fn(*mut c_void)),
100            ),
101            RunnableVariant::Compat(runnable) => (
102                runnable.into_raw().as_ptr() as *mut c_void,
103                Some(trampoline_compat as unsafe extern "C" fn(*mut c_void)),
104            ),
105        };
106        unsafe {
107            let queue =
108                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH.try_into().unwrap(), 0);
109            let when = dispatch_time(DISPATCH_TIME_NOW as u64, duration.as_nanos() as i64);
110            dispatch_after_f(when, queue, context, trampoline);
111        }
112    }
113}
114
115extern "C" fn trampoline(runnable: *mut c_void) {
116    let task =
117        unsafe { Runnable::<RunnableMeta>::from_raw(NonNull::new_unchecked(runnable as *mut ())) };
118
119    let location = task.metadata().location;
120
121    let start = Instant::now();
122    let timing = TaskTiming {
123        location,
124        start,
125        end: None,
126    };
127
128    THREAD_TIMINGS.with(|timings| {
129        let mut timings = timings.lock();
130        let timings = &mut timings.timings;
131        if let Some(last_timing) = timings.iter_mut().rev().next() {
132            if last_timing.location == timing.location {
133                return;
134            }
135        }
136
137        timings.push_back(timing);
138    });
139
140    task.run();
141    let end = Instant::now();
142
143    THREAD_TIMINGS.with(|timings| {
144        let mut timings = timings.lock();
145        let timings = &mut timings.timings;
146        let Some(last_timing) = timings.iter_mut().rev().next() else {
147            return;
148        };
149        last_timing.end = Some(end);
150    });
151}
152
153extern "C" fn trampoline_compat(runnable: *mut c_void) {
154    let task = unsafe { Runnable::<()>::from_raw(NonNull::new_unchecked(runnable as *mut ())) };
155
156    let location = core::panic::Location::caller();
157
158    let start = Instant::now();
159    let timing = TaskTiming {
160        location,
161        start,
162        end: None,
163    };
164    THREAD_TIMINGS.with(|timings| {
165        let mut timings = timings.lock();
166        let timings = &mut timings.timings;
167        if let Some(last_timing) = timings.iter_mut().rev().next() {
168            if last_timing.location == timing.location {
169                return;
170            }
171        }
172
173        timings.push_back(timing);
174    });
175
176    task.run();
177    let end = Instant::now();
178
179    THREAD_TIMINGS.with(|timings| {
180        let mut timings = timings.lock();
181        let timings = &mut timings.timings;
182        let Some(last_timing) = timings.iter_mut().rev().next() else {
183            return;
184        };
185        last_timing.end = Some(end);
186    });
187}