Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 45b9130

Browse files
Benoit Hudsonfilmor
Benoit Hudson
authored andcommitted
Allow user code to register for a callback on shutdown.
This is needed to cleanly close sockets, UI, etc.
1 parent 3e4ebac commit 45b9130

File tree

2 files changed

+139
-7
lines changed

2 files changed

+139
-7
lines changed

src/embed_tests/pyinitialize.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,65 @@ public void ReInitialize()
7474
}
7575
PythonEngine.Shutdown();
7676
}
77+
78+
[Test]
79+
public void TestScopeIsShutdown()
80+
{
81+
PythonEngine.Initialize();
82+
var scope = PyScopeManager.Global.Create("test");
83+
PythonEngine.Shutdown();
84+
Assert.That(PyScopeManager.Global.Contains("test"), Is.False);
85+
}
86+
87+
/// <summary>
88+
/// Helper for testing the shutdown handlers.
89+
/// </summary>
90+
int shutdown_count = 0;
91+
void OnShutdownIncrement()
92+
{
93+
shutdown_count++;
94+
}
95+
void OnShutdownDouble()
96+
{
97+
shutdown_count *= 2;
98+
}
99+
100+
/// <summary>
101+
/// Test the shutdown handlers.
102+
/// </summary>
103+
[Test]
104+
public void ShutdownHandlers()
105+
{
106+
// Test we can run one shutdown handler.
107+
shutdown_count = 0;
108+
PythonEngine.Initialize();
109+
PythonEngine.AddShutdownHandler(OnShutdownIncrement);
110+
PythonEngine.Shutdown();
111+
Assert.That(shutdown_count, Is.EqualTo(1));
112+
113+
// Test we can run multiple shutdown handlers in the right order.
114+
shutdown_count = 4;
115+
PythonEngine.Initialize();
116+
PythonEngine.AddShutdownHandler(OnShutdownIncrement);
117+
PythonEngine.AddShutdownHandler(OnShutdownDouble);
118+
PythonEngine.Shutdown();
119+
// Correct: 4 * 2 + 1 = 9
120+
// Wrong: (4 + 1) * 2 = 10
121+
Assert.That(shutdown_count, Is.EqualTo(9));
122+
123+
// Test we can remove shutdown handlers, handling duplicates.
124+
shutdown_count = 4;
125+
PythonEngine.Initialize();
126+
PythonEngine.AddShutdownHandler(OnShutdownIncrement);
127+
PythonEngine.AddShutdownHandler(OnShutdownIncrement);
128+
PythonEngine.AddShutdownHandler(OnShutdownDouble);
129+
PythonEngine.AddShutdownHandler(OnShutdownIncrement);
130+
PythonEngine.AddShutdownHandler(OnShutdownDouble);
131+
PythonEngine.RemoveShutdownHandler(OnShutdownDouble);
132+
PythonEngine.Shutdown();
133+
// Correct: (4 + 1) * 2 + 1 + 1 = 12
134+
// Wrong: (4 * 2) + 1 + 1 + 1 = 11
135+
Assert.That(shutdown_count, Is.EqualTo(12));
136+
}
77137
}
78138
}

src/runtime/pythonengine.cs

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,16 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true)
168168
initialized = true;
169169
Exceptions.Clear();
170170

171+
// Make sure we clean up properly on app domain unload.
172+
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
173+
174+
// Remember to shut down the runtime.
175+
AddShutdownHandler(Runtime.Shutdown);
176+
177+
// The global scope gets used implicitly quite early on, remember
178+
// to clear it out when we shut down.
179+
AddShutdownHandler(PyScopeManager.Global.Clear);
180+
171181
if (setSysArgv)
172182
{
173183
Py.SetArgv(args);
@@ -220,9 +230,6 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true)
220230
{
221231
locals.Dispose();
222232
}
223-
224-
// Make sure we clean up properly on app domain unload.
225-
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
226233
}
227234
}
228235

@@ -302,21 +309,86 @@ public static void Shutdown()
302309
{
303310
if (initialized)
304311
{
305-
PyScopeManager.Global.Clear();
312+
// If the shutdown handlers trigger a domain unload,
313+
// don't call shutdown again.
314+
AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
315+
316+
ExecuteShutdownHandlers();
317+
306318
Marshal.FreeHGlobal(_pythonHome);
307319
_pythonHome = IntPtr.Zero;
308320
Marshal.FreeHGlobal(_programName);
309321
_programName = IntPtr.Zero;
310322
Marshal.FreeHGlobal(_pythonPath);
311323
_pythonPath = IntPtr.Zero;
312324

313-
Runtime.Shutdown();
314-
315-
AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
316325
initialized = false;
317326
}
318327
}
319328

329+
/// <summary>
330+
/// Called when the engine is shut down.
331+
///
332+
/// Shutdown handlers are run in reverse order they were added, so that
333+
/// resources available when running a shutdown handler are the same as
334+
/// what was available when it was added.
335+
/// </summary>
336+
public delegate void ShutdownHandler();
337+
338+
static List<ShutdownHandler> ShutdownHandlers = new List<ShutdownHandler>();
339+
340+
/// <summary>
341+
/// Add a function to be called when the engine is shut down.
342+
///
343+
/// Shutdown handlers are executed in the opposite order they were
344+
/// added, so that you can be sure that everything that was initialized
345+
/// when you added the handler is still initialized when you need to shut
346+
/// down.
347+
///
348+
/// If the same shutdown handler is added several times, it will be run
349+
/// several times.
350+
///
351+
/// Don't add shutdown handlers while running a shutdown handler.
352+
/// </summary>
353+
public static void AddShutdownHandler(ShutdownHandler handler)
354+
{
355+
ShutdownHandlers.Add(handler);
356+
}
357+
358+
/// <summary>
359+
/// Remove a shutdown handler.
360+
///
361+
/// If the same shutdown handler is added several times, only the last
362+
/// one is removed.
363+
///
364+
/// Don't remove shutdown handlers while running a shutdown handler.
365+
/// </summary>
366+
public static void RemoveShutdownHandler(ShutdownHandler handler)
367+
{
368+
for (int index = ShutdownHandlers.Count - 1; index >= 0; --index)
369+
{
370+
if (ShutdownHandlers[index] == handler)
371+
{
372+
ShutdownHandlers.RemoveAt(index);
373+
break;
374+
}
375+
}
376+
}
377+
378+
/// <summary>
379+
/// Run all the shutdown handlers.
380+
///
381+
/// They're run in opposite order they were added.
382+
/// </summary>
383+
static void ExecuteShutdownHandlers()
384+
{
385+
while(ShutdownHandlers.Count > 0)
386+
{
387+
var handler = ShutdownHandlers[ShutdownHandlers.Count - 1];
388+
ShutdownHandlers.RemoveAt(ShutdownHandlers.Count - 1);
389+
handler();
390+
}
391+
}
320392

321393
/// <summary>
322394
/// AcquireLock Method

0 commit comments

Comments
 (0)