Skip to content

Commit c6043a7

Browse files
authored
Unregister signal handlers during engine shutdown (#1953)
1 parent 4758db7 commit c6043a7

File tree

4 files changed

+55
-2
lines changed

4 files changed

+55
-2
lines changed

src/core/IronPython.Modules/signal.NtSignalState.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ public NtSignalState(PythonContext pc) : base(pc) {
2727
}
2828

2929

30+
protected override void Dispose(bool disposing) {
31+
if (disposing) {
32+
NativeWindowsSignal.SetConsoleCtrlHandler(this.WinAllSignalsHandlerDelegate, false);
33+
} else {
34+
var winAllSignalsHandlerDelegate = new NativeWindowsSignal.WinSignalsHandler(WindowsEventHandler);
35+
NativeWindowsSignal.SetConsoleCtrlHandler(winAllSignalsHandlerDelegate, false);
36+
}
37+
base.Dispose(disposing);
38+
}
39+
40+
3041
// Our implementation of WinSignalsHandler
3142
private bool WindowsEventHandler(uint winSignal) {
3243
bool retVal;

src/core/IronPython.Modules/signal.SimpleSignalState.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
using System;
1010
using System.Runtime.InteropServices;
1111

12+
using Microsoft.Scripting.Hosting.Shell;
13+
1214
using IronPython.Runtime;
1315

1416
namespace IronPython.Modules {
@@ -18,13 +20,23 @@ private class SimpleSignalState : PythonSignalState {
1820

1921
public SimpleSignalState(PythonContext pc) : base(pc) {
2022
Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);
21-
if (pc.Console is Microsoft.Scripting.Hosting.Shell.BasicConsole console) {
23+
if (pc.Console is BasicConsole console) {
2224
// in console hosting scenarios, we need to override the console handler of Ctrl+C
2325
_consoleHandler = console.ConsoleCancelEventHandler;
2426
console.ConsoleCancelEventHandler = null;
2527
}
2628
}
2729

30+
protected override void Dispose(bool disposing) {
31+
if (disposing) {
32+
Console.CancelKeyPress -= new ConsoleCancelEventHandler(Console_CancelKeyPress);
33+
if (_consoleHandler != null && DefaultContext.DefaultPythonContext.Console is BasicConsole console) {
34+
// restore the original console handler
35+
console.ConsoleCancelEventHandler = _consoleHandler;
36+
}
37+
}
38+
base.Dispose(disposing);
39+
}
2840

2941
private void Console_CancelKeyPress(object? sender, ConsoleCancelEventArgs e) {
3042
int pySignal = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? SIGINT

src/core/IronPython.Modules/signal.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ private static void SetPythonSignalState(CodeContext/*!*/ context, PythonSignalS
421421
/// <summary>
422422
/// This class is used to store the installed signal handlers.
423423
/// </summary>
424-
private class PythonSignalState {
424+
private class PythonSignalState : IDisposable {
425425
// this provides us with access to the Main thread's stack
426426
public readonly PythonContext SignalPythonContext;
427427

@@ -511,6 +511,25 @@ protected void CallPythonHandler(int signum, object? handler) {
511511
}
512512
}
513513
}
514+
515+
public void Dispose() {
516+
Dispose(true);
517+
GC.SuppressFinalize(this);
518+
}
519+
520+
~PythonSignalState() {
521+
Dispose(false);
522+
}
523+
524+
protected virtual void Dispose(bool disposing) {
525+
if (!_disposed) {
526+
if (disposing) {
527+
Array.Clear(PySignalToPyHandler, 0, PySignalToPyHandler.Length);
528+
}
529+
_disposed = true;
530+
}
531+
}
532+
private bool _disposed = false;
514533
}
515534

516535

src/core/IronPython/Runtime/PythonContext.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,11 +545,16 @@ public object GetModuleState(object key) {
545545
/// Sets per-runtime state used by a module. The module should have a unique key for
546546
/// each piece of state it needs to store.
547547
/// </summary>
548+
/// <remarks>
549+
/// The previous piece of state (if any) is disposed if it implements IDisposable.
550+
/// </remarks>
548551
public void SetModuleState(object key, object value) {
549552
EnsureModuleState();
550553

551554
lock (_moduleState) {
555+
_moduleState.TryGetValue(key, out object oldState);
552556
_moduleState[key] = value;
557+
(oldState as IDisposable)?.Dispose();
553558
}
554559
}
555560

@@ -1319,6 +1324,12 @@ public override void Shutdown() {
13191324
Flush(SharedContext, SystemStandardOut);
13201325
Flush(SharedContext, SystemStandardError);
13211326

1327+
lock (_moduleState) {
1328+
foreach (var state in _moduleState.Values) {
1329+
(state as IDisposable)?.Dispose();
1330+
}
1331+
}
1332+
13221333
static void Flush(CodeContext context, object obj) {
13231334
if (obj is PythonIOModule._IOBase pf) {
13241335
if (!pf.closed)

0 commit comments

Comments
 (0)