5
5
ArrayPrototypeMap,
6
6
ArrayPrototypePush,
7
7
AtomicsAdd,
8
+ AtomicsNotify,
9
+ AtomicsStore,
10
+ AtomicsWaitAsync,
8
11
Float64Array,
9
12
FunctionPrototypeBind,
13
+ Int32Array,
10
14
JSONStringify,
11
15
MathMax,
12
16
ObjectEntries,
@@ -34,12 +38,14 @@ const {
34
38
35
39
const errorCodes = require ( 'internal/errors' ) . codes ;
36
40
const {
41
+ ERR_INVALID_ARG_TYPE ,
42
+ ERR_INVALID_ARG_VALUE ,
43
+ ERR_WORKER_CONNECTION_REFUSED ,
44
+ ERR_WORKER_INVALID_EXEC_ARGV ,
37
45
ERR_WORKER_NOT_RUNNING ,
38
46
ERR_WORKER_PATH ,
47
+ ERR_WORKER_SAME_THREAD ,
39
48
ERR_WORKER_UNSERIALIZABLE_ERROR ,
40
- ERR_WORKER_INVALID_EXEC_ARGV ,
41
- ERR_INVALID_ARG_TYPE ,
42
- ERR_INVALID_ARG_VALUE ,
43
49
} = errorCodes ;
44
50
45
51
const workerIo = require ( 'internal/worker/io' ) ;
@@ -59,7 +65,7 @@ const {
59
65
const { deserializeError } = require ( 'internal/error_serdes' ) ;
60
66
const { fileURLToPath, isURL, pathToFileURL } = require ( 'internal/url' ) ;
61
67
const { kEmptyObject } = require ( 'internal/util' ) ;
62
- const { validateArray, validateString } = require ( 'internal/validators' ) ;
68
+ const { validateArray, validateFunction , validateString } = require ( 'internal/validators' ) ;
63
69
const {
64
70
throwIfBuildingSnapshot,
65
71
} = require ( 'internal/v8/startup_snapshot' ) ;
@@ -74,6 +80,8 @@ const {
74
80
kCodeRangeSizeMb,
75
81
kStackSizeMb,
76
82
kTotalResourceLimitCount,
83
+ sendToWorker,
84
+ setMainPort,
77
85
} = internalBinding ( 'worker' ) ;
78
86
79
87
const kHandle = Symbol ( 'kHandle' ) ;
@@ -100,6 +108,14 @@ let cwdCounter;
100
108
101
109
const environmentData = new SafeMap ( ) ;
102
110
111
+ // SharedArrayBuffer must always be Int32, so it's * 4.
112
+ // We need one for the operation status (performing / performed) and one for the result (success / failure).
113
+ const WORKER_MESSAGING_SHARED_DATA = 2 * 4 ;
114
+ const WORKER_MESSAGING_STATUS_INDEX = 0 ;
115
+ const WORKER_MESSAGING_RESULT_INDEX = 1 ;
116
+ let connectionsListener = null ;
117
+ let mainPortWasSetup = false ;
118
+
103
119
// SharedArrayBuffers can be disabled with --enable-sharedarraybuffer-per-context.
104
120
if ( isMainThread && SharedArrayBuffer !== undefined ) {
105
121
cwdCounter = new Uint32Array ( new SharedArrayBuffer ( 4 ) ) ;
@@ -527,6 +543,79 @@ function eventLoopUtilization(util1, util2) {
527
543
) ;
528
544
}
529
545
546
+ function setConnectionsListener ( fn ) {
547
+ if ( isMainThread && ! mainPortWasSetup ) {
548
+ setupMainPort ( ) ;
549
+ mainPortWasSetup = true ;
550
+ }
551
+
552
+ if ( typeof fn === 'undefined' || fn === null ) {
553
+ connectionsListener = null ;
554
+ return ;
555
+ }
556
+
557
+ validateFunction ( fn , 'fn' ) ;
558
+ connectionsListener = fn ;
559
+ }
560
+
561
+ async function processConnectionRequest ( request ) {
562
+ const status = new Int32Array ( request . memory ) ;
563
+
564
+ try {
565
+ const result = await connectionsListener ?. ( request . sender , request . port , request . data ) ;
566
+ AtomicsStore ( status , WORKER_MESSAGING_RESULT_INDEX , result === true ? 0 : 1 ) ;
567
+ } catch ( e ) {
568
+ debug ( 'connections listener rejected' , e ) ;
569
+ AtomicsStore ( status , WORKER_MESSAGING_RESULT_INDEX , 2 ) ;
570
+ } finally {
571
+ AtomicsNotify ( status , WORKER_MESSAGING_STATUS_INDEX , 1 ) ;
572
+ }
573
+ }
574
+
575
+ async function connect ( target , data ) {
576
+ if ( target === threadId ) {
577
+ throw new ERR_WORKER_SAME_THREAD ( ) ;
578
+ }
579
+
580
+ // Create a shared array to exchange the status and the result
581
+ const memory = new SharedArrayBuffer ( WORKER_MESSAGING_SHARED_DATA ) ;
582
+ const status = new Int32Array ( memory ) ;
583
+ const promise = AtomicsWaitAsync ( status , WORKER_MESSAGING_STATUS_INDEX , 0 ) . value ;
584
+
585
+ // Create the channel and send it to the other thread
586
+ const { port1, port2 } = new MessageChannel ( ) ;
587
+ sendToWorker ( target , { type : messageTypes . CONNECT , sender : threadId , port : port2 , memory, data } , [ port2 ] ) ;
588
+
589
+ // Wait for the response
590
+ await promise ;
591
+
592
+ if ( status [ WORKER_MESSAGING_RESULT_INDEX ] === 1 ) {
593
+ port1 . close ( ) ;
594
+ port2 . close ( ) ;
595
+ throw new ERR_WORKER_CONNECTION_REFUSED ( ) ;
596
+ }
597
+
598
+ return port1 ;
599
+ }
600
+
601
+ function setupMainPort ( ) {
602
+ const { port1, port2 } = new MessageChannel ( ) ;
603
+ setMainPort ( port2 ) ;
604
+
605
+ // Set message management
606
+ port1 . on ( 'message' , ( message ) => {
607
+ if ( message . type === messageTypes . CONNECT ) {
608
+ processConnectionRequest ( message ) ;
609
+ } else {
610
+ assert ( message . type === messageTypes . CONNECT , `Unknown worker message type ${ message . type } ` ) ;
611
+ }
612
+ } ) ;
613
+
614
+ // Never block the process on this channel
615
+ port1 . unref ( ) ;
616
+ port2 . unref ( ) ;
617
+ }
618
+
530
619
module . exports = {
531
620
ownsProcessState,
532
621
kIsOnline,
@@ -537,6 +626,10 @@ module.exports = {
537
626
setEnvironmentData,
538
627
getEnvironmentData,
539
628
assignEnvironmentData,
629
+ setConnectionsListener,
630
+ setupMainPort,
631
+ processConnectionRequest,
632
+ connect,
540
633
threadId,
541
634
InternalWorker,
542
635
Worker,
0 commit comments