@@ -168,6 +168,16 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true)
168
168
initialized = true ;
169
169
Exceptions . Clear ( ) ;
170
170
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
+
171
181
if ( setSysArgv )
172
182
{
173
183
Py . SetArgv ( args ) ;
@@ -220,9 +230,6 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true)
220
230
{
221
231
locals . Dispose ( ) ;
222
232
}
223
-
224
- // Make sure we clean up properly on app domain unload.
225
- AppDomain . CurrentDomain . DomainUnload += OnDomainUnload ;
226
233
}
227
234
}
228
235
@@ -302,21 +309,86 @@ public static void Shutdown()
302
309
{
303
310
if ( initialized )
304
311
{
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
+
306
318
Marshal . FreeHGlobal ( _pythonHome ) ;
307
319
_pythonHome = IntPtr . Zero ;
308
320
Marshal . FreeHGlobal ( _programName ) ;
309
321
_programName = IntPtr . Zero ;
310
322
Marshal . FreeHGlobal ( _pythonPath ) ;
311
323
_pythonPath = IntPtr . Zero ;
312
324
313
- Runtime . Shutdown ( ) ;
314
-
315
- AppDomain . CurrentDomain . DomainUnload -= OnDomainUnload ;
316
325
initialized = false ;
317
326
}
318
327
}
319
328
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
+ }
320
392
321
393
/// <summary>
322
394
/// AcquireLock Method
0 commit comments