3
3
#![ warn( missing_docs) ]
4
4
5
5
use std:: {
6
- any:: Any ,
7
6
future:: Future ,
8
7
io,
9
8
num:: NonZeroUsize ,
10
9
panic:: resume_unwind,
11
- pin:: Pin ,
12
10
sync:: { Arc , Mutex } ,
13
11
thread:: { available_parallelism, JoinHandle } ,
14
12
} ;
15
13
16
14
use compio_driver:: { AsyncifyPool , ProactorBuilder } ;
17
- use compio_runtime:: { event:: Event , Runtime } ;
15
+ use compio_runtime:: { event:: Event , JoinHandle as CompioJoinHandle , Runtime } ;
18
16
use flume:: { unbounded, SendError , Sender } ;
17
+ use futures_channel:: oneshot;
18
+
19
+ type Dispatching = Box < dyn Dispatchable + Send > ;
20
+
21
+ trait Dispatchable {
22
+ fn spawn ( self : Box < Self > , handle : & Runtime ) -> CompioJoinHandle < ( ) > ;
23
+ }
24
+
25
+ /// Concrete type for the closure we're sending to worker threads
26
+ struct Concrete < F , R > {
27
+ callback : oneshot:: Sender < R > ,
28
+ func : F ,
29
+ }
30
+
31
+ impl < F , Fut , R > Dispatchable for Concrete < F , R >
32
+ where
33
+ F : FnOnce ( ) -> Fut + Send + ' static ,
34
+ Fut : Future < Output = R > ,
35
+ R : Send + ' static ,
36
+ {
37
+ fn spawn ( self : Box < Self > , handle : & Runtime ) -> CompioJoinHandle < ( ) > {
38
+ let Concrete { callback, func } = * self ;
39
+ handle. spawn ( async move {
40
+ let res = func ( ) . await ;
41
+ callback. send ( res) . ok ( ) ;
42
+ } )
43
+ }
44
+ }
19
45
20
46
/// The dispatcher. It manages the threads and dispatches the tasks.
21
47
pub struct Dispatcher {
22
- sender : Sender < Box < Closure > > ,
48
+ sender : Sender < Dispatching > ,
23
49
threads : Vec < JoinHandle < ( ) > > ,
24
50
pool : AsyncifyPool ,
25
51
}
@@ -30,7 +56,7 @@ impl Dispatcher {
30
56
let mut proactor_builder = builder. proactor_builder ;
31
57
proactor_builder. force_reuse_thread_pool ( ) ;
32
58
let pool = proactor_builder. create_or_get_thread_pool ( ) ;
33
- let ( sender, receiver) = unbounded :: < Box < Closure > > ( ) ;
59
+ let ( sender, receiver) = unbounded :: < Dispatching > ( ) ;
34
60
35
61
let threads = ( 0 ..builder. nthreads )
36
62
. map ( {
@@ -57,14 +83,14 @@ impl Dispatcher {
57
83
. expect ( "cannot create compio runtime" )
58
84
. block_on ( async move {
59
85
while let Ok ( f) = receiver. recv_async ( ) . await {
60
- let fut = ( f ) ( ) ;
86
+ let task = Runtime :: with_current ( |rt| f . spawn ( rt ) ) ;
61
87
if builder. concurrent {
62
- compio_runtime :: spawn ( fut ) . detach ( )
88
+ task . detach ( )
63
89
} else {
64
- fut . await
90
+ task . await . ok ( ) ;
65
91
}
66
92
}
67
- } )
93
+ } ) ;
68
94
} )
69
95
}
70
96
} )
@@ -86,75 +112,36 @@ impl Dispatcher {
86
112
DispatcherBuilder :: default ( )
87
113
}
88
114
89
- fn prepare < Fut , Fn , R > ( & self , f : Fn ) -> ( Executing < R > , Box < Closure > )
90
- where
91
- Fn : ( FnOnce ( ) -> Fut ) + Send + ' static ,
92
- Fut : Future < Output = R > + ' static ,
93
- R : Any + Send + ' static ,
94
- {
95
- let event = Event :: new ( ) ;
96
- let handle = event. handle ( ) ;
97
- let res = Arc :: new ( Mutex :: new ( None ) ) ;
98
- let dispatched = Executing {
99
- event,
100
- result : res. clone ( ) ,
101
- } ;
102
- let closure = Box :: new ( || {
103
- Box :: pin ( async move {
104
- * res. lock ( ) . unwrap ( ) = Some ( f ( ) . await ) ;
105
- handle. notify ( ) ;
106
- } ) as BoxFuture < ( ) >
107
- } ) ;
108
- ( dispatched, closure)
109
- }
110
-
111
- /// Spawn a boxed closure to the threads.
112
- ///
113
- /// If all threads have panicked, this method will return an error with the
114
- /// sent closure.
115
- pub fn spawn ( & self , closure : Box < Closure > ) -> Result < ( ) , SendError < Box < Closure > > > {
116
- self . sender . send ( closure)
117
- }
118
-
119
115
/// Dispatch a task to the threads
120
116
///
121
117
/// The provided `f` should be [`Send`] because it will be send to another
122
- /// thread before calling. The return [`Future`] need not to be [`Send`]
118
+ /// thread before calling. The returned [`Future`] need not to be [`Send`]
123
119
/// because it will be executed on only one thread.
124
120
///
125
121
/// # Error
126
122
///
127
123
/// If all threads have panicked, this method will return an error with the
128
- /// sent closure. Notice that the returned closure is not the same as the
129
- /// argument and cannot be simply transmuted back to `Fn`.
130
- pub fn dispatch < Fut , Fn > ( & self , f : Fn ) -> Result < ( ) , SendError < Box < Closure > > >
131
- where
132
- Fn : ( FnOnce ( ) -> Fut ) + Send + ' static ,
133
- Fut : Future < Output = ( ) > + ' static ,
134
- {
135
- self . spawn ( Box :: new ( || Box :: pin ( f ( ) ) as BoxFuture < ( ) > ) )
136
- }
137
-
138
- /// Execute a task on the threads and retrieve its returned value.
139
- ///
140
- /// The provided `f` should be [`Send`] because it will be send to another
141
- /// thread before calling. The return [`Future`] need not to be [`Send`]
142
- /// because it will be executed on only one thread.
143
- ///
144
- /// # Error
145
- ///
146
- /// If all threads have panicked, this method will return an error with the
147
- /// sent closure. Notice that the returned closure is not the same as the
148
- /// argument and cannot be simply transmuted back to `Fn`.
149
- pub fn execute < Fut , Fn , R > ( & self , f : Fn ) -> Result < Executing < R > , SendError < Box < Closure > > >
124
+ /// sent closure.
125
+ pub fn dispatch < Fut , Fn , R > ( & self , f : Fn ) -> Result < oneshot:: Receiver < R > , SendError < Fn > >
150
126
where
151
127
Fn : ( FnOnce ( ) -> Fut ) + Send + ' static ,
152
128
Fut : Future < Output = R > + ' static ,
153
- R : Any + Send + ' static ,
129
+ R : Send + ' static ,
154
130
{
155
- let ( dispatched, closure) = self . prepare ( f) ;
156
- self . spawn ( closure) ?;
157
- Ok ( dispatched)
131
+ let ( tx, rx) = oneshot:: channel ( ) ;
132
+ let concrete: Concrete < Fn , R > = Concrete {
133
+ callback : tx,
134
+ func : f,
135
+ } ;
136
+ match self . sender . send ( Box :: new ( concrete) ) {
137
+ Ok ( _) => Ok ( rx) ,
138
+ Err ( err) => {
139
+ // SAFETY: We know the dispatchable we sent has type `Concrete<Fn, R>`
140
+ let recovered =
141
+ unsafe { Box :: from_raw ( Box :: into_raw ( err. 0 ) as * mut Concrete < Fn , R > ) } ;
142
+ Err ( SendError ( recovered. func ) )
143
+ }
144
+ }
158
145
}
159
146
160
147
/// Stop the dispatcher and wait for the threads to complete. If there is a
@@ -253,37 +240,3 @@ impl Default for DispatcherBuilder {
253
240
Self :: new ( )
254
241
}
255
242
}
256
-
257
- type BoxFuture < T > = Pin < Box < dyn Future < Output = T > > > ;
258
- type Closure = dyn ( FnOnce ( ) -> BoxFuture < ( ) > ) + Send ;
259
-
260
- /// The join handle for an executing task. It can be used to wait for the
261
- /// task's returned value.
262
- pub struct Executing < R > {
263
- event : Event ,
264
- result : Arc < Mutex < Option < R > > > ,
265
- }
266
-
267
- impl < R : ' static > Executing < R > {
268
- fn take ( val : & Mutex < Option < R > > ) -> R {
269
- val. lock ( )
270
- . unwrap ( )
271
- . take ( )
272
- . expect ( "the result should be set" )
273
- }
274
-
275
- /// Try to wait for the task to complete without blocking.
276
- pub fn try_join ( self ) -> Result < R , Self > {
277
- if self . event . notified ( ) {
278
- Ok ( Self :: take ( & self . result ) )
279
- } else {
280
- Err ( self )
281
- }
282
- }
283
-
284
- /// Wait for the task to complete.
285
- pub async fn join ( self ) -> R {
286
- self . event . wait ( ) . await ;
287
- Self :: take ( & self . result )
288
- }
289
- }
0 commit comments