@@ -11,6 +11,7 @@ use std::sync::Arc;
1111use std:: sync:: Mutex ;
1212
1313use crossbeam:: channel:: Receiver ;
14+ use crossbeam:: channel:: Select ;
1415use crossbeam:: channel:: Sender ;
1516use futures:: executor:: block_on;
1617use stdext:: result:: ResultExt ;
@@ -37,6 +38,8 @@ use crate::wire::comm_info_request::CommInfoRequest;
3738use crate :: wire:: comm_msg:: CommWireMsg ;
3839use crate :: wire:: comm_open:: CommOpen ;
3940use crate :: wire:: exception:: Exception ;
41+ use crate :: wire:: execute_reply:: ExecuteReply ;
42+ use crate :: wire:: execute_request:: ExecuteRequest ;
4043use crate :: wire:: jupyter_message:: JupyterMessage ;
4144use crate :: wire:: jupyter_message:: Message ;
4245use crate :: wire:: jupyter_message:: ProtocolMessage ;
@@ -205,6 +208,10 @@ impl Shell {
205208 ) ;
206209 } ,
207210
211+ CommEvent :: Barrier ( done_tx) => {
212+ done_tx. send ( ( ) ) . log_err ( ) ;
213+ } ,
214+
208215 CommEvent :: Message ( comm_id, msg) => {
209216 let Some ( comm) = self . open_comms . iter ( ) . find ( |c| c. comm_id == comm_id) else {
210217 log:: warn!( "Received message for unknown comm channel {comm_id}: {msg:?}" ) ;
@@ -240,6 +247,15 @@ impl Shell {
240247 /// Process a message received from the front-end, optionally dispatching
241248 /// messages to the IOPub or execution threads
242249 fn process_message ( & mut self , msg : Message ) -> crate :: Result < ( ) > {
250+ // Execute requests get special handling: Shell select-loops on both
251+ // the execute response and comm events so it can process
252+ // backend-initiated comm opens (with barrier handshakes) while R is
253+ // still executing, preventing a deadlock where R waits for Shell to
254+ // drain the barrier while Shell waits for the execute response.
255+ if let Message :: ExecuteRequest ( req) = msg {
256+ return self . handle_execute_request ( req) ;
257+ }
258+
243259 // Extract references to the components we need to pass to handlers.
244260 // This allows us to borrow different fields of self independently.
245261 let iopub_tx = & self . iopub_tx ;
@@ -256,13 +272,6 @@ impl Shell {
256272 Message :: IsCompleteRequest ( req) => Self :: handle_request ( iopub_tx, socket, req, |msg| {
257273 block_on ( shell_handler. handle_is_complete_request ( msg) )
258274 } ) ,
259- Message :: ExecuteRequest ( req) => {
260- // FIXME: We should ideally not pass the originator to the language kernel
261- let originator = Originator :: from ( & req) ;
262- Self :: handle_request ( iopub_tx, socket, req, |msg| {
263- block_on ( shell_handler. handle_execute_request ( originator, msg) )
264- } )
265- } ,
266275 Message :: CompleteRequest ( req) => Self :: handle_request ( iopub_tx, socket, req, |msg| {
267276 block_on ( shell_handler. handle_complete_request ( msg) )
268277 } ) ,
@@ -363,6 +372,63 @@ impl Shell {
363372 result. and ( Ok ( ( ) ) )
364373 }
365374
375+ /// Handle an execute request. Unlike other requests that use the generic
376+ /// `handle_request`, this method select-loops on both the execute response
377+ /// and `comm_event_rx`. This allows Shell to process comm events (e.g.
378+ /// `CommEvent::Barrier` from `comm_open_backend`) while the R thread is
379+ /// still executing, preventing a deadlock where the R thread waits for
380+ /// Shell to drain comm events while Shell waits for the execute response.
381+ fn handle_execute_request ( & mut self , req : JupyterMessage < ExecuteRequest > ) -> crate :: Result < ( ) > {
382+ self . iopub_tx
383+ . send ( status ( req. clone ( ) , ExecutionState :: Busy ) )
384+ . unwrap ( ) ;
385+
386+ log:: info!( "Received shell request: {req:?}" ) ;
387+
388+ // FIXME: We should ideally not pass the originator to the language kernel
389+ let originator = Originator :: from ( & req) ;
390+ let response_rx = self
391+ . shell_handler
392+ . start_execute_request ( originator, & req. content ) ;
393+
394+ // Select-loop: drain comm events while waiting for the execute reply.
395+ let result = loop {
396+ let mut sel = Select :: new ( ) ;
397+ let resp_idx = sel. recv ( & response_rx) ;
398+ sel. recv ( & self . comm_event_rx ) ;
399+
400+ let ready = sel. ready ( ) ;
401+
402+ while let Ok ( event) = self . comm_event_rx . try_recv ( ) {
403+ self . process_comm_event ( event) ;
404+ }
405+
406+ if ready == resp_idx {
407+ break response_rx. recv ( ) . unwrap ( ) ;
408+ }
409+ } ;
410+
411+ let result = match result {
412+ Ok ( reply) => req. send_reply ( reply, & self . socket ) ,
413+ Err ( crate :: Error :: ShellErrorReply ( error) ) => {
414+ req. send_error :: < ExecuteReply > ( error, & self . socket )
415+ } ,
416+ Err ( crate :: Error :: ShellErrorExecuteReply ( error, exec_count) ) => {
417+ req. send_execute_error ( error, exec_count, & self . socket )
418+ } ,
419+ Err ( err) => {
420+ let error = Exception :: internal_error ( format ! ( "{err:?}" ) ) ;
421+ req. send_error :: < ExecuteReply > ( error, & self . socket )
422+ } ,
423+ } ;
424+
425+ self . iopub_tx
426+ . send ( status ( req. clone ( ) , ExecutionState :: Idle ) )
427+ . unwrap ( ) ;
428+
429+ result. and ( Ok ( ( ) ) )
430+ }
431+
366432 fn handle_notification < Not , Handler > (
367433 iopub_tx : & Sender < IOPubMessage > ,
368434 not : JupyterMessage < Not > ,
0 commit comments