@@ -11,14 +11,14 @@ use std::{
11
11
thread:: { available_parallelism, JoinHandle } ,
12
12
} ;
13
13
14
- use compio_driver:: { AsyncifyPool , ProactorBuilder } ;
14
+ use compio_driver:: { AsyncifyPool , DispatchError , Dispatchable , ProactorBuilder } ;
15
15
use compio_runtime:: { event:: Event , JoinHandle as CompioJoinHandle , Runtime } ;
16
- use flume:: { unbounded, SendError , Sender } ;
16
+ use flume:: { unbounded, Sender } ;
17
17
use futures_channel:: oneshot;
18
18
19
- type Dispatching = Box < dyn Dispatchable + Send > ;
19
+ type Spawning = Box < dyn Spawnable + Send > ;
20
20
21
- trait Dispatchable {
21
+ trait Spawnable {
22
22
fn spawn ( self : Box < Self > , handle : & Runtime ) -> CompioJoinHandle < ( ) > ;
23
23
}
24
24
@@ -28,7 +28,14 @@ struct Concrete<F, R> {
28
28
func : F ,
29
29
}
30
30
31
- impl < F , Fut , R > Dispatchable for Concrete < F , R >
31
+ impl < F , R > Concrete < F , R > {
32
+ pub fn new ( func : F ) -> ( Self , oneshot:: Receiver < R > ) {
33
+ let ( tx, rx) = oneshot:: channel ( ) ;
34
+ ( Self { callback : tx, func } , rx)
35
+ }
36
+ }
37
+
38
+ impl < F , Fut , R > Spawnable for Concrete < F , R >
32
39
where
33
40
F : FnOnce ( ) -> Fut + Send + ' static ,
34
41
Fut : Future < Output = R > ,
@@ -43,10 +50,22 @@ where
43
50
}
44
51
}
45
52
46
- #[ derive( Debug ) ]
53
+ impl < F , R > Dispatchable for Concrete < F , R >
54
+ where
55
+ F : FnOnce ( ) -> R + Send + ' static ,
56
+ R : Send + ' static ,
57
+ {
58
+ fn run ( self : Box < Self > ) {
59
+ let Concrete { callback, func } = * self ;
60
+ let res = func ( ) ;
61
+ callback. send ( res) . ok ( ) ;
62
+ }
63
+ }
64
+
47
65
/// The dispatcher. It manages the threads and dispatches the tasks.
66
+ #[ derive( Debug ) ]
48
67
pub struct Dispatcher {
49
- sender : Sender < Dispatching > ,
68
+ sender : Sender < Spawning > ,
50
69
threads : Vec < JoinHandle < ( ) > > ,
51
70
pool : AsyncifyPool ,
52
71
}
@@ -57,7 +76,7 @@ impl Dispatcher {
57
76
let mut proactor_builder = builder. proactor_builder ;
58
77
proactor_builder. force_reuse_thread_pool ( ) ;
59
78
let pool = proactor_builder. create_or_get_thread_pool ( ) ;
60
- let ( sender, receiver) = unbounded :: < Dispatching > ( ) ;
79
+ let ( sender, receiver) = unbounded :: < Spawning > ( ) ;
61
80
62
81
let threads = ( 0 ..builder. nthreads )
63
82
. map ( {
@@ -123,28 +142,51 @@ impl Dispatcher {
123
142
///
124
143
/// If all threads have panicked, this method will return an error with the
125
144
/// sent closure.
126
- pub fn dispatch < Fut , Fn , R > ( & self , f : Fn ) -> Result < oneshot:: Receiver < R > , SendError < Fn > >
145
+ pub fn dispatch < Fn , Fut , R > ( & self , f : Fn ) -> Result < oneshot:: Receiver < R > , DispatchError < Fn > >
127
146
where
128
147
Fn : ( FnOnce ( ) -> Fut ) + Send + ' static ,
129
148
Fut : Future < Output = R > + ' static ,
130
149
R : Send + ' static ,
131
150
{
132
- let ( tx, rx) = oneshot:: channel ( ) ;
133
- let concrete: Concrete < Fn , R > = Concrete {
134
- callback : tx,
135
- func : f,
136
- } ;
151
+ let ( concrete, rx) = Concrete :: new ( f) ;
152
+
137
153
match self . sender . send ( Box :: new ( concrete) ) {
138
154
Ok ( _) => Ok ( rx) ,
139
155
Err ( err) => {
140
156
// SAFETY: We know the dispatchable we sent has type `Concrete<Fn, R>`
141
157
let recovered =
142
158
unsafe { Box :: from_raw ( Box :: into_raw ( err. 0 ) as * mut Concrete < Fn , R > ) } ;
143
- Err ( SendError ( recovered. func ) )
159
+ Err ( DispatchError ( recovered. func ) )
144
160
}
145
161
}
146
162
}
147
163
164
+ /// Dispatch a blocking task to the threads.
165
+ ///
166
+ /// Blocking pool of the dispatcher will be obtained from the proactor
167
+ /// builder. So any configuration of the proactor's blocking pool will be
168
+ /// applied to the dispatcher.
169
+ ///
170
+ /// # Error
171
+ ///
172
+ /// If all threads are busy and the thread pool is full, this method will
173
+ /// return an error with the original closure. The limit can be configured
174
+ /// with [`DispatcherBuilder::proactor_builder`] and
175
+ /// [`ProactorBuilder::thread_pool_limit`].
176
+ pub fn dispatch_blocking < Fn , R > ( & self , f : Fn ) -> Result < oneshot:: Receiver < R > , DispatchError < Fn > >
177
+ where
178
+ Fn : FnOnce ( ) -> R + Send + ' static ,
179
+ R : Send + ' static ,
180
+ {
181
+ let ( concrete, rx) = Concrete :: new ( f) ;
182
+
183
+ self . pool
184
+ . dispatch ( concrete)
185
+ . map_err ( |e| DispatchError ( e. 0 . func ) ) ?;
186
+
187
+ Ok ( rx)
188
+ }
189
+
148
190
/// Stop the dispatcher and wait for the threads to complete. If there is a
149
191
/// thread panicked, this method will resume the panic.
150
192
pub async fn join ( self ) -> io:: Result < ( ) > {
@@ -163,7 +205,7 @@ impl Dispatcher {
163
205
handle. notify ( ) ;
164
206
}
165
207
} ) {
166
- std:: thread:: spawn ( f) ;
208
+ std:: thread:: spawn ( f. 0 ) ;
167
209
}
168
210
event. wait ( ) . await ;
169
211
let mut guard = results. lock ( ) . unwrap ( ) ;
0 commit comments