@@ -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