@@ -27,6 +27,9 @@ static INTERRUPT_TASKS: LazyLock<TaskChannels> = LazyLock::new(|| TaskChannels::
27
27
/// Task channels for idle-time tasks
28
28
static IDLE_TASKS : LazyLock < TaskChannels > = LazyLock :: new ( || TaskChannels :: new ( ) ) ;
29
29
30
+ /// Task channels for lock-time tasks
31
+ static LOCK_TASKS : LazyLock < TaskChannels > = LazyLock :: new ( || TaskChannels :: new ( ) ) ;
32
+
30
33
// Compared to `futures::BoxFuture`, this doesn't require the future to be Send.
31
34
// We don't need this bound since the executor runs on only on the R thread
32
35
pub ( crate ) type BoxFuture < ' a , T > = Pin < Box < dyn Future < Output = T > + ' a > > ;
@@ -58,11 +61,15 @@ impl TaskChannels {
58
61
}
59
62
}
60
63
61
- /// Returns receivers for both interrupt and idle tasks.
64
+ /// Returns receivers for interrupt, idle, and lock tasks.
62
65
/// Initializes the task channels if they haven't been initialized yet.
63
66
/// Can only be called once (intended for `RMain` during init).
64
- pub ( crate ) fn take_receivers ( ) -> ( Receiver < RTask > , Receiver < RTask > ) {
65
- ( INTERRUPT_TASKS . take_rx ( ) , IDLE_TASKS . take_rx ( ) )
67
+ pub ( crate ) fn take_receivers ( ) -> ( Receiver < RTask > , Receiver < RTask > , Receiver < RTask > ) {
68
+ (
69
+ INTERRUPT_TASKS . take_rx ( ) ,
70
+ IDLE_TASKS . take_rx ( ) ,
71
+ LOCK_TASKS . take_rx ( ) ,
72
+ )
66
73
}
67
74
68
75
pub enum RTask {
@@ -274,6 +281,87 @@ where
274
281
return result. lock ( ) . unwrap ( ) . take ( ) . unwrap ( ) ;
275
282
}
276
283
284
+ pub fn spawn_try_lock < ' env , F , T > ( f : F ) -> Option < T >
285
+ where
286
+ F : FnOnce ( ) -> T ,
287
+ F : ' env + Send ,
288
+ T : ' env + Send ,
289
+ {
290
+ if stdext:: IS_TESTING && !RMain :: is_initialized ( ) {
291
+ let _lock = harp:: fixtures:: R_TEST_LOCK . lock ( ) ;
292
+ r_test_init ( ) ;
293
+ return Some ( f ( ) ) ;
294
+ }
295
+
296
+ if RMain :: on_main_thread ( ) {
297
+ panic ! ( "Can't call `spawn_try_lock()` on main thread" ) ;
298
+ }
299
+
300
+ let result = SharedOption :: default ( ) ;
301
+
302
+ {
303
+ let result = Arc :: clone ( & result) ;
304
+ let closure = move || {
305
+ * result. lock ( ) . unwrap ( ) = Some ( f ( ) ) ;
306
+ } ;
307
+
308
+ let closure: Box < dyn FnOnce ( ) + Send + ' env > = Box :: new ( closure) ;
309
+ let closure: Box < dyn FnOnce ( ) + Send + ' static > = unsafe { std:: mem:: transmute ( closure) } ;
310
+
311
+ // Channel to communicate status of the task/closure
312
+ let ( status_tx, status_rx) = bounded :: < RTaskStatus > ( 0 ) ;
313
+
314
+ // Send the task to the R thread
315
+ let task = RTask :: Sync ( RTaskSync {
316
+ fun : closure,
317
+ status_tx : Some ( status_tx) ,
318
+ start_info : RTaskStartInfo :: new ( false ) ,
319
+ } ) ;
320
+ if let Err ( _) = LOCK_TASKS . tx ( ) . try_send ( task) {
321
+ return None ;
322
+ } ;
323
+
324
+ // Block until we get the signal that the task has started
325
+ let status = status_rx. recv ( ) . unwrap ( ) ;
326
+
327
+ let RTaskStatus :: Started = status else {
328
+ let trace = std:: backtrace:: Backtrace :: force_capture ( ) ;
329
+ panic ! (
330
+ "Task `status` value must be `Started`: {status:?}\n \
331
+ Backtrace of calling thread:\n \n
332
+ {trace}"
333
+ ) ;
334
+ } ;
335
+
336
+ // Block until task was completed
337
+ let status = status_rx. recv ( ) . unwrap ( ) ;
338
+
339
+ let RTaskStatus :: Finished ( status) = status else {
340
+ let trace = std:: backtrace:: Backtrace :: force_capture ( ) ;
341
+ panic ! (
342
+ "Task `status` value must be `Finished`: {status:?}\n \
343
+ Backtrace of calling thread:\n \n
344
+ {trace}"
345
+ ) ;
346
+ } ;
347
+
348
+ // If the task failed send a backtrace of the current thread to the
349
+ // main thread
350
+ if let Err ( err) = status {
351
+ let trace = std:: backtrace:: Backtrace :: force_capture ( ) ;
352
+ panic ! (
353
+ "While running task: {err:?}\n \
354
+ Backtrace of calling thread:\n \n \
355
+ {trace}"
356
+ ) ;
357
+ }
358
+ }
359
+
360
+ // Retrieve closure result from the synchronized shared option.
361
+ // If we get here without panicking we know the result was assigned.
362
+ return Some ( result. lock ( ) . unwrap ( ) . take ( ) . unwrap ( ) ) ;
363
+ }
364
+
277
365
pub ( crate ) fn spawn_idle < F , Fut > ( fun : F )
278
366
where
279
367
F : FnOnce ( ) -> Fut + ' static + Send ,
0 commit comments