@@ -9,26 +9,37 @@ pub struct Thread(task_queue::JoinHandle);
99
1010pub const DEFAULT_MIN_STACK_SIZE : usize = 4096 ;
1111
12+ pub use self :: task_queue:: JoinNotifier ;
13+
1214mod task_queue {
13- use crate :: sync :: mpsc ;
15+ use super :: wait_notify ;
1416 use crate :: sync:: { Mutex , MutexGuard , Once } ;
1517
16- pub type JoinHandle = mpsc:: Receiver < ( ) > ;
18+ pub type JoinHandle = wait_notify:: Waiter ;
19+
20+ pub struct JoinNotifier ( Option < wait_notify:: Notifier > ) ;
21+
22+ impl Drop for JoinNotifier {
23+ fn drop ( & mut self ) {
24+ self . 0 . take ( ) . unwrap ( ) . notify ( ) ;
25+ }
26+ }
1727
1828 pub ( super ) struct Task {
1929 p : Box < dyn FnOnce ( ) > ,
20- done : mpsc :: Sender < ( ) > ,
30+ done : JoinNotifier ,
2131 }
2232
2333 impl Task {
2434 pub ( super ) fn new ( p : Box < dyn FnOnce ( ) > ) -> ( Task , JoinHandle ) {
25- let ( done, recv) = mpsc:: channel ( ) ;
35+ let ( done, recv) = wait_notify:: new ( ) ;
36+ let done = JoinNotifier ( Some ( done) ) ;
2637 ( Task { p, done } , recv)
2738 }
2839
29- pub ( super ) fn run ( self ) {
40+ pub ( super ) fn run ( self ) -> JoinNotifier {
3041 ( self . p ) ( ) ;
31- let _ = self . done . send ( ( ) ) ;
42+ self . done
3243 }
3344 }
3445
@@ -47,6 +58,48 @@ mod task_queue {
4758 }
4859}
4960
61+ /// This module provides a synchronization primitive that does not use thread
62+ /// local variables. This is needed for signaling that a thread has finished
63+ /// execution. The signal is sent once all TLS destructors have finished at
64+ /// which point no new thread locals should be created.
65+ pub mod wait_notify {
66+ use super :: super :: waitqueue:: { SpinMutex , WaitQueue , WaitVariable } ;
67+ use crate :: sync:: Arc ;
68+
69+ pub struct Notifier ( Arc < SpinMutex < WaitVariable < bool > > > ) ;
70+
71+ impl Notifier {
72+ /// Notify the waiter. The waiter is either notified right away (if
73+ /// currently blocked in `Waiter::wait()`) or later when it calls the
74+ /// `Waiter::wait()` method.
75+ pub fn notify ( self ) {
76+ let mut guard = self . 0 . lock ( ) ;
77+ * guard. lock_var_mut ( ) = true ;
78+ let _ = WaitQueue :: notify_one ( guard) ;
79+ }
80+ }
81+
82+ pub struct Waiter ( Arc < SpinMutex < WaitVariable < bool > > > ) ;
83+
84+ impl Waiter {
85+ /// Wait for a notification. If `Notifier::notify()` has already been
86+ /// called, this will return immediately, otherwise the current thread
87+ /// is blocked until notified.
88+ pub fn wait ( self ) {
89+ let guard = self . 0 . lock ( ) ;
90+ if * guard. lock_var ( ) {
91+ return ;
92+ }
93+ WaitQueue :: wait ( guard, || { } ) ;
94+ }
95+ }
96+
97+ pub fn new ( ) -> ( Notifier , Waiter ) {
98+ let inner = Arc :: new ( SpinMutex :: new ( WaitVariable :: new ( false ) ) ) ;
99+ ( Notifier ( inner. clone ( ) ) , Waiter ( inner) )
100+ }
101+ }
102+
50103impl Thread {
51104 // unsafe: see thread::Builder::spawn_unchecked for safety requirements
52105 pub unsafe fn new ( _stack : usize , p : Box < dyn FnOnce ( ) > ) -> io:: Result < Thread > {
@@ -57,7 +110,7 @@ impl Thread {
57110 Ok ( Thread ( handle) )
58111 }
59112
60- pub ( super ) fn entry ( ) {
113+ pub ( super ) fn entry ( ) -> JoinNotifier {
61114 let mut pending_tasks = task_queue:: lock ( ) ;
62115 let task = rtunwrap ! ( Some , pending_tasks. pop( ) ) ;
63116 drop ( pending_tasks) ; // make sure to not hold the task queue lock longer than necessary
@@ -78,7 +131,7 @@ impl Thread {
78131 }
79132
80133 pub fn join ( self ) {
81- let _ = self . 0 . recv ( ) ;
134+ self . 0 . wait ( ) ;
82135 }
83136}
84137
0 commit comments