| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,9 @@ | |||
| 1 | 1 | using NUnit.Framework; | |
| 2 | 2 | using Python.Runtime; | |
| 3 | 3 | using System; | |
| 4 | + using System.Collections.Generic; | ||
| 5 | + using System.ComponentModel; | ||
| 6 | + using System.Diagnostics; | ||
| 4 | 7 | using System.Linq; | |
| 5 | 8 | using System.Threading; | |
| 6 | 9 | ||
@@ -25,18 +28,22 @@ public void TearDown() | |||
| 25 | 28 | PythonEngine.Shutdown(); | |
| 26 | 29 | } | |
| 27 | 30 | ||
| 28 | - private static void FullGCCollect() | ||
| 31 | + private static bool FullGCCollect() | ||
| 29 | 32 | { | |
| 30 | 33 | GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); | |
| 31 | 34 | try | |
| 32 | 35 | { | |
| 33 | - GC.WaitForFullGCComplete(); | ||
| 36 | + return GC.WaitForFullGCComplete() == GCNotificationStatus.Succeeded; | ||
| 34 | 37 | } | |
| 35 | 38 | catch (NotImplementedException) | |
| 36 | 39 | { | |
| 37 | 40 | // Some clr runtime didn't implement GC.WaitForFullGCComplete yet. | |
| 41 | + return false; | ||
| 42 | + } | ||
| 43 | + finally | ||
| 44 | + { | ||
| 45 | + GC.WaitForPendingFinalizers(); | ||
| 38 | 46 | } | |
| 39 | - GC.WaitForPendingFinalizers(); | ||
| 40 | 47 | } | |
| 41 | 48 | ||
| 42 | 49 | [Test] | |
@@ -96,23 +103,33 @@ public void CollectBasicObject() | |||
| 96 | 103 | } | |
| 97 | 104 | ||
| 98 | 105 | [Test] | |
| 106 | + [Ignore("Ignore temporarily")] | ||
| 99 | 107 | public void CollectOnShutdown() | |
| 100 | 108 | { | |
| 101 | - MakeAGarbage(out var shortWeak, out var longWeak); | ||
| 102 | - FullGCCollect(); | ||
| 103 | - var garbage = Finalizer.Instance.GetCollectedObjects(); | ||
| 104 | - Assert.IsNotEmpty(garbage); | ||
| 109 | + IntPtr op = MakeAGarbage(out var shortWeak, out var longWeak); | ||
| 110 | + int hash = shortWeak.Target.GetHashCode(); | ||
| 111 | + List<WeakReference> garbage; | ||
| 112 | + if (!FullGCCollect()) | ||
| 113 | + { | ||
| 114 | + Assert.IsTrue(WaitForCollected(op, hash, 10000)); | ||
| 115 | + } | ||
| 116 | + Assert.IsFalse(shortWeak.IsAlive); | ||
| 117 | + garbage = Finalizer.Instance.GetCollectedObjects(); | ||
| 118 | + Assert.IsNotEmpty(garbage, "The garbage object should be collected"); | ||
| 119 | + Assert.IsTrue(garbage.Any(r => ReferenceEquals(r.Target, longWeak.Target)), | ||
| 120 | + "Garbage should contains the collected object"); | ||
| 121 | + | ||
| 105 | 122 | PythonEngine.Shutdown(); | |
| 106 | 123 | garbage = Finalizer.Instance.GetCollectedObjects(); | |
| 107 | 124 | Assert.IsEmpty(garbage); | |
| 108 | 125 | } | |
| 109 | 126 | ||
| 110 | - private static void MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak) | ||
| 127 | + private static IntPtr MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak) | ||
| 111 | 128 | { | |
| 112 | 129 | PyLong obj = new PyLong(1024); | |
| 113 | 130 | shortWeak = new WeakReference(obj); | |
| 114 | 131 | longWeak = new WeakReference(obj, true); | |
| 115 | - obj = null; | ||
| 132 | + return obj.Handle; | ||
| 116 | 133 | } | |
| 117 | 134 | ||
| 118 | 135 | private static long CompareWithFinalizerOn(PyObject pyCollect, bool enbale) | |
@@ -269,5 +286,28 @@ private static IntPtr CreateStringGarbage() | |||
| 269 | 286 | return s1.Handle; | |
| 270 | 287 | } | |
| 271 | 288 | ||
| 289 | + private static bool WaitForCollected(IntPtr op, int hash, int milliseconds) | ||
| 290 | + { | ||
| 291 | + var stopwatch = Stopwatch.StartNew(); | ||
| 292 | + do | ||
| 293 | + { | ||
| 294 | + var garbage = Finalizer.Instance.GetCollectedObjects(); | ||
| 295 | + foreach (var item in garbage) | ||
| 296 | + { | ||
| 297 | + // The validation is not 100% precise, | ||
| 298 | + // but it's rare that two conditions satisfied but they're still not the same object. | ||
| 299 | + if (item.Target.GetHashCode() != hash) | ||
| 300 | + { | ||
| 301 | + continue; | ||
| 302 | + } | ||
| 303 | + var obj = (IPyDisposable)item.Target; | ||
| 304 | + if (obj.GetTrackedHandles().Contains(op)) | ||
| 305 | + { | ||
| 306 | + return true; | ||
| 307 | + } | ||
| 308 | + } | ||
| 309 | + } while (stopwatch.ElapsedMilliseconds < milliseconds); | ||
| 310 | + return false; | ||
| 311 | + } | ||
| 272 | 312 | } | |
| 273 | 313 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -155,9 +155,9 @@ public static void Initialize() | |||
| 155 | 155 | Initialize(setSysArgv: true); | |
| 156 | 156 | } | |
| 157 | 157 | ||
| 158 | - public static void Initialize(bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Default, bool fromPython = false) | ||
| 158 | + public static void Initialize(bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Default) | ||
| 159 | 159 | { | |
| 160 | - Initialize(Enumerable.Empty<string>(), setSysArgv: setSysArgv, initSigs: initSigs, mode, fromPython: fromPython); | ||
| 160 | + Initialize(Enumerable.Empty<string>(), setSysArgv: setSysArgv, initSigs: initSigs, mode); | ||
| 161 | 161 | } | |
| 162 | 162 | ||
| 163 | 163 | /// <summary> | |
@@ -170,7 +170,7 @@ public static void Initialize(bool setSysArgv = true, bool initSigs = false, Shu | |||
| 170 | 170 | /// interpreter lock (GIL) to call this method. | |
| 171 | 171 | /// initSigs can be set to 1 to do default python signal configuration. This will override the way signals are handled by the application. | |
| 172 | 172 | /// </remarks> | |
| 173 | - public static void Initialize(IEnumerable<string> args, bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Default, bool fromPython = false) | ||
| 173 | + public static void Initialize(IEnumerable<string> args, bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Default) | ||
| 174 | 174 | { | |
| 175 | 175 | if (initialized) | |
| 176 | 176 | { | |
@@ -182,7 +182,7 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true, | |||
| 182 | 182 | // during an initial "import clr", and the world ends shortly thereafter. | |
| 183 | 183 | // This is probably masking some bad mojo happening somewhere in Runtime.Initialize(). | |
| 184 | 184 | delegateManager = new DelegateManager(); | |
| 185 | - Runtime.Initialize(initSigs, mode, fromPython); | ||
| 185 | + Runtime.Initialize(initSigs, mode); | ||
| 186 | 186 | initialized = true; | |
| 187 | 187 | Exceptions.Clear(); | |
| 188 | 188 | ||
@@ -234,7 +234,8 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true, | |||
| 234 | 234 | // add the imported module to the clr module, and copy the API functions | |
| 235 | 235 | // and decorators into the main clr module. | |
| 236 | 236 | Runtime.PyDict_SetItemString(clr_dict, "_extras", module); | |
| 237 | - foreach (PyObject key in locals.Keys()) | ||
| 237 | + using (var keys = locals.Keys()) | ||
| 238 | + foreach (PyObject key in keys) | ||
| 238 | 239 | { | |
| 239 | 240 | if (!key.ToString().StartsWith("_") || key.ToString().Equals("__version__")) | |
| 240 | 241 | { | |
@@ -265,7 +266,7 @@ public static IntPtr InitExt() | |||
| 265 | 266 | { | |
| 266 | 267 | try | |
| 267 | 268 | { | |
| 268 | - Initialize(setSysArgv: false, fromPython: true); | ||
| 269 | + Initialize(setSysArgv: false); | ||
| 269 | 270 | ||
| 270 | 271 | // Trickery - when the import hook is installed into an already | |
| 271 | 272 | // running Python, the standard import machinery is still in | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -125,7 +125,7 @@ internal static Version PyVersion | |||
| 125 | 125 | /// </summary> | |
| 126 | 126 | /// <remarks>Always call this method from the Main thread. After the | |
| 127 | 127 | /// first call to this method, the main thread has acquired the GIL.</remarks> | |
| 128 | - internal static void Initialize(bool initSigs = false, ShutdownMode mode = ShutdownMode.Default, bool fromPython = false) | ||
| 128 | + internal static void Initialize(bool initSigs = false, ShutdownMode mode = ShutdownMode.Default) | ||
| 129 | 129 | { | |
| 130 | 130 | if (_isInitialized) | |
| 131 | 131 | { | |
@@ -139,7 +139,6 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd | |||
| 139 | 139 | } | |
| 140 | 140 | ShutdownMode = mode; | |
| 141 | 141 | ||
| 142 | - IntPtr state = IntPtr.Zero; | ||
| 143 | 142 | if (Py_IsInitialized() == 0) | |
| 144 | 143 | { | |
| 145 | 144 | Py_InitializeEx(initSigs ? 1 : 0); | |
@@ -154,12 +153,12 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd | |||
| 154 | 153 | RuntimeState.Save(); | |
| 155 | 154 | } | |
| 156 | 155 | } | |
| 157 | - else if (!fromPython) | ||
| 156 | + else | ||
| 158 | 157 | { | |
| 159 | 158 | // If we're coming back from a domain reload or a soft shutdown, | |
| 160 | 159 | // we have previously released the thread state. Restore the main | |
| 161 | 160 | // thread state here. | |
| 162 | - PyEval_RestoreThread(PyGILState_GetThisThreadState()); | ||
| 161 | + PyGILState_Ensure(); | ||
| 163 | 162 | } | |
| 164 | 163 | MainManagedThreadId = Thread.CurrentThread.ManagedThreadId; | |
| 165 | 164 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments