@@ -77,6 +77,19 @@ function eachWorker(cb) {
77
77
}
78
78
}
79
79
80
+ // Extremely simple progress tracker
81
+ function ProgressTracker ( missing , callback ) {
82
+ this . missing = missing ;
83
+ this . callback = callback ;
84
+ }
85
+ ProgressTracker . prototype . done = function ( ) {
86
+ this . missing -= 1 ;
87
+ this . check ( ) ;
88
+ } ;
89
+ ProgressTracker . prototype . check = function ( ) {
90
+ if ( this . missing === 0 ) this . callback ( ) ;
91
+ } ;
92
+
80
93
cluster . setupMaster = function ( options ) {
81
94
// This can only be called from the master.
82
95
assert ( cluster . isMaster ) ;
@@ -239,7 +252,10 @@ if (cluster.isMaster) {
239
252
// Messages to a worker will be handled using this methods
240
253
else if ( cluster . isWorker ) {
241
254
242
- // TODO: the disconnect step will use this
255
+ // Handle worker.disconnect from master
256
+ messageHandingObject . disconnect = function ( message , worker ) {
257
+ worker . disconnect ( ) ;
258
+ } ;
243
259
}
244
260
245
261
function toDecInt ( value ) {
@@ -293,9 +309,11 @@ function Worker(customEnv) {
293
309
} ) ;
294
310
}
295
311
296
- // handle internalMessage and exit event
312
+ // handle internalMessage, exit and disconnect event
297
313
this . process . on ( 'internalMessage' , handleMessage . bind ( null , this ) ) ;
298
314
this . process . on ( 'exit' , prepareDeath . bind ( null , this , 'dead' , 'death' ) ) ;
315
+ this . process . on ( 'disconnect' ,
316
+ prepareDeath . bind ( null , this , 'disconnected' , 'disconnect' ) ) ;
299
317
300
318
// relay message and error
301
319
this . process . on ( 'message' , this . emit . bind ( this , 'message' ) ) ;
@@ -356,14 +374,6 @@ Worker.prototype.send = function() {
356
374
this . process . send . apply ( this . process , arguments ) ;
357
375
} ;
358
376
359
-
360
- function closeWorkerChannel ( worker , callback ) {
361
- //Apparently the .close method is async, but do not have a callback
362
- worker . process . _channel . close ( ) ;
363
- worker . process . _channel = null ;
364
- process . nextTick ( callback ) ;
365
- }
366
-
367
377
// Kill the worker without restarting
368
378
Worker . prototype . destroy = function ( ) {
369
379
var self = this ;
@@ -373,9 +383,14 @@ Worker.prototype.destroy = function() {
373
383
if ( cluster . isMaster ) {
374
384
// Disconnect IPC channel
375
385
// this way the worker won't need to propagate suicide state to master
376
- closeWorkerChannel ( this , function ( ) {
386
+ if ( self . process . connected ) {
387
+ self . process . once ( 'disconnect' , function ( ) {
388
+ self . process . kill ( ) ;
389
+ } ) ;
390
+ self . process . disconnect ( ) ;
391
+ } else {
377
392
self . process . kill ( ) ;
378
- } ) ;
393
+ }
379
394
380
395
} else {
381
396
// Channel is open
@@ -403,6 +418,59 @@ Worker.prototype.destroy = function() {
403
418
}
404
419
} ;
405
420
421
+ // The .disconnect function will close all server and then disconnect
422
+ // the IPC channel.
423
+ if ( cluster . isMaster ) {
424
+ // Used in master
425
+ Worker . prototype . disconnect = function ( ) {
426
+ this . suicide = true ;
427
+
428
+ sendInternalMessage ( this , { cmd : 'disconnect' } ) ;
429
+ } ;
430
+
431
+ } else {
432
+ // Used in workers
433
+ Worker . prototype . disconnect = function ( ) {
434
+ var self = this ;
435
+
436
+ this . suicide = true ;
437
+
438
+ // keep track of open servers
439
+ var servers = Object . keys ( serverLisenters ) . length ;
440
+ var progress = new ProgressTracker ( servers , function ( ) {
441
+ // there are no more servers open so we will close the IPC channel.
442
+ // Closeing the IPC channel will emit emit a disconnect event
443
+ // in both master and worker on the process object.
444
+ // This event will be handled by prepearDeath.
445
+ self . process . disconnect ( ) ;
446
+ } ) ;
447
+
448
+ // depending on where this function was called from (master or worker)
449
+ // the suicide state has allready been set.
450
+ // But it dosn't really matter if we set it again.
451
+ sendInternalMessage ( this , { cmd : 'suicide' } , function ( ) {
452
+ // in case there are no servers
453
+ progress . check ( ) ;
454
+
455
+ // closeing all servers graceful
456
+ var server ;
457
+ for ( var key in serverLisenters ) {
458
+ server = serverLisenters [ key ] ;
459
+
460
+ // in case the server is closed we wont close it again
461
+ if ( server . _handle === null ) {
462
+ progress . done ( ) ;
463
+ continue ;
464
+ }
465
+
466
+ server . on ( 'close' , progress . done . bind ( progress ) ) ;
467
+ server . close ( ) ;
468
+ }
469
+ } ) ;
470
+
471
+ } ;
472
+ }
473
+
406
474
// Fork a new worker
407
475
cluster . fork = function ( env ) {
408
476
// This can only be called from the master.
@@ -414,6 +482,33 @@ cluster.fork = function(env) {
414
482
return ( new cluster . Worker ( env ) ) ;
415
483
} ;
416
484
485
+ // execute .disconnect on all workers and close handlers when done
486
+ cluster . disconnect = function ( callback ) {
487
+ // This can only be called from the master.
488
+ assert ( cluster . isMaster ) ;
489
+
490
+ // Close all TCP handlers when all workers are disconnected
491
+ var workers = Object . keys ( cluster . workers ) . length ;
492
+ var progress = new ProgressTracker ( workers , function ( ) {
493
+ for ( var key in serverHandlers ) {
494
+ serverHandlers [ key ] . close ( ) ;
495
+ delete serverHandlers [ key ] ;
496
+ }
497
+
498
+ // call callback when done
499
+ if ( callback ) callback ( ) ;
500
+ } ) ;
501
+
502
+ // begin disconnecting all workers
503
+ eachWorker ( function ( worker ) {
504
+ worker . once ( 'disconnect' , progress . done . bind ( progress ) ) ;
505
+ worker . disconnect ( ) ;
506
+ } ) ;
507
+
508
+ // in case there wasn't any workers
509
+ progress . check ( ) ;
510
+ } ;
511
+
417
512
// Sync way to quickly kill all cluster workers
418
513
// However the workers may not die instantly
419
514
function quickDestroyCluster ( ) {
0 commit comments