@@ -38,6 +38,26 @@ impl<G: GenServer> GenServerHandle<G> {
3838 handle_clone
3939 }
4040
41+ pub ( crate ) fn new_blocking ( mut initial_state : G :: State ) -> Self {
42+ let ( tx, mut rx) = mpsc:: channel :: < GenServerInMsg < G > > ( ) ;
43+ let handle = GenServerHandle { tx } ;
44+ let mut gen_server: G = GenServer :: new ( ) ;
45+ let handle_clone = handle. clone ( ) ;
46+ // Ignore the JoinHandle for now. Maybe we'll use it in the future
47+ let _join_handle = rt:: spawn_blocking ( || {
48+ rt:: block_on ( async move {
49+ if gen_server
50+ . run ( & handle, & mut rx, & mut initial_state)
51+ . await
52+ . is_err ( )
53+ {
54+ tracing:: trace!( "GenServer crashed" )
55+ } ;
56+ } )
57+ } ) ;
58+ handle_clone
59+ }
60+
4161 pub fn sender ( & self ) -> mpsc:: Sender < GenServerInMsg < G > > {
4262 self . tx . clone ( )
4363 }
@@ -97,6 +117,15 @@ where
97117 GenServerHandle :: new ( initial_state)
98118 }
99119
120+ /// Tokio tasks depend on a coolaborative multitasking model. "work stealing" can't
121+ /// happen if the task is blocking the thread. As such, for sync compute task
122+ /// or other blocking tasks need to be in their own separate thread, and the OS
123+ /// will manage them through hardware interrupts.
124+ /// Start blocking provides such thread.
125+ fn start_blocking ( initial_state : Self :: State ) -> GenServerHandle < Self > {
126+ GenServerHandle :: new_blocking ( initial_state)
127+ }
128+
100129 fn run (
101130 & mut self ,
102131 handle : & GenServerHandle < Self > ,
@@ -199,3 +228,136 @@ where
199228 state : & mut Self :: State ,
200229 ) -> impl std:: future:: Future < Output = CastResponse > + Send ;
201230}
231+
232+ #[ cfg( test) ]
233+ mod tests {
234+ use super :: * ;
235+ use crate :: tasks:: send_after;
236+ use std:: { process:: exit, thread, time:: Duration } ;
237+ struct BadlyBehavedTask ;
238+
239+ #[ derive( Clone ) ]
240+ pub enum InMessage {
241+ GetCount ,
242+ Stop ,
243+ }
244+ #[ derive( Clone ) ]
245+ pub enum OutMsg {
246+ Count ( u64 ) ,
247+ }
248+
249+ impl GenServer for BadlyBehavedTask {
250+ type CallMsg = InMessage ;
251+ type CastMsg = ( ) ;
252+ type OutMsg = ( ) ;
253+ type State = ( ) ;
254+ type Error = ( ) ;
255+
256+ fn new ( ) -> Self {
257+ Self { }
258+ }
259+
260+ async fn handle_call (
261+ & mut self ,
262+ _: Self :: CallMsg ,
263+ _: & GenServerHandle < Self > ,
264+ _: & mut Self :: State ,
265+ ) -> CallResponse < Self :: OutMsg > {
266+ CallResponse :: Stop ( ( ) )
267+ }
268+
269+ async fn handle_cast (
270+ & mut self ,
271+ _: Self :: CastMsg ,
272+ _: & GenServerHandle < Self > ,
273+ _: & mut Self :: State ,
274+ ) -> CastResponse {
275+ rt:: sleep ( Duration :: from_millis ( 20 ) ) . await ;
276+ thread:: sleep ( Duration :: from_secs ( 2 ) ) ;
277+ CastResponse :: Stop
278+ }
279+ }
280+
281+ struct WellBehavedTask ;
282+
283+ #[ derive( Clone ) ]
284+ struct CountState {
285+ pub count : u64 ,
286+ }
287+
288+ impl GenServer for WellBehavedTask {
289+ type CallMsg = InMessage ;
290+ type CastMsg = ( ) ;
291+ type OutMsg = OutMsg ;
292+ type State = CountState ;
293+ type Error = ( ) ;
294+
295+ fn new ( ) -> Self {
296+ Self { }
297+ }
298+
299+ async fn handle_call (
300+ & mut self ,
301+ message : Self :: CallMsg ,
302+ _: & GenServerHandle < Self > ,
303+ state : & mut Self :: State ,
304+ ) -> CallResponse < Self :: OutMsg > {
305+ match message {
306+ InMessage :: GetCount => CallResponse :: Reply ( OutMsg :: Count ( state. count ) ) ,
307+ InMessage :: Stop => CallResponse :: Stop ( OutMsg :: Count ( state. count ) ) ,
308+ }
309+ }
310+
311+ async fn handle_cast (
312+ & mut self ,
313+ _: Self :: CastMsg ,
314+ handle : & GenServerHandle < Self > ,
315+ state : & mut Self :: State ,
316+ ) -> CastResponse {
317+ state. count += 1 ;
318+ println ! ( "{:?}: good still alive" , thread:: current( ) . id( ) ) ;
319+ send_after ( Duration :: from_millis ( 100 ) , handle. to_owned ( ) , ( ) ) ;
320+ CastResponse :: NoReply
321+ }
322+ }
323+
324+ #[ test]
325+ pub fn badly_behaved_thread_non_blocking ( ) {
326+ let runtime = rt:: Runtime :: new ( ) . unwrap ( ) ;
327+ runtime. block_on ( async move {
328+ let mut badboy = BadlyBehavedTask :: start ( ( ) ) ;
329+ let _ = badboy. cast ( ( ) ) . await ;
330+ let mut goodboy = WellBehavedTask :: start ( CountState { count : 0 } ) ;
331+ let _ = goodboy. cast ( ( ) ) . await ;
332+ rt:: sleep ( Duration :: from_secs ( 1 ) ) . await ;
333+ let count = goodboy. call ( InMessage :: GetCount ) . await . unwrap ( ) ;
334+
335+ match count {
336+ OutMsg :: Count ( num) => {
337+ assert_ne ! ( num, 10 ) ;
338+ }
339+ }
340+ goodboy. call ( InMessage :: Stop ) . await . unwrap ( ) ;
341+ } ) ;
342+ }
343+
344+ #[ test]
345+ pub fn badly_behaved_thread ( ) {
346+ let runtime = rt:: Runtime :: new ( ) . unwrap ( ) ;
347+ runtime. block_on ( async move {
348+ let mut badboy = BadlyBehavedTask :: start_blocking ( ( ) ) ;
349+ let _ = badboy. cast ( ( ) ) . await ;
350+ let mut goodboy = WellBehavedTask :: start ( CountState { count : 0 } ) ;
351+ let _ = goodboy. cast ( ( ) ) . await ;
352+ rt:: sleep ( Duration :: from_secs ( 1 ) ) . await ;
353+ let count = goodboy. call ( InMessage :: GetCount ) . await . unwrap ( ) ;
354+
355+ match count {
356+ OutMsg :: Count ( num) => {
357+ assert_eq ! ( num, 10 ) ;
358+ }
359+ }
360+ goodboy. call ( InMessage :: Stop ) . await . unwrap ( ) ;
361+ } ) ;
362+ }
363+ }
0 commit comments