@@ -161,6 +161,81 @@ mod tests {
161161 . unwrap ( )
162162 }
163163
164+ fn async_begin < P : FnOnce ( Result < ( ) , glib:: Error > ) + Send + ' static > (
165+ duration : Duration ,
166+ must_be_cancelled_on_begin : bool ,
167+ must_be_cancelled_after_sleep : bool ,
168+ cancellable : & Cancellable ,
169+ callback : P ,
170+ ) {
171+ // We do not use std::thread here since we want to simulate what C code normally does.
172+ // Also not using spawn_blocking() directly, since we want to have the full control
173+ // for the test case.
174+ let callback = Box :: new ( callback) ;
175+ let task = unsafe {
176+ crate :: Task :: < bool > :: new ( None :: < & glib:: Binding > , Some ( cancellable) , move |t, _| {
177+ let cancellable = t. cancellable ( ) . unwrap ( ) ;
178+ let ret = t. propagate ( ) ;
179+ println ! (
180+ "Task callback, returning {:?} - cancelled {}" ,
181+ ret,
182+ cancellable. is_cancelled( )
183+ ) ;
184+ assert_eq ! ( cancellable. is_cancelled( ) , must_be_cancelled_after_sleep) ;
185+ match ret {
186+ Err ( e) => callback ( Err ( e) ) ,
187+ Ok ( _) => callback ( Ok ( ( ) ) ) ,
188+ } ;
189+ } )
190+ } ;
191+
192+ task. run_in_thread ( move |task, _: Option < & glib:: Binding > , cancellable| {
193+ let cancellable = cancellable. unwrap ( ) ;
194+ let func = || {
195+ println ! (
196+ "Task thread started, cancelled {} - want {}" ,
197+ cancellable. is_cancelled( ) ,
198+ must_be_cancelled_on_begin
199+ ) ;
200+ assert_eq ! ( cancellable. is_cancelled( ) , must_be_cancelled_on_begin) ;
201+ std:: thread:: sleep ( duration) ;
202+ assert_eq ! ( cancellable. is_cancelled( ) , must_be_cancelled_after_sleep) ;
203+ println ! (
204+ "Task thread done, cancelled {} - want {}" ,
205+ cancellable. is_cancelled( ) ,
206+ must_be_cancelled_after_sleep
207+ )
208+ } ;
209+
210+ if let Err ( e) = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( func) ) {
211+ std:: panic:: resume_unwind ( e) ;
212+ }
213+
214+ unsafe {
215+ task. return_result ( match cancellable. set_error_if_cancelled ( ) {
216+ Err ( e) => Err ( e) ,
217+ Ok ( _) => Ok ( true ) ,
218+ } ) ;
219+ }
220+ } ) ;
221+ }
222+
223+ fn async_future (
224+ duration : Duration ,
225+ must_be_cancelled_on_begin : bool ,
226+ must_be_cancelled_after_sleep : bool ,
227+ ) -> std:: pin:: Pin < Box < dyn Future < Output = Result < ( ) , glib:: Error > > + ' static > > {
228+ Box :: pin ( crate :: GioFuture :: new ( & ( ) , move |_, cancellable, send| {
229+ async_begin (
230+ duration,
231+ must_be_cancelled_on_begin,
232+ must_be_cancelled_after_sleep,
233+ cancellable,
234+ move |res| send. resolve ( res) ,
235+ ) ;
236+ } ) )
237+ }
238+
164239 #[ test]
165240 fn cancellable_future_ok ( ) {
166241 let ctx = glib:: MainContext :: new ( ) ;
@@ -234,4 +309,108 @@ mod tests {
234309 assert ! ( matches!( r1, Err ( Cancelled ) ) ) ;
235310 assert ! ( matches!( r2, ( ) ) ) ;
236311 }
312+
313+ #[ test]
314+ fn cancellable_future_immediate_cancel_with_gio_future ( ) {
315+ let ctx = glib:: MainContext :: new ( ) ;
316+ let c = Cancellable :: new ( ) ;
317+
318+ async fn async_chain ( ) -> Result < ( ) , glib:: Error > {
319+ async_future ( Duration :: from_millis ( 250 ) , true , true ) . await ?;
320+ async_future ( Duration :: from_secs ( 9999999 ) , true , true ) . await
321+ }
322+
323+ c. cancel ( ) ;
324+
325+ let result = ctx
326+ . block_on ( ctx. spawn_local ( {
327+ CancellableFuture :: new (
328+ futures_util:: future:: join5 (
329+ async_chain ( ) ,
330+ async_chain ( ) ,
331+ async_chain ( ) ,
332+ async_chain ( ) ,
333+ CancellableFuture :: new ( async_chain ( ) , Cancellable :: new ( ) ) ,
334+ ) ,
335+ c. clone ( ) ,
336+ )
337+ } ) )
338+ . expect ( "futures must be executed" ) ;
339+
340+ assert ! ( matches!( result, Err ( Cancelled ) ) ) ;
341+ }
342+
343+ #[ test]
344+ fn cancellable_future_delayed_cancel_with_gio_future ( ) {
345+ let ctx = glib:: MainContext :: new ( ) ;
346+ let c = Cancellable :: new ( ) ;
347+
348+ async fn async_chain ( ) -> Result < ( ) , glib:: Error > {
349+ async_future ( Duration :: from_millis ( 250 ) , false , true ) . await ?;
350+ async_future ( Duration :: from_secs ( 9999999 ) , true , true ) . await
351+ }
352+
353+ let ( result, _, _) = ctx
354+ . block_on ( ctx. spawn_local ( {
355+ futures_util:: future:: join3 (
356+ CancellableFuture :: new (
357+ futures_util:: future:: join5 (
358+ async_chain ( ) ,
359+ async_chain ( ) ,
360+ async_chain ( ) ,
361+ async_chain ( ) ,
362+ CancellableFuture :: new ( async_chain ( ) , Cancellable :: new ( ) ) ,
363+ ) ,
364+ c. clone ( ) ,
365+ ) ,
366+ cancel_after_sleep_in_thread ( Duration :: from_millis ( 100 ) , c. clone ( ) ) ,
367+ // Let's wait a bit more to ensure that more events are processed
368+ // by the loop. Not required, but it simulates a more real
369+ // scenario.
370+ glib:: timeout_future ( Duration :: from_millis ( 350 ) ) ,
371+ )
372+ } ) )
373+ . expect ( "futures must be executed" ) ;
374+
375+ assert ! ( matches!( result, Err ( Cancelled ) ) ) ;
376+ }
377+
378+ #[ test]
379+ fn cancellable_future_late_cancel_with_gio_future ( ) {
380+ let ctx = glib:: MainContext :: new ( ) ;
381+ let c = Cancellable :: new ( ) ;
382+
383+ async fn async_chain ( ) -> Result < ( ) , glib:: Error > {
384+ async_future ( Duration :: from_millis ( 100 ) , false , false ) . await ?;
385+ async_future ( Duration :: from_millis ( 100 ) , false , false ) . await
386+ }
387+
388+ let results = ctx
389+ . block_on ( ctx. spawn_local ( async move {
390+ let ret = CancellableFuture :: new (
391+ futures_util:: future:: join5 (
392+ async_chain ( ) ,
393+ async_chain ( ) ,
394+ async_chain ( ) ,
395+ async_chain ( ) ,
396+ CancellableFuture :: new ( async_chain ( ) , Cancellable :: new ( ) ) ,
397+ ) ,
398+ c. clone ( ) ,
399+ )
400+ . await ;
401+
402+ c. cancel ( ) ;
403+ ret
404+ } ) )
405+ . expect ( "futures must be executed" ) ;
406+
407+ assert ! ( results. is_ok( ) ) ;
408+
409+ let r1 = results. unwrap ( ) ;
410+ assert ! ( matches!( r1. 0 , Ok ( ( ) ) ) ) ;
411+ assert ! ( matches!( r1. 1 , Ok ( ( ) ) ) ) ;
412+ assert ! ( matches!( r1. 2 , Ok ( ( ) ) ) ) ;
413+ assert ! ( matches!( r1. 3 , Ok ( ( ) ) ) ) ;
414+ assert ! ( matches!( r1. 4 . unwrap( ) , Ok ( ( ) ) ) ) ;
415+ }
237416}
0 commit comments