@@ -23,6 +23,8 @@ const errnoException = util._errnoException;
2323const SocketListSend = SocketList . SocketListSend ;
2424const SocketListReceive = SocketList . SocketListReceive ;
2525
26+ const MAX_HANDLE_RETRANSMISSIONS = 3 ;
27+
2628// this object contain function to convert TCP objects to native handle objects
2729// and back again.
2830const handleConversion = {
@@ -88,17 +90,18 @@ const handleConversion ={
8890return handle ;
8991} ,
9092
91- postSend : function ( handle , options , target ) {
93+ postSend : function ( message , handle , options , callback , target ) {
9294// Store the handle after successfully sending it, so it can be closed
9395// when the NODE_HANDLE_ACK is received. If the handle could not be sent,
9496// just close it.
9597if ( handle && ! options . keepOpen ) {
9698if ( target ) {
97- // There can only be one _pendingHandle as passing handles are
99+ // There can only be one _pendingMessage as passing handles are
98100// processed one at a time: handles are stored in _handleQueue while
99101// waiting for the NODE_HANDLE_ACK of the current passing handle.
100- assert ( ! target . _pendingHandle ) ;
101- target . _pendingHandle = handle ;
102+ assert ( ! target . _pendingMessage ) ;
103+ target . _pendingMessage =
104+ { callback, message, handle, options, retransmissions : 0 } ;
102105} else {
103106handle . close ( ) ;
104107}
@@ -249,6 +252,11 @@ function getHandleWrapType(stream){
249252return false ;
250253}
251254
255+ function closePendingHandle ( target ) {
256+ target . _pendingMessage . handle . close ( ) ;
257+ target . _pendingMessage = null ;
258+ }
259+
252260
253261ChildProcess . prototype . spawn = function ( options ) {
254262var ipc ;
@@ -434,7 +442,7 @@ function setupChannel(target, channel){
434442} ) ;
435443
436444target . _handleQueue = null ;
437- target . _pendingHandle = null ;
445+ target . _pendingMessage = null ;
438446
439447const control = new Control ( channel ) ;
440448
@@ -490,16 +498,31 @@ function setupChannel(target, channel){
490498// handlers will go through this
491499target . on ( 'internalMessage' , function ( message , handle ) {
492500// Once acknowledged - continue sending handles.
493- if ( message . cmd === 'NODE_HANDLE_ACK' ) {
494- if ( target . _pendingHandle ) {
495- target . _pendingHandle . close ( ) ;
496- target . _pendingHandle = null ;
501+ if ( message . cmd === 'NODE_HANDLE_ACK' ||
502+ message . cmd === 'NODE_HANDLE_NACK' ) {
503+
504+ if ( target . _pendingMessage ) {
505+ if ( message . cmd === 'NODE_HANDLE_ACK' ) {
506+ closePendingHandle ( target ) ;
507+ } else if ( target . _pendingMessage . retransmissions ++ ===
508+ MAX_HANDLE_RETRANSMISSIONS ) {
509+ closePendingHandle ( target ) ;
510+ process . emitWarning ( 'Handle did not reach the receiving process ' +
511+ 'correctly' , 'SentHandleNotReceivedWarning' ) ;
512+ }
497513}
498514
499515assert ( Array . isArray ( target . _handleQueue ) ) ;
500516var queue = target . _handleQueue ;
501517target . _handleQueue = null ;
502518
519+ if ( target . _pendingMessage ) {
520+ target . _send ( target . _pendingMessage . message ,
521+ target . _pendingMessage . handle ,
522+ target . _pendingMessage . options ,
523+ target . _pendingMessage . callback ) ;
524+ }
525+
503526for ( var i = 0 ; i < queue . length ; i ++ ) {
504527var args = queue [ i ] ;
505528target . _send ( args . message , args . handle , args . options , args . callback ) ;
@@ -514,6 +537,12 @@ function setupChannel(target, channel){
514537
515538if ( message . cmd !== 'NODE_HANDLE' ) return ;
516539
540+ // It is possible that the handle is not received because of some error on
541+ // ancillary data reception such as MSG_CTRUNC. In this case, report the
542+ // sender about it by sending a NODE_HANDLE_NACK message.
543+ if ( ! handle )
544+ return target . _send ( { cmd : 'NODE_HANDLE_NACK' } , null , true ) ;
545+
517546// Acknowledge handle receival. Don't emit error events (for example if
518547// the other side has disconnected) because this call to send() is not
519548// initiated by the user and it shouldn't be fatal to be unable to ACK
@@ -624,7 +653,8 @@ function setupChannel(target, channel){
624653net . _setSimultaneousAccepts ( handle ) ;
625654}
626655} else if ( this . _handleQueue &&
627- ! ( message && message . cmd === 'NODE_HANDLE_ACK' ) ) {
656+ ! ( message && ( message . cmd === 'NODE_HANDLE_ACK' ||
657+ message . cmd === 'NODE_HANDLE_NACK' ) ) ) {
628658// Queue request anyway to avoid out-of-order messages.
629659this . _handleQueue . push ( {
630660callback : callback ,
@@ -646,7 +676,7 @@ function setupChannel(target, channel){
646676if ( ! this . _handleQueue )
647677this . _handleQueue = [ ] ;
648678if ( obj && obj . postSend )
649- obj . postSend ( handle , options , target ) ;
679+ obj . postSend ( message , handle , options , callback , target ) ;
650680}
651681
652682if ( req . async ) {
@@ -662,7 +692,7 @@ function setupChannel(target, channel){
662692} else {
663693// Cleanup handle on error
664694if ( obj && obj . postSend )
665- obj . postSend ( handle , options ) ;
695+ obj . postSend ( message , handle , options , callback ) ;
666696
667697if ( ! options . swallowErrors ) {
668698const ex = errnoException ( err , 'write' ) ;
@@ -711,10 +741,8 @@ function setupChannel(target, channel){
711741// This marks the fact that the channel is actually disconnected.
712742this . channel = null ;
713743
714- if ( this . _pendingHandle ) {
715- this . _pendingHandle . close ( ) ;
716- this . _pendingHandle = null ;
717- }
744+ if ( this . _pendingMessage )
745+ closePendingHandle ( this ) ;
718746
719747var fired = false ;
720748function finish ( ) {
0 commit comments