11#![ cfg( feature = "Foundation_NSThread" ) ]
22use core:: fmt;
33use core:: marker:: PhantomData ;
4+ use core:: mem:: { self , ManuallyDrop } ;
45use core:: panic:: { RefUnwindSafe , UnwindSafe } ;
56
67use crate :: common:: * ;
@@ -72,7 +73,7 @@ fn make_multithreaded() {
7273/// }
7374///
7475/// // Usage
75- /// let mtm = MainThreadMarker::new().unwrap( );
76+ /// let mtm = MainThreadMarker::new().expect("must be on the main thread" );
7677/// unsafe { do_thing(obj, mtm) }
7778/// ```
7879#[ derive( Clone , Copy , PartialEq , Eq , Hash , PartialOrd , Ord ) ]
@@ -88,6 +89,7 @@ impl MainThreadMarker {
8889 ///
8990 /// Returns [`None`] if the current thread was not the main thread.
9091 #[ cfg( feature = "Foundation_NSThread" ) ]
92+ #[ inline]
9193 pub fn new ( ) -> Option < Self > {
9294 if NSThread :: isMainThread_class ( ) {
9395 // SAFETY: We just checked that we are running on the main thread.
@@ -100,17 +102,207 @@ impl MainThreadMarker {
100102 /// Construct a new [`MainThreadMarker`] without first checking whether
101103 /// the current thread is the main one.
102104 ///
105+ ///
103106 /// # Safety
104107 ///
105108 /// The current thread must be the main thread.
109+ ///
110+ /// Alternatively, you may create this briefly if you know that a an API
111+ /// is safe in a specific case, but is not marked so. If you do that, you
112+ /// must ensure that any use of the marker is actually safe to do from
113+ /// another thread than the main one.
114+ #[ inline]
106115 pub unsafe fn new_unchecked ( ) -> Self {
107116 // SAFETY: Upheld by caller
117+ //
118+ // We can't debug_assert that this actually is the main thread, see
119+ // the comment above.
108120 Self { _priv : PhantomData }
109121 }
122+
123+ /// Submit the given closure to the runloop on the main thread.
124+ ///
125+ /// If the current thread is the main thread, this simply runs the
126+ /// closure.
127+ ///
128+ /// The closure is passed a [`MainThreadMarker`] that it can further use
129+ /// to access APIs that are only accessible from the main thread.
130+ ///
131+ /// This function should only be used in applications whose main thread is
132+ /// running an event loop with `dispatch_main`, `UIApplicationMain`,
133+ /// `NSApplicationMain`, `CFRunLoop` or similar; it will block
134+ /// indefinitely if that is not the case.
135+ ///
136+ ///
137+ /// # Example
138+ ///
139+ /// ```no_run
140+ /// use icrate::Foundation::MainThreadMarker;
141+ /// MainThreadMarker::run_on_main(|mtm| {
142+ /// // Do something on the main thread with the given marker
143+ /// });
144+ /// ```
145+ #[ cfg( feature = "dispatch" ) ]
146+ pub fn run_on_main < F , R > ( f : F ) -> R
147+ where
148+ F : Send + FnOnce ( MainThreadMarker ) -> R ,
149+ R : Send ,
150+ {
151+ if let Some ( mtm) = MainThreadMarker :: new ( ) {
152+ f ( mtm)
153+ } else {
154+ dispatch:: Queue :: main ( ) . exec_sync ( || {
155+ // SAFETY: The outer closure is submitted to run on the main
156+ // thread, so now, when the closure actually runs, it's
157+ // guaranteed to be on the main thread.
158+ f ( unsafe { MainThreadMarker :: new_unchecked ( ) } )
159+ } )
160+ }
161+ }
110162}
111163
112164impl fmt:: Debug for MainThreadMarker {
113165 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
114166 f. debug_tuple ( "MainThreadMarker" ) . finish ( )
115167 }
116168}
169+
170+ /// Make a type that can only be used on the main thread be `Send` + `Sync`.
171+ ///
172+ /// On `Drop`, the inner type is sent to the main thread's runloop and dropped
173+ /// there. This may lead to deadlocks if the main runloop is not running, or
174+ /// if it is waiting on a lock that the dropping thread is holding. See
175+ /// [`MainThreadMarker::run_on_main`] for some of the caveats around that.
176+ ///
177+ ///
178+ /// # Related
179+ ///
180+ /// This type takes inspiration from `threadbound::ThreadBound`.
181+ ///
182+ /// The functionality also somewhat resembles Swift's `@MainActor`, which
183+ /// ensures that a type is only usable from the main thread.
184+ #[ doc( alias = "@MainActor" ) ]
185+ #[ cfg( feature = "dispatch" ) ]
186+ pub struct MainThreadBound < T > ( ManuallyDrop < T > ) ;
187+
188+ // SAFETY: The inner value is guaranteed to originate from the main thread
189+ // because `new` takes [`MainThreadMarker`].
190+ //
191+ // `into_inner` is the only way to get the value out, and that is also
192+ // guaranteed to happen on the main thread.
193+ //
194+ // Finally, the value is dropped on the main thread in `Drop`.
195+ #[ cfg( feature = "dispatch" ) ]
196+ unsafe impl < T > Send for MainThreadBound < T > { }
197+
198+ // SAFETY: We only provide access to the inner value via. `get` and `get_mut`.
199+ //
200+ // Both of these take [`MainThreadMarker`], which guarantees that the access
201+ // is done from the main thread.
202+ #[ cfg( feature = "dispatch" ) ]
203+ unsafe impl < T > Sync for MainThreadBound < T > { }
204+
205+ #[ cfg( feature = "dispatch" ) ]
206+ impl < T > Drop for MainThreadBound < T > {
207+ fn drop ( & mut self ) {
208+ if mem:: needs_drop :: < T > ( ) {
209+ // TODO: Figure out whether we should assume the main thread to be
210+ // dead if we're panicking, and just leak instead?
211+ MainThreadMarker :: run_on_main ( |_mtm| {
212+ let this = self ;
213+ // SAFETY: The value is dropped on the main thread, which is
214+ // the same thread that it originated from (guaranteed by
215+ // `new` taking `MainThreadMarker`).
216+ //
217+ // Additionally, the value is never used again after this
218+ // point.
219+ unsafe { ManuallyDrop :: drop ( & mut this. 0 ) } ;
220+ } )
221+ }
222+ }
223+ }
224+
225+ /// Main functionality.
226+ #[ cfg( feature = "dispatch" ) ]
227+ impl < T > MainThreadBound < T > {
228+ /// Create a new [`MainThreadBound`] value of type `T`.
229+ ///
230+ ///
231+ /// # Example
232+ ///
233+ /// ```no_run
234+ /// use icrate::Foundation::{MainThreadMarker, MainThreadBound};
235+ ///
236+ /// let foo;
237+ /// # foo = ();
238+ /// let mtm = MainThreadMarker::new().expect("must be on the main thread");
239+ /// let foo = MainThreadBound::new(foo, mtm);
240+ ///
241+ /// // `foo` is now `Send + Sync`.
242+ /// ```
243+ #[ inline]
244+ pub fn new ( inner : T , _mtm : MainThreadMarker ) -> Self {
245+ Self ( ManuallyDrop :: new ( inner) )
246+ }
247+
248+ /// Returns a reference to the value.
249+ #[ inline]
250+ pub fn get ( & self , _mtm : MainThreadMarker ) -> & T {
251+ & self . 0
252+ }
253+
254+ /// Returns a mutable reference to the value.
255+ #[ inline]
256+ pub fn get_mut ( & mut self , _mtm : MainThreadMarker ) -> & mut T {
257+ & mut self . 0
258+ }
259+
260+ /// Extracts the value from the [`MainThreadBound`] container.
261+ #[ inline]
262+ pub fn into_inner ( self , _mtm : MainThreadMarker ) -> T {
263+ // Prevent our `Drop` impl from running.
264+ //
265+ // This is a bit confusing, now `this` is:
266+ // `ManuallyDrop<Self(ManuallyDrop<T>)>`
267+ let mut this = ManuallyDrop :: new ( self ) ;
268+
269+ // SAFETY: `self` is consumed by this function, and wrapped in
270+ // `ManuallyDrop`, so the item's destructor is never run.
271+ unsafe { ManuallyDrop :: take ( & mut this. 0 ) }
272+ }
273+ }
274+
275+ /// Helper functions for running [`MainThreadMarker::run_on_main`].
276+ #[ cfg( feature = "dispatch" ) ]
277+ impl < T > MainThreadBound < T > {
278+ /// Access the item on the main thread.
279+ ///
280+ /// See [`MainThreadMarker::run_on_main`] for caveats.
281+ #[ inline]
282+ pub fn get_on_main < F , R > ( & self , f : F ) -> R
283+ where
284+ F : Send + FnOnce ( & T , MainThreadMarker ) -> R ,
285+ R : Send ,
286+ {
287+ MainThreadMarker :: run_on_main ( |mtm| f ( self . get ( mtm) , mtm) )
288+ }
289+
290+ /// Access the item mutably on the main thread.
291+ ///
292+ /// See [`MainThreadMarker::run_on_main`] for caveats.
293+ #[ inline]
294+ pub fn get_on_main_mut < F , R > ( & mut self , f : F ) -> R
295+ where
296+ F : Send + FnOnce ( & mut T , MainThreadMarker ) -> R ,
297+ R : Send ,
298+ {
299+ MainThreadMarker :: run_on_main ( |mtm| f ( self . get_mut ( mtm) , mtm) )
300+ }
301+ }
302+
303+ #[ cfg( feature = "dispatch" ) ]
304+ impl < T > fmt:: Debug for MainThreadBound < T > {
305+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
306+ f. debug_struct ( "MainThreadBound" ) . finish_non_exhaustive ( )
307+ }
308+ }
0 commit comments