@@ -314,16 +314,13 @@ public static object default_int_handlerImpl(int signalnum, TraceBackFrame? fram
314314 anything else -- the callable Python object used as a handler
315315 """ ) ]
316316 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 ) ) {
323322 return value ;
324323 } else {
325- // Handles the special case of SIG_IGN. This is not really a signal,
326- // but CPython returns null for it any ways
327324 return null ;
328325 }
329326 }
@@ -375,12 +372,16 @@ public static object default_int_handlerImpl(int signalnum, TraceBackFrame? fram
375372 if ( signalnum == SIGKILL || signalnum == SIGSTOP ) throw PythonNT . GetOsError ( PythonErrno . EINVAL ) ;
376373 }
377374 object ? last_handler = null ;
378- lock ( GetPythonSignalState ( context ) . PySignalToPyHandler ) {
375+ var state = GetPythonSignalState ( context ) ;
376+ lock ( state . SyncRoot ) {
379377 // 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+
382383 // Set the new action
383- GetPythonSignalState ( context ) . PySignalToPyHandler [ signalnum ] = action ;
384+ state . SetPyHandler ( signalnum , action ) ;
384385 }
385386
386387 return last_handler ;
@@ -417,35 +418,97 @@ private static void SetPythonSignalState(CodeContext/*!*/ context, PythonSignalS
417418 context . LanguageContext . SetModuleState ( _PythonSignalStateKey , pss ) ;
418419 }
419420
420-
421+ /// <summary>
422+ /// This class is used to store the installed signal handlers.
423+ /// </summary>
421424 private class PythonSignalState {
422425 // 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 ;
427441
428442 public PythonSignalState ( PythonContext pc ) {
429443 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+
430449 int [ ] sigs = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ? _PySupportedSignals_Windows
431450 : RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ? _PySupportedSignals_MacOS
432451 : RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) ? _PySupportedSignals_Linux
433452 : throw new NotSupportedException ( "Unsupported platform for signal module" ) ;
434453
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
438455 foreach ( int sig in sigs ) {
439456 PySignalToPyHandler [ sig ] = sig_dfl ;
440457 }
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
441465 PySignalToPyHandler [ SIGINT ] = default_int_handler ;
442466 if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) || RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) ) {
443467 PySignalToPyHandler [ SIGPIPE ] = sig_ign ;
444468 PySignalToPyHandler [ SIGXFSZ ] = sig_ign ;
445469 }
446470 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+ }
449512 }
450513 }
451514 }
0 commit comments