| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,9 +2,9 @@ | |||
| 2 | 2 | using Python.Runtime; | |
| 3 | 3 | using System; | |
| 4 | 4 | using System.Collections.Generic; | |
| 5 | - using System.ComponentModel; | ||
| 6 | 5 | using System.Diagnostics; | |
| 7 | 6 | using System.Linq; | |
| 7 | + using System.Runtime.CompilerServices; | ||
| 8 | 8 | using System.Threading; | |
| 9 | 9 | ||
| 10 | 10 | namespace Python.EmbeddingTest | |
@@ -28,26 +28,14 @@ public void TearDown() | |||
| 28 | 28 | PythonEngine.Shutdown(); | |
| 29 | 29 | } | |
| 30 | 30 | ||
| 31 | - private static bool FullGCCollect() | ||
| 31 | + private static void FullGCCollect() | ||
| 32 | 32 | { | |
| 33 | - GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); | ||
| 34 | - try | ||
| 35 | - { | ||
| 36 | - return GC.WaitForFullGCComplete() == GCNotificationStatus.Succeeded; | ||
| 37 | - } | ||
| 38 | - catch (NotImplementedException) | ||
| 39 | - { | ||
| 40 | - // Some clr runtime didn't implement GC.WaitForFullGCComplete yet. | ||
| 41 | - return false; | ||
| 42 | - } | ||
| 43 | - finally | ||
| 44 | - { | ||
| 45 | - GC.WaitForPendingFinalizers(); | ||
| 46 | - } | ||
| 33 | + GC.Collect(); | ||
| 34 | + GC.WaitForPendingFinalizers(); | ||
| 47 | 35 | } | |
| 48 | 36 | ||
| 49 | 37 | [Test] | |
| 50 | - [Ignore("Ignore temporarily")] | ||
| 38 | + [Obsolete("GC tests are not guaranteed")] | ||
| 51 | 39 | public void CollectBasicObject() | |
| 52 | 40 | { | |
| 53 | 41 | Assert.IsTrue(Finalizer.Instance.Enable); | |
@@ -104,18 +92,13 @@ public void CollectBasicObject() | |||
| 104 | 92 | } | |
| 105 | 93 | ||
| 106 | 94 | [Test] | |
| 107 | - [Ignore("Ignore temporarily")] | ||
| 95 | + [Obsolete("GC tests are not guaranteed")] | ||
| 108 | 96 | public void CollectOnShutdown() | |
| 109 | 97 | { | |
| 110 | 98 | IntPtr op = MakeAGarbage(out var shortWeak, out var longWeak); | |
| 111 | - int hash = shortWeak.Target.GetHashCode(); | ||
| 112 | - List<WeakReference> garbage; | ||
| 113 | - if (!FullGCCollect()) | ||
| 114 | - { | ||
| 115 | - Assert.IsTrue(WaitForCollected(op, hash, 10000)); | ||
| 116 | - } | ||
| 99 | + FullGCCollect(); | ||
| 117 | 100 | Assert.IsFalse(shortWeak.IsAlive); | |
| 118 | - garbage = Finalizer.Instance.GetCollectedObjects(); | ||
| 101 | + List<WeakReference> garbage = Finalizer.Instance.GetCollectedObjects(); | ||
| 119 | 102 | Assert.IsNotEmpty(garbage, "The garbage object should be collected"); | |
| 120 | 103 | Assert.IsTrue(garbage.Any(r => ReferenceEquals(r.Target, longWeak.Target)), | |
| 121 | 104 | "Garbage should contains the collected object"); | |
@@ -125,12 +108,29 @@ public void CollectOnShutdown() | |||
| 125 | 108 | Assert.IsEmpty(garbage); | |
| 126 | 109 | } | |
| 127 | 110 | ||
| 111 | + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to obj | ||
| 112 | + [Obsolete("GC tests are not guaranteed")] | ||
| 128 | 113 | private static IntPtr MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak) | |
| 129 | 114 | { | |
| 130 | - PyLong obj = new PyLong(1024); | ||
| 131 | - shortWeak = new WeakReference(obj); | ||
| 132 | - longWeak = new WeakReference(obj, true); | ||
| 133 | - return obj.Handle; | ||
| 115 | + IntPtr handle = IntPtr.Zero; | ||
| 116 | + WeakReference @short = null, @long = null; | ||
| 117 | + // must create Python object in the thread where we have GIL | ||
| 118 | + IntPtr val = PyLong.FromLong(1024); | ||
| 119 | + // must create temp object in a different thread to ensure it is not present | ||
| 120 | + // when conservatively scanning stack for GC roots. | ||
| 121 | + // see https://xamarin.github.io/bugzilla-archives/17/17593/bug.html | ||
| 122 | + var garbageGen = new Thread(() => | ||
| 123 | + { | ||
| 124 | + var obj = new PyObject(val, skipCollect: true); | ||
| 125 | + @short = new WeakReference(obj); | ||
| 126 | + @long = new WeakReference(obj, true); | ||
| 127 | + handle = obj.Handle; | ||
| 128 | + }); | ||
| 129 | + garbageGen.Start(); | ||
| 130 | + Assert.IsTrue(garbageGen.Join(TimeSpan.FromSeconds(5)), "Garbage creation timed out"); | ||
| 131 | + shortWeak = @short; | ||
| 132 | + longWeak = @long; | ||
| 133 | + return handle; | ||
| 134 | 134 | } | |
| 135 | 135 | ||
| 136 | 136 | private static long CompareWithFinalizerOn(PyObject pyCollect, bool enbale) | |
@@ -211,6 +211,7 @@ internal static void CreateMyPyObject(IntPtr op) | |||
| 211 | 211 | } | |
| 212 | 212 | ||
| 213 | 213 | [Test] | |
| 214 | + [Obsolete("GC tests are not guaranteed")] | ||
| 214 | 215 | public void ErrorHandling() | |
| 215 | 216 | { | |
| 216 | 217 | bool called = false; | |
@@ -228,7 +229,7 @@ public void ErrorHandling() | |||
| 228 | 229 | WeakReference longWeak; | |
| 229 | 230 | { | |
| 230 | 231 | MakeAGarbage(out shortWeak, out longWeak); | |
| 231 | - var obj = (PyLong)longWeak.Target; | ||
| 232 | + var obj = (PyObject)longWeak.Target; | ||
| 232 | 233 | IntPtr handle = obj.Handle; | |
| 233 | 234 | shortWeak = null; | |
| 234 | 235 | longWeak = null; | |
@@ -279,36 +280,13 @@ public void ValidateRefCount() | |||
| 279 | 280 | } | |
| 280 | 281 | } | |
| 281 | 282 | ||
| 283 | + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to s1 and s2 | ||
| 282 | 284 | private static IntPtr CreateStringGarbage() | |
| 283 | 285 | { | |
| 284 | 286 | PyString s1 = new PyString("test_string"); | |
| 285 | 287 | // s2 steal a reference from s1 | |
| 286 | 288 | PyString s2 = new PyString(s1.Handle); | |
| 287 | 289 | return s1.Handle; | |
| 288 | 290 | } | |
| 289 | - | ||
| 290 | - private static bool WaitForCollected(IntPtr op, int hash, int milliseconds) | ||
| 291 | - { | ||
| 292 | - var stopwatch = Stopwatch.StartNew(); | ||
| 293 | - do | ||
| 294 | - { | ||
| 295 | - var garbage = Finalizer.Instance.GetCollectedObjects(); | ||
| 296 | - foreach (var item in garbage) | ||
| 297 | - { | ||
| 298 | - // The validation is not 100% precise, | ||
| 299 | - // but it's rare that two conditions satisfied but they're still not the same object. | ||
| 300 | - if (item.Target.GetHashCode() != hash) | ||
| 301 | - { | ||
| 302 | - continue; | ||
| 303 | - } | ||
| 304 | - var obj = (IPyDisposable)item.Target; | ||
| 305 | - if (obj.GetTrackedHandles().Contains(op)) | ||
| 306 | - { | ||
| 307 | - return true; | ||
| 308 | - } | ||
| 309 | - } | ||
| 310 | - } while (stopwatch.ElapsedMilliseconds < milliseconds); | ||
| 311 | - return false; | ||
| 312 | - } | ||
| 313 | 291 | } | |
| 314 | 292 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -74,7 +74,7 @@ public PyLong(uint value) : base(FromInt((int)value)) | |||
| 74 | 74 | } | |
| 75 | 75 | ||
| 76 | 76 | ||
| 77 | - private static IntPtr FromLong(long value) | ||
| 77 | + internal static IntPtr FromLong(long value) | ||
| 78 | 78 | { | |
| 79 | 79 | IntPtr val = Runtime.PyLong_FromLongLong(value); | |
| 80 | 80 | PythonException.ThrowIfIsNull(val); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -54,6 +54,19 @@ public PyObject(IntPtr ptr) | |||
| 54 | 54 | #endif | |
| 55 | 55 | } | |
| 56 | 56 | ||
| 57 | + [Obsolete("for testing purposes only")] | ||
| 58 | + internal PyObject(IntPtr ptr, bool skipCollect) | ||
| 59 | + { | ||
| 60 | + if (ptr == IntPtr.Zero) throw new ArgumentNullException(nameof(ptr)); | ||
| 61 | + | ||
| 62 | + obj = ptr; | ||
| 63 | + if (!skipCollect) | ||
| 64 | + Finalizer.Instance.ThrottledCollect(); | ||
| 65 | + #if TRACE_ALLOC | ||
| 66 | + Traceback = new StackTrace(1); | ||
| 67 | + #endif | ||
| 68 | + } | ||
| 69 | + | ||
| 57 | 70 | /// <summary> | |
| 58 | 71 | /// Creates new <see cref="PyObject"/> pointing to the same object as | |
| 59 | 72 | /// the <paramref name="reference"/>. Increments refcount, allowing <see cref="PyObject"/> | |
| Back | FazBrowse Home | New Git URL |
0 commit comments