@@ -314,16 +314,13 @@ public static object default_int_handlerImpl(int signalnum, TraceBackFrame? fram
314
314
anything else -- the callable Python object used as a handler
315
315
""" ) ]
316
316
public static object ? getsignal ( CodeContext /*!*/ context , int signalnum ) {
317
- lock ( GetPythonSignalState ( context ) . PySignalToPyHandler ) {
318
- // Negative Scenarios
319
- if ( signalnum <= 0 || signalnum >= NSIG ) {
320
- throw PythonOps . ValueError ( "signal number out of range" ) ;
321
- } else if ( GetPythonSignalState ( context ) . PySignalToPyHandler . TryGetValue ( signalnum , out object ? value ) ) {
322
- // Default
317
+ if ( signalnum <= 0 || signalnum >= NSIG ) throw PythonOps . ValueError ( "signal number out of range" ) ;
318
+
319
+ var state = GetPythonSignalState ( context ) ;
320
+ lock ( state . SyncRoot ) {
321
+ if ( state . TryGetPyHandler ( signalnum , out object ? value ) ) {
323
322
return value ;
324
323
} else {
325
- // Handles the special case of SIG_IGN. This is not really a signal,
326
- // but CPython returns null for it any ways
327
324
return null ;
328
325
}
329
326
}
@@ -375,12 +372,16 @@ public static object default_int_handlerImpl(int signalnum, TraceBackFrame? fram
375
372
if ( signalnum == SIGKILL || signalnum == SIGSTOP ) throw PythonNT . GetOsError ( PythonErrno . EINVAL ) ;
376
373
}
377
374
object ? last_handler = null ;
378
- lock ( GetPythonSignalState ( context ) . PySignalToPyHandler ) {
375
+ var state = GetPythonSignalState ( context ) ;
376
+ lock ( state . SyncRoot ) {
379
377
// CPython returns the previous handler for the signal
380
- last_handler = getsignal ( context , signalnum ) ;
381
- if ( last_handler is null ) throw PythonNT . GetOsError ( PythonErrno . EINVAL ) ;
378
+ if ( ! state . TryGetPyHandler ( signalnum , out last_handler ) || last_handler is null ) {
379
+ // null marks signals that cannot be handled or are unsupported
380
+ throw PythonNT . GetOsError ( PythonErrno . EINVAL ) ;
381
+ }
382
+
382
383
// Set the new action
383
- GetPythonSignalState ( context ) . PySignalToPyHandler [ signalnum ] = action ;
384
+ state . SetPyHandler ( signalnum , action ) ;
384
385
}
385
386
386
387
return last_handler ;
@@ -417,35 +418,97 @@ private static void SetPythonSignalState(CodeContext/*!*/ context, PythonSignalS
417
418
context . LanguageContext . SetModuleState ( _PythonSignalStateKey , pss ) ;
418
419
}
419
420
420
-
421
+ /// <summary>
422
+ /// This class is used to store the installed signal handlers.
423
+ /// </summary>
421
424
private class PythonSignalState {
422
425
// this provides us with access to the Main thread's stack
423
- public PythonContext SignalPythonContext ;
424
-
425
- // Map out signal identifiers to their actual handlers
426
- public Dictionary < int , object > PySignalToPyHandler ;
426
+ public readonly PythonContext SignalPythonContext ;
427
+
428
+ public object SyncRoot => PySignalToPyHandler ;
429
+
430
+ /// <summary>
431
+ /// Map out signal identifiers to their actual handlers.
432
+ /// </summary>
433
+ /// <remarks>
434
+ /// The objects in this array are either:
435
+ /// 1. Int32(SIG_DFL) - let the OS handle the signal in the default way;
436
+ /// 2. Int32(SIG_IGN) - ignore the signal;
437
+ /// 3. a callable Python object - the handler for the signal;
438
+ /// 4. null - the signal (or handling thereof) is not supported on this platform.
439
+ /// </remarks>
440
+ protected readonly object ? [ ] PySignalToPyHandler ;
427
441
428
442
public PythonSignalState ( PythonContext pc ) {
429
443
SignalPythonContext = pc ;
444
+ PySignalToPyHandler = new object [ NSIG ] ;
445
+
446
+ object sig_dfl = ScriptingRuntimeHelpers . Int32ToObject ( SIG_DFL ) ;
447
+ object sig_ign = ScriptingRuntimeHelpers . Int32ToObject ( SIG_IGN ) ;
448
+
430
449
int [ ] sigs = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? _PySupportedSignals_Windows
431
450
: RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ? _PySupportedSignals_MacOS
432
451
: RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) ? _PySupportedSignals_Linux
433
452
: throw new NotSupportedException ( "Unsupported platform for signal module" ) ;
434
453
435
- PySignalToPyHandler = new Dictionary < int , object > ( sigs . Length ) ;
436
- object sig_dfl = ScriptingRuntimeHelpers . Int32ToObject ( SIG_DFL ) ;
437
- object sig_ign = ScriptingRuntimeHelpers . Int32ToObject ( SIG_IGN ) ;
454
+ // Setting all defined signals to SIG_DFL
438
455
foreach ( int sig in sigs ) {
439
456
PySignalToPyHandler [ sig ] = sig_dfl ;
440
457
}
458
+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) ) {
459
+ for ( int sig = SIGRTMIN ; sig <= SIGRTMAX ; sig ++ ) {
460
+ PySignalToPyHandler [ sig ] = sig_dfl ;
461
+ }
462
+ }
463
+
464
+ // Setting exceptions to the rule
441
465
PySignalToPyHandler [ SIGINT ] = default_int_handler ;
442
466
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) || RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ) {
443
467
PySignalToPyHandler [ SIGPIPE ] = sig_ign ;
444
468
PySignalToPyHandler [ SIGXFSZ ] = sig_ign ;
445
469
}
446
470
if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ) {
447
- PySignalToPyHandler . Remove ( SIGKILL ) ;
448
- PySignalToPyHandler . Remove ( SIGSTOP ) ;
471
+ PySignalToPyHandler [ SIGKILL ] = null ;
472
+ PySignalToPyHandler [ SIGSTOP ] = null ;
473
+ }
474
+ }
475
+
476
+
477
+ public virtual bool TryGetPyHandler ( int signalnum , out object ? value )
478
+ => ( value = PySignalToPyHandler [ signalnum ] ) != null ;
479
+
480
+
481
+ public virtual void SetPyHandler ( int signalnum , object value )
482
+ => PySignalToPyHandler [ signalnum ] = value ;
483
+
484
+
485
+ /// <summary>
486
+ /// Call the Python handler callable passing the given signal number as argument to the callable.
487
+ /// </summary>
488
+ protected void CallPythonHandler ( int signum , object ? handler ) {
489
+ if ( handler is null ) return ;
490
+
491
+ if ( handler == default_int_handler ) {
492
+ // We're dealing with the default_int_handlerImpl which we
493
+ // know doesn't care about the frame parameter
494
+ default_int_handlerImpl ( signum , null ) ;
495
+ return ;
496
+ } else {
497
+ // We're dealing with a callable matching PySignalHandler's signature
498
+ try {
499
+ PySignalHandler temp = ( PySignalHandler ) Converter . ConvertToDelegate ( handler ,
500
+ typeof ( PySignalHandler ) ) ;
501
+
502
+ if ( SignalPythonContext . PythonOptions . Frames ) {
503
+ temp . Invoke ( signum , SysModule . _getframeImpl ( null ,
504
+ 0 ,
505
+ SignalPythonContext . _mainThreadFunctionStack ) ) ;
506
+ } else {
507
+ temp . Invoke ( signum , null ) ;
508
+ }
509
+ } catch ( Exception ex ) {
510
+ System . Console . WriteLine ( SignalPythonContext . FormatException ( ex ) ) ;
511
+ }
449
512
}
450
513
}
451
514
}
0 commit comments