@@ -369,16 +369,25 @@ function setupChannel(target, channel) {
369369 assert ( util . isArray ( target . _handleQueue ) ) ;
370370 var queue = target . _handleQueue ;
371371 target . _handleQueue = null ;
372+
372373 queue . forEach ( function ( args ) {
373- target . send ( args . message , args . handle ) ;
374+ target . _send ( args . message , args . handle , false ) ;
374375 } ) ;
376+
377+ // Process a pending disconnect (if any).
378+ if ( ! target . connected && target . _channel && ! target . _handleQueue )
379+ target . _disconnect ( ) ;
380+
375381 return ;
376382 }
377383
378384 if ( message . cmd !== 'NODE_HANDLE' ) return ;
379385
380- // Acknowledge handle receival.
381- target . send ( { cmd : 'NODE_HANDLE_ACK' } ) ;
386+ // Acknowledge handle receival. Don't emit error events (for example if
387+ // the other side has disconnected) because this call to send() is not
388+ // initiated by the user and it shouldn't be fatal to be unable to ACK
389+ // a message.
390+ target . _send ( { cmd : 'NODE_HANDLE_ACK' } , null , true ) ;
382391
383392 var obj = handleConversion [ message . type ] ;
384393
@@ -395,14 +404,17 @@ function setupChannel(target, channel) {
395404 } ) ;
396405
397406 target . send = function ( message , handle ) {
398- if ( util . isUndefined ( message ) ) {
399- throw new TypeError ( 'message cannot be undefined' ) ;
400- }
401-
402- if ( ! this . connected ) {
407+ if ( ! this . connected )
403408 this . emit ( 'error' , new Error ( 'channel closed' ) ) ;
404- return ;
405- }
409+ else
410+ this . _send ( message , handle , false ) ;
411+ } ;
412+
413+ target . _send = function ( message , handle , swallowErrors ) {
414+ assert ( this . connected || this . _channel ) ;
415+
416+ if ( util . isUndefined ( message ) )
417+ throw new TypeError ( 'message cannot be undefined' ) ;
406418
407419 // package messages with a handle object
408420 if ( handle ) {
@@ -454,7 +466,8 @@ function setupChannel(target, channel) {
454466 var err = channel . writeUtf8String ( req , string , handle ) ;
455467
456468 if ( err ) {
457- this . emit ( 'error' , errnoException ( err , 'write' ) ) ;
469+ if ( ! swallowErrors )
470+ this . emit ( 'error' , errnoException ( err , 'write' ) ) ;
458471 } else if ( handle && ! this . _handleQueue ) {
459472 this . _handleQueue = [ ] ;
460473 }
@@ -467,15 +480,37 @@ function setupChannel(target, channel) {
467480 return channel . writeQueueSize < ( 65536 * 2 ) ;
468481 } ;
469482
483+ // connected will be set to false immediately when a disconnect() is
484+ // requested, even though the channel might still be alive internally to
485+ // process queued messages. The three states are distinguished as follows:
486+ // - disconnect() never requested: _channel is not null and connected
487+ // is true
488+ // - disconnect() requested, messages in the queue: _channel is not null
489+ // and connected is false
490+ // - disconnect() requested, channel actually disconnected: _channel is
491+ // null and connected is false
470492 target . connected = true ;
493+
471494 target . disconnect = function ( ) {
472495 if ( ! this . connected ) {
473496 this . emit ( 'error' , new Error ( 'IPC channel is already disconnected' ) ) ;
474497 return ;
475498 }
476499
477- // do not allow messages to be written
500+ // Do not allow any new messages to be written.
478501 this . connected = false ;
502+
503+ // If there are no queued messages, disconnect immediately. Otherwise,
504+ // postpone the disconnect so that it happens internally after the
505+ // queue is flushed.
506+ if ( ! this . _handleQueue )
507+ this . _disconnect ( ) ;
508+ }
509+
510+ target . _disconnect = function ( ) {
511+ assert ( this . _channel ) ;
512+
513+ // This marks the fact that the channel is actually disconnected.
479514 this . _channel = null ;
480515
481516 var fired = false ;
0 commit comments