54
54
import com .oracle .graal .python .builtins .objects .PNone ;
55
55
import com .oracle .graal .python .builtins .objects .buffer .PythonBufferAccessLibrary ;
56
56
import com .oracle .graal .python .builtins .objects .bytes .PBytes ;
57
+ import com .oracle .graal .python .builtins .objects .common .SequenceNodes ;
57
58
import com .oracle .graal .python .builtins .objects .common .SequenceStorageNodes ;
58
59
import com .oracle .graal .python .builtins .objects .exception .OSErrorEnum ;
59
60
import com .oracle .graal .python .builtins .objects .ints .PInt ;
71
72
import com .oracle .graal .python .nodes .function .builtins .PythonBinaryBuiltinNode ;
72
73
import com .oracle .graal .python .nodes .function .builtins .PythonUnaryBuiltinNode ;
73
74
import com .oracle .graal .python .nodes .util .CannotCastException ;
75
+ import com .oracle .graal .python .nodes .util .CastToJavaDoubleNode ;
74
76
import com .oracle .graal .python .nodes .util .CastToJavaIntExactNode ;
75
77
import com .oracle .graal .python .nodes .util .CastToJavaIntLossyNode ;
76
78
import com .oracle .graal .python .nodes .util .CastToTruffleStringNode ;
77
79
import com .oracle .graal .python .runtime .GilNode ;
80
+ import com .oracle .graal .python .runtime .PosixSupportLibrary ;
81
+ import com .oracle .graal .python .runtime .PosixSupportLibrary .Timeval ;
78
82
import com .oracle .graal .python .runtime .PythonContext ;
79
83
import com .oracle .graal .python .runtime .PythonContext .SharedMultiprocessingData ;
80
84
import com .oracle .graal .python .runtime .sequence .PSequence ;
@@ -356,32 +360,81 @@ PNone close(@SuppressWarnings("unused") long fd) {
356
360
}
357
361
}
358
362
359
- @ Builtin (name = "_select" , minNumOfPositionalArgs = 1 , parameterNames = { "rlist" } )
363
+ @ Builtin (name = "_select" , minNumOfPositionalArgs = 4 )
360
364
@ GenerateNodeFactory
361
365
abstract static class SelectNode extends PythonBuiltinNode {
366
+ /*
367
+ * We would like to poll two different things with a timeout: the actual file descriptors
368
+ * and the Java managed LinkedBlockingQueues.
369
+ *
370
+ * The LinkedBlockingQueue does not expose anything that would allow us to wait on multiple
371
+ * LinkedBlockingQueues at once, so we'd have to spawn a thread for each or roll out our own
372
+ * synchronization of take/offer to allow that.
373
+ *
374
+ * The actual file descriptors could be backed by Java POSIX emulation layer, or by the
375
+ * native POSIX implementation -- the `select` can run actual native select, which we cannot
376
+ * easily interrupt from Java if one of the LinkedBlockingQueue is unblocked earlier than
377
+ * the native select returns.
378
+ *
379
+ * Given all these complexities, for the time being, we do active waiting here, but at least
380
+ * without holding the GIL, and we also yield in every iteration.
381
+ */
382
+
362
383
@ Specialization
363
- Object doGeneric (VirtualFrame frame , Object rlist ,
384
+ Object doGeneric (VirtualFrame frame , Object multiprocessingFdsList , Object multiprocessingObjsList , Object posixFileObjsList , Object timeoutObj ,
385
+ @ Cached PosixModuleBuiltins .FileDescriptorConversionNode fdConvertor ,
364
386
@ Cached PyObjectSizeNode sizeNode ,
365
387
@ Cached PyObjectGetItem getItem ,
388
+ @ Cached SequenceNodes .GetObjectArrayNode getObjectArrayNode ,
366
389
@ Cached ListNodes .FastConstructListNode constructListNode ,
367
390
@ Cached CastToJavaIntLossyNode castToJava ,
391
+ @ Cached CastToJavaDoubleNode castToDouble ,
368
392
@ Cached GilNode gil ) {
369
- ArrayBuilder <Integer > notEmpty = new ArrayBuilder <>();
370
- SharedMultiprocessingData sharedData = getContext ().getSharedMultiprocessingData ();
371
- PSequence pSequence = constructListNode .execute (frame , rlist );
372
- for (int i = 0 ; i < sizeNode .execute (frame , pSequence ); i ++) {
393
+ PythonContext context = getContext ();
394
+ SharedMultiprocessingData sharedData = context .getSharedMultiprocessingData ();
395
+
396
+ PSequence pSequence = constructListNode .execute (frame , multiprocessingFdsList );
397
+ int size = sizeNode .execute (frame , pSequence );
398
+ int [] multiprocessingFds = new int [size ];
399
+ for (int i = 0 ; i < size ; i ++) {
373
400
Object pythonObject = getItem .execute (frame , pSequence , i );
374
- int fd = toInt (castToJava , pythonObject );
375
- gil .release (true );
376
- try {
377
- if (!sharedData .isBlocking (fd )) {
378
- notEmpty .add (fd );
401
+ multiprocessingFds [i ] = toInt (castToJava , pythonObject );
402
+ }
403
+
404
+ Object [] posixFileObjs = getObjectArrayNode .execute (posixFileObjsList );
405
+ int [] posixFds = new int [posixFileObjs .length ];
406
+ for (int i = 0 ; i < posixFileObjs .length ; i ++) {
407
+ posixFds [i ] = toInt (castToJava , fdConvertor .execute (frame , posixFileObjs [i ]));
408
+ }
409
+
410
+ double timeout = castToDouble .execute (timeoutObj );
411
+
412
+ Object [] multiprocessingObjs = getObjectArrayNode .execute (multiprocessingObjsList );
413
+ gil .release (true );
414
+ try {
415
+ boolean [] selectedMultiprocessingFds = new boolean [multiprocessingFds .length ];
416
+ boolean [] selectedPosixFds = new boolean [posixFds .length ];
417
+
418
+ doSelect (context .getPosixSupport (), sharedData , posixFds , selectedPosixFds , multiprocessingFds , selectedMultiprocessingFds , timeout );
419
+
420
+ ArrayBuilder <Object > result = new ArrayBuilder <>(4 );
421
+ for (int i = 0 ; i < selectedMultiprocessingFds .length ; i ++) {
422
+ if (selectedMultiprocessingFds [i ]) {
423
+ result .add (multiprocessingObjs [i ]);
424
+ }
425
+ }
426
+ for (int i = 0 ; i < selectedPosixFds .length ; i ++) {
427
+ if (selectedPosixFds [i ]) {
428
+ result .add (posixFileObjs [i ]);
379
429
}
380
- } finally {
381
- gil .acquire ();
382
430
}
431
+
432
+ return factory ().createList (result .toArray (new Object [0 ]));
433
+ } catch (PosixSupportLibrary .PosixException e ) {
434
+ throw raiseOSErrorFromPosixException (frame , e );
435
+ } finally {
436
+ gil .acquire ();
383
437
}
384
- return factory ().createList (notEmpty .toObjectArray (new Object [0 ]));
385
438
}
386
439
387
440
private static int toInt (CastToJavaIntLossyNode castToJava , Object pythonObject ) {
@@ -391,6 +444,48 @@ private static int toInt(CastToJavaIntLossyNode castToJava, Object pythonObject)
391
444
throw CompilerDirectives .shouldNotReachHere ();
392
445
}
393
446
}
447
+
448
+ @ TruffleBoundary
449
+ private static void doSelect (Object posix , SharedMultiprocessingData sharedData ,
450
+ int [] posixFds , boolean [] selectedPosixFds ,
451
+ int [] multiprocessingFds , boolean [] selectedMultiprocessingFds ,
452
+ double timeoutInS ) throws PosixSupportLibrary .PosixException {
453
+ PosixSupportLibrary posixLib = PosixSupportLibrary .getUncached ();
454
+ boolean blocking = timeoutInS >= 0 ;
455
+ boolean untilReady = timeoutInS == 0 ;
456
+ long deadline = 0 ;
457
+ if (blocking && !untilReady ) {
458
+ long timeout = (long ) (timeoutInS * 1000_000_000.0 );
459
+ deadline = System .nanoTime () + timeout ;
460
+ }
461
+ while (true ) {
462
+ boolean selected = false ;
463
+ if (posixFds .length > 0 ) {
464
+ PosixSupportLibrary .SelectResult selectResult = posixLib .select (posix , posixFds ,
465
+ PythonUtils .EMPTY_INT_ARRAY , PythonUtils .EMPTY_INT_ARRAY , Timeval .SELECT_TIMEOUT_NOW );
466
+ System .arraycopy (selectResult .getReadFds (), 0 , selectedPosixFds , 0 , selectedPosixFds .length );
467
+ if (blocking ) {
468
+ for (boolean b : selectedPosixFds ) {
469
+ selected |= b ;
470
+ }
471
+ }
472
+ }
473
+ for (int i = 0 ; i < multiprocessingFds .length ; i ++) {
474
+ int fd = multiprocessingFds [i ];
475
+ selectedMultiprocessingFds [i ] = !sharedData .isBlocking (fd );
476
+ if (selectedMultiprocessingFds [i ]) {
477
+ selected = true ;
478
+ }
479
+ }
480
+ if (!blocking || selected ) {
481
+ return ;
482
+ }
483
+ if (deadline != 0 && deadline - System .nanoTime () < 0 ) {
484
+ return ;
485
+ }
486
+ Thread .yield ();
487
+ }
488
+ }
394
489
}
395
490
396
491
}
0 commit comments