@@ -62,6 +62,9 @@ impl FrameSender for WriterFrameSender {
6262/// Production code uses a channel-based implementation; tests use a buffer-based one.
6363pub trait ResponseReceiver : Send {
6464 fn recv_response ( & self ) -> Result < BinaryFrame , String > ;
65+ /// Defer a frame for later processing (used when sync_call receives
66+ /// a BridgeResponse for a different call_id).
67+ fn defer ( & self , frame : BinaryFrame ) ;
6568}
6669
6770/// ResponseReceiver that reads frames from a byte buffer via ipc_binary::read_frame.
@@ -81,6 +84,10 @@ impl ReaderResponseReceiver {
8184}
8285
8386impl ResponseReceiver for ReaderResponseReceiver {
87+ fn defer ( & self , _frame : BinaryFrame ) {
88+ // Test-only receiver — deferred frames are dropped
89+ }
90+
8491 fn recv_response ( & self ) -> Result < BinaryFrame , String > {
8592 let mut reader = self . reader . lock ( ) . unwrap ( ) ;
8693 ipc_binary:: read_frame ( & mut * reader)
@@ -132,6 +139,10 @@ impl FrameSender for StubFrameSender {
132139struct StubResponseReceiver ;
133140
134141impl ResponseReceiver for StubResponseReceiver {
142+ fn defer ( & self , _frame : BinaryFrame ) {
143+ panic ! ( "stub bridge function called during snapshot creation" )
144+ }
145+
135146 fn recv_response ( & self ) -> Result < BinaryFrame , String > {
136147 panic ! ( "stub bridge function called during snapshot creation — bridge IIFE must not call bridge functions at setup time" )
137148 }
@@ -227,35 +238,42 @@ impl BridgeCallContext {
227238 return Err ( format ! ( "failed to write BridgeCall: {}" , e) ) ;
228239 }
229240
230- // Receive BridgeResponse directly (no re-serialization)
241+ // Receive BridgeResponse matching our call_id.
242+ // Non-matching BridgeResponses (from async bridge calls like timers)
243+ // are deferred for later processing by the event loop.
231244 let response = {
232245 let rx = self . response_rx . lock ( ) . unwrap ( ) ;
233- match rx. recv_response ( ) {
234- Ok ( frame) => frame,
235- Err ( e) => {
236- self . pending_calls . lock ( ) . unwrap ( ) . remove ( & call_id) ;
237- return Err ( e) ;
246+ loop {
247+ match rx. recv_response ( ) {
248+ Ok ( frame) => {
249+ match & frame {
250+ BinaryFrame :: BridgeResponse { call_id : resp_id, .. } if * resp_id == call_id => {
251+ break frame;
252+ }
253+ _ => {
254+ // Non-matching response — defer for event loop
255+ rx. defer ( frame) ;
256+ }
257+ }
258+ }
259+ Err ( e) => {
260+ self . pending_calls . lock ( ) . unwrap ( ) . remove ( & call_id) ;
261+ return Err ( e) ;
262+ }
238263 }
239264 }
240265 } ;
241266
242267 // Remove from pending
243268 self . pending_calls . lock ( ) . unwrap ( ) . remove ( & call_id) ;
244269
245- // Validate and extract BridgeResponse
270+ // Extract BridgeResponse
246271 match response {
247272 BinaryFrame :: BridgeResponse {
248- call_id : resp_id,
249273 status,
250274 payload,
251275 ..
252276 } => {
253- if resp_id != call_id {
254- return Err ( format ! (
255- "call_id mismatch: expected {}, got {}" ,
256- call_id, resp_id
257- ) ) ;
258- }
259277 if status == 1 {
260278 // Error: payload is UTF-8 error message
261279 Err ( String :: from_utf8_lossy ( & payload) . to_string ( ) )
@@ -266,7 +284,7 @@ impl BridgeCallContext {
266284 Ok ( Some ( payload) )
267285 }
268286 }
269- _ => Err ( "expected BridgeResponse, got different message type" . into ( ) ) ,
287+ _ => unreachable ! ( "loop only breaks on BridgeResponse" ) ,
270288 }
271289 }
272290
0 commit comments