@@ -23,6 +23,8 @@ const errnoException = util._errnoException;
23
23
const SocketListSend = SocketList . SocketListSend ;
24
24
const SocketListReceive = SocketList . SocketListReceive ;
25
25
26
+ const MAX_HANDLE_RETRANSMISSIONS = 3 ;
27
+
26
28
// this object contain function to convert TCP objects to native handle objects
27
29
// and back again.
28
30
const handleConversion = {
@@ -88,17 +90,18 @@ const handleConversion = {
88
90
return handle ;
89
91
} ,
90
92
91
- postSend : function ( handle , options , target ) {
93
+ postSend : function ( message , handle , options , callback , target ) {
92
94
// Store the handle after successfully sending it, so it can be closed
93
95
// when the NODE_HANDLE_ACK is received. If the handle could not be sent,
94
96
// just close it.
95
97
if ( handle && ! options . keepOpen ) {
96
98
if ( target ) {
97
- // There can only be one _pendingHandle as passing handles are
99
+ // There can only be one _pendingMessage as passing handles are
98
100
// processed one at a time: handles are stored in _handleQueue while
99
101
// 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 } ;
102
105
} else {
103
106
handle . close ( ) ;
104
107
}
@@ -249,6 +252,11 @@ function getHandleWrapType(stream) {
249
252
return false ;
250
253
}
251
254
255
+ function closePendingHandle ( target ) {
256
+ target . _pendingMessage . handle . close ( ) ;
257
+ target . _pendingMessage = null ;
258
+ }
259
+
252
260
253
261
ChildProcess . prototype . spawn = function ( options ) {
254
262
var ipc ;
@@ -434,7 +442,7 @@ function setupChannel(target, channel) {
434
442
} ) ;
435
443
436
444
target . _handleQueue = null ;
437
- target . _pendingHandle = null ;
445
+ target . _pendingMessage = null ;
438
446
439
447
const control = new Control ( channel ) ;
440
448
@@ -490,16 +498,31 @@ function setupChannel(target, channel) {
490
498
// handlers will go through this
491
499
target . on ( 'internalMessage' , function ( message , handle ) {
492
500
// 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
+ }
497
513
}
498
514
499
515
assert ( Array . isArray ( target . _handleQueue ) ) ;
500
516
var queue = target . _handleQueue ;
501
517
target . _handleQueue = null ;
502
518
519
+ if ( target . _pendingMessage ) {
520
+ target . _send ( target . _pendingMessage . message ,
521
+ target . _pendingMessage . handle ,
522
+ target . _pendingMessage . options ,
523
+ target . _pendingMessage . callback ) ;
524
+ }
525
+
503
526
for ( var i = 0 ; i < queue . length ; i ++ ) {
504
527
var args = queue [ i ] ;
505
528
target . _send ( args . message , args . handle , args . options , args . callback ) ;
@@ -514,6 +537,12 @@ function setupChannel(target, channel) {
514
537
515
538
if ( message . cmd !== 'NODE_HANDLE' ) return ;
516
539
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
+
517
546
// Acknowledge handle receival. Don't emit error events (for example if
518
547
// the other side has disconnected) because this call to send() is not
519
548
// initiated by the user and it shouldn't be fatal to be unable to ACK
@@ -624,7 +653,8 @@ function setupChannel(target, channel) {
624
653
net . _setSimultaneousAccepts ( handle ) ;
625
654
}
626
655
} else if ( this . _handleQueue &&
627
- ! ( message && message . cmd === 'NODE_HANDLE_ACK' ) ) {
656
+ ! ( message && ( message . cmd === 'NODE_HANDLE_ACK' ||
657
+ message . cmd === 'NODE_HANDLE_NACK' ) ) ) {
628
658
// Queue request anyway to avoid out-of-order messages.
629
659
this . _handleQueue . push ( {
630
660
callback : callback ,
@@ -646,7 +676,7 @@ function setupChannel(target, channel) {
646
676
if ( ! this . _handleQueue )
647
677
this . _handleQueue = [ ] ;
648
678
if ( obj && obj . postSend )
649
- obj . postSend ( handle , options , target ) ;
679
+ obj . postSend ( message , handle , options , callback , target ) ;
650
680
}
651
681
652
682
if ( req . async ) {
@@ -662,7 +692,7 @@ function setupChannel(target, channel) {
662
692
} else {
663
693
// Cleanup handle on error
664
694
if ( obj && obj . postSend )
665
- obj . postSend ( handle , options ) ;
695
+ obj . postSend ( message , handle , options , callback ) ;
666
696
667
697
if ( ! options . swallowErrors ) {
668
698
const ex = errnoException ( err , 'write' ) ;
@@ -711,10 +741,8 @@ function setupChannel(target, channel) {
711
741
// This marks the fact that the channel is actually disconnected.
712
742
this . channel = null ;
713
743
714
- if ( this . _pendingHandle ) {
715
- this . _pendingHandle . close ( ) ;
716
- this . _pendingHandle = null ;
717
- }
744
+ if ( this . _pendingMessage )
745
+ closePendingHandle ( this ) ;
718
746
719
747
var fired = false ;
720
748
function finish ( ) {
0 commit comments