@@ -52,7 +52,6 @@ use amalthea::Error;
52
52
use anyhow:: * ;
53
53
use bus:: Bus ;
54
54
use crossbeam:: channel:: bounded;
55
- use crossbeam:: channel:: unbounded;
56
55
use crossbeam:: channel:: Receiver ;
57
56
use crossbeam:: channel:: Sender ;
58
57
use harp:: command:: r_command;
@@ -89,6 +88,7 @@ use regex::Regex;
89
88
use serde_json:: json;
90
89
use stdext:: result:: ResultOrLog ;
91
90
use stdext:: * ;
91
+ use tokio:: sync:: mpsc:: UnboundedReceiver as AsyncUnboundedReceiver ;
92
92
use uuid:: Uuid ;
93
93
94
94
use crate :: dap:: dap:: DapBackendEvent ;
@@ -107,6 +107,7 @@ use crate::lsp::state_handlers::ConsoleInputs;
107
107
use crate :: modules;
108
108
use crate :: modules:: ARK_ENVS ;
109
109
use crate :: plots:: graphics_device;
110
+ use crate :: plots:: graphics_device:: GraphicsDeviceNotification ;
110
111
use crate :: r_task;
111
112
use crate :: r_task:: BoxFuture ;
112
113
use crate :: r_task:: RTask ;
@@ -321,7 +322,7 @@ impl RMain {
321
322
/// Sets up the main R thread, initializes the `R_MAIN` singleton,
322
323
/// and starts R. Does not return!
323
324
/// SAFETY: Must be called only once. Enforced with a panic.
324
- pub fn start (
325
+ pub ( crate ) fn start (
325
326
r_args : Vec < String > ,
326
327
startup_file : Option < String > ,
327
328
comm_manager_tx : Sender < CommManagerEvent > ,
@@ -334,6 +335,7 @@ impl RMain {
334
335
dap : Arc < Mutex < Dap > > ,
335
336
session_mode : SessionMode ,
336
337
default_repos : DefaultRepos ,
338
+ graphics_device_rx : AsyncUnboundedReceiver < GraphicsDeviceNotification > ,
337
339
) {
338
340
// Set the main thread ID.
339
341
// Must happen before doing anything that checks `RMain::on_main_thread()`,
@@ -345,9 +347,7 @@ impl RMain {
345
347
} ;
346
348
}
347
349
348
- // Channels to send/receive tasks from auxiliary threads via `RTask`s
349
- let ( tasks_interrupt_tx, tasks_interrupt_rx) = unbounded :: < RTask > ( ) ;
350
- let ( tasks_idle_tx, tasks_idle_rx) = unbounded :: < RTask > ( ) ;
350
+ let ( tasks_interrupt_rx, tasks_idle_rx) = r_task:: take_receivers ( ) ;
351
351
352
352
R_MAIN . set ( UnsafeCell :: new ( RMain :: new (
353
353
tasks_interrupt_rx,
@@ -364,12 +364,6 @@ impl RMain {
364
364
365
365
let main = RMain :: get_mut ( ) ;
366
366
367
- // Initialize the GD context on this thread
368
- graphics_device:: init_graphics_device (
369
- main. get_comm_manager_tx ( ) . clone ( ) ,
370
- main. get_iopub_tx ( ) . clone ( ) ,
371
- ) ;
372
-
373
367
let mut r_args = r_args. clone ( ) ;
374
368
375
369
// Record if the user has requested that we don't load the site/user level R profiles
@@ -445,12 +439,6 @@ impl RMain {
445
439
. or_log_error ( & format ! ( "Failed to source startup file '{file}' due to" ) ) ;
446
440
}
447
441
448
- // R and ark are now set up enough to allow interrupt-time and idle-time tasks
449
- // to be sent through. Idle-time tasks will be run once we enter
450
- // `read_console()` for the first time. Interrupt-time tasks could be run
451
- // sooner if we hit a check-interrupt before then.
452
- r_task:: initialize ( tasks_interrupt_tx, tasks_idle_tx) ;
453
-
454
442
// Initialize support functions (after routine registration, after r_task initialization)
455
443
match modules:: initialize ( ) {
456
444
Err ( err) => {
@@ -493,6 +481,18 @@ impl RMain {
493
481
) ;
494
482
Self :: complete_initialization ( main. banner . take ( ) , kernel_init_tx) ;
495
483
484
+ // Initialize the GD context on this thread.
485
+ // Note that we do it after init is complete to avoid deadlocking
486
+ // integration tests by spawning an async task. The deadlock is caused
487
+ // by https://github.com/posit-dev/ark/blob/bd827e735970ca17102aeddfbe2c3ccf26950a36/crates/ark/src/r_task.rs#L261.
488
+ // We should be able to remove this escape hatch in `r_task()` by
489
+ // instantiating an `RMain` in unit tests as well.
490
+ graphics_device:: init_graphics_device (
491
+ main. get_comm_manager_tx ( ) . clone ( ) ,
492
+ main. get_iopub_tx ( ) . clone ( ) ,
493
+ graphics_device_rx,
494
+ ) ;
495
+
496
496
// Now that R has started and libr and ark have fully initialized, run site and user
497
497
// level R profiles, in that order
498
498
if !ignore_site_r_profile {
@@ -810,10 +810,16 @@ impl RMain {
810
810
let tasks_interrupt_rx = self . tasks_interrupt_rx . clone ( ) ;
811
811
let tasks_idle_rx = self . tasks_idle_rx . clone ( ) ;
812
812
813
+ // Process R's polled events regularly while waiting for console input.
814
+ // We used to poll every 200ms but that lead to visible delays for the
815
+ // processing of plot events.
816
+ let polled_events_rx = crossbeam:: channel:: tick ( Duration :: from_millis ( 50 ) ) ;
817
+
813
818
let r_request_index = select. recv ( & r_request_rx) ;
814
819
let stdin_reply_index = select. recv ( & stdin_reply_rx) ;
815
820
let kernel_request_index = select. recv ( & kernel_request_rx) ;
816
821
let tasks_interrupt_index = select. recv ( & tasks_interrupt_rx) ;
822
+ let polled_events_index = select. recv ( & polled_events_rx) ;
817
823
818
824
// Don't process idle tasks in browser prompts. We currently don't want
819
825
// idle tasks (e.g. for srcref generation) to run when the call stack is
@@ -859,18 +865,7 @@ impl RMain {
859
865
}
860
866
}
861
867
862
- let oper = select. select_timeout ( Duration :: from_millis ( 200 ) ) ;
863
-
864
- let Ok ( oper) = oper else {
865
- // We hit a timeout. Process idle events because we need to
866
- // pump the event loop while waiting for console input.
867
- //
868
- // Alternatively, we could try to figure out the file
869
- // descriptors that R has open and select() on those for
870
- // available data?
871
- unsafe { Self :: process_idle_events ( ) } ;
872
- continue ;
873
- } ;
868
+ let oper = select. select ( ) ;
874
869
875
870
match oper. index ( ) {
876
871
// We've got an execute request from the frontend
@@ -910,6 +905,12 @@ impl RMain {
910
905
self . handle_task ( task) ;
911
906
} ,
912
907
908
+ // It's time to run R's polled events
909
+ i if i == polled_events_index => {
910
+ let _ = oper. recv ( & polled_events_rx) . unwrap ( ) ;
911
+ Self :: process_idle_events ( ) ;
912
+ } ,
913
+
913
914
i => log:: error!( "Unexpected index in Select: {i}" ) ,
914
915
}
915
916
}
@@ -1845,6 +1846,14 @@ impl RMain {
1845
1846
1846
1847
/// Invoked by the R event loop
1847
1848
fn polled_events ( & mut self ) {
1849
+ // Don't process tasks until R is fully initialized
1850
+ if !Self :: is_initialized ( ) {
1851
+ if !self . tasks_interrupt_rx . is_empty ( ) {
1852
+ log:: trace!( "Delaying execution of interrupt task as R isn't initialized yet" ) ;
1853
+ }
1854
+ return ;
1855
+ }
1856
+
1848
1857
// Skip running tasks if we don't have 128KB of stack space available.
1849
1858
// This is 1/8th of the typical Windows stack space (1MB, whereas macOS
1850
1859
// and Linux have 8MB).
@@ -1863,23 +1872,27 @@ impl RMain {
1863
1872
}
1864
1873
}
1865
1874
1866
- unsafe fn process_idle_events ( ) {
1875
+ fn process_idle_events ( ) {
1867
1876
// Process regular R events. We're normally running with polled
1868
1877
// events disabled so that won't run here. We also run with
1869
1878
// interrupts disabled, so on Windows those won't get run here
1870
1879
// either (i.e. if `UserBreak` is set), but it will reset `UserBreak`
1871
1880
// so we need to ensure we handle interrupts right before calling
1872
1881
// this.
1873
- R_ProcessEvents ( ) ;
1882
+ unsafe { R_ProcessEvents ( ) } ;
1874
1883
1875
1884
crate :: sys:: interface:: run_activity_handlers ( ) ;
1876
1885
1877
1886
// Run pending finalizers. We need to do this eagerly as otherwise finalizers
1878
1887
// might end up being executed on the LSP thread.
1879
1888
// https://github.com/rstudio/positron/issues/431
1880
- R_RunPendingFinalizers ( ) ;
1889
+ unsafe { R_RunPendingFinalizers ( ) } ;
1881
1890
1882
- // Check for Positron render requests
1891
+ // Check for Positron render requests.
1892
+ //
1893
+ // TODO: This should move to a spawned task that'd be woken up by
1894
+ // incoming messages on plot comms. This way we'll prevent the delays
1895
+ // introduced by timeout-based event polling.
1883
1896
graphics_device:: on_process_idle_events ( ) ;
1884
1897
}
1885
1898
0 commit comments