| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 6034640 commit 33db56d
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -36,16 +36,18 @@ public void CollectBasicObject() | |||
| 36 | 36 | { | |
| 37 | 37 | Assert.IsTrue(Finalizer.Instance.Enable); | |
| 38 | 38 | ||
| 39 | - int thId = Thread.CurrentThread.ManagedThreadId; | ||
| 40 | 39 | Finalizer.Instance.Threshold = 1; | |
| 41 | 40 | bool called = false; | |
| 41 | + var objectCount = 0; | ||
| 42 | 42 | EventHandler<Finalizer.CollectArgs> handler = (s, e) => | |
| 43 | 43 | { | |
| 44 | - Assert.AreEqual(thId, Thread.CurrentThread.ManagedThreadId); | ||
| 45 | - Assert.GreaterOrEqual(e.ObjectCount, 1); | ||
| 44 | + objectCount = e.ObjectCount; | ||
| 46 | 45 | called = true; | |
| 47 | 46 | }; | |
| 48 | 47 | ||
| 48 | + Assert.IsFalse(called); | ||
| 49 | + Finalizer.Instance.CollectOnce += handler; | ||
| 50 | + | ||
| 49 | 51 | WeakReference shortWeak; | |
| 50 | 52 | WeakReference longWeak; | |
| 51 | 53 | { | |
@@ -61,18 +63,16 @@ public void CollectBasicObject() | |||
| 61 | 63 | Assert.NotZero(garbage.Count); | |
| 62 | 64 | Assert.IsTrue(garbage.Any(T => ReferenceEquals(T.Target, longWeak.Target))); | |
| 63 | 65 | } | |
| 64 | - | ||
| 65 | - Assert.IsFalse(called); | ||
| 66 | - Finalizer.Instance.CollectOnce += handler; | ||
| 67 | 66 | try | |
| 68 | 67 | { | |
| 69 | - Finalizer.Instance.CallPendingFinalizers(); | ||
| 68 | + Finalizer.Instance.Collect(forceDispose: false); | ||
| 70 | 69 | } | |
| 71 | 70 | finally | |
| 72 | 71 | { | |
| 73 | 72 | Finalizer.Instance.CollectOnce -= handler; | |
| 74 | 73 | } | |
| 75 | 74 | Assert.IsTrue(called); | |
| 75 | + Assert.GreaterOrEqual(objectCount, 1); | ||
| 76 | 76 | } | |
| 77 | 77 | ||
| 78 | 78 | private static void MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak) | |
@@ -85,7 +85,7 @@ private static void MakeAGarbage(out WeakReference shortWeak, out WeakReference | |||
| 85 | 85 | ||
| 86 | 86 | private static long CompareWithFinalizerOn(PyObject pyCollect, bool enbale) | |
| 87 | 87 | { | |
| 88 | - // Must larger than 512 bytes make sure Python use | ||
| 88 | + // Must larger than 512 bytes make sure Python use | ||
| 89 | 89 | string str = new string('1', 1024); | |
| 90 | 90 | Finalizer.Instance.Enable = true; | |
| 91 | 91 | FullGCCollect(); | |
@@ -164,10 +164,11 @@ internal static void CreateMyPyObject(IntPtr op) | |||
| 164 | 164 | public void ErrorHandling() | |
| 165 | 165 | { | |
| 166 | 166 | bool called = false; | |
| 167 | + var errorMessage = ""; | ||
| 167 | 168 | EventHandler<Finalizer.ErrorArgs> handleFunc = (sender, args) => | |
| 168 | 169 | { | |
| 169 | 170 | called = true; | |
| 170 | - Assert.AreEqual(args.Error.Message, "MyPyObject"); | ||
| 171 | + errorMessage = args.Error.Message; | ||
| 171 | 172 | }; | |
| 172 | 173 | Finalizer.Instance.Threshold = 1; | |
| 173 | 174 | Finalizer.Instance.ErrorHandler += handleFunc; | |
@@ -193,6 +194,7 @@ public void ErrorHandling() | |||
| 193 | 194 | { | |
| 194 | 195 | Finalizer.Instance.ErrorHandler -= handleFunc; | |
| 195 | 196 | } | |
| 197 | + Assert.AreEqual(errorMessage, "MyPyObject"); | ||
| 196 | 198 | } | |
| 197 | 199 | ||
| 198 | 200 | [Test] | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,10 +1,9 @@ | |||
| 1 | 1 | using System; | |
| 2 | 2 | using System.Collections.Concurrent; | |
| 3 | 3 | using System.Collections.Generic; | |
| 4 | - using System.Diagnostics; | ||
| 5 | 4 | using System.Linq; | |
| 6 | - using System.Runtime.InteropServices; | ||
| 7 | 5 | using System.Threading; | |
| 6 | + using System.Threading.Tasks; | ||
| 8 | 7 | ||
| 9 | 8 | namespace Python.Runtime | |
| 10 | 9 | { | |
@@ -28,20 +27,10 @@ public class ErrorArgs : EventArgs | |||
| 28 | 27 | public int Threshold { get; set; } | |
| 29 | 28 | public bool Enable { get; set; } | |
| 30 | 29 | ||
| 31 | - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] | ||
| 32 | - struct PendingArgs | ||
| 33 | - { | ||
| 34 | - public bool cancelled; | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] | ||
| 38 | - private delegate int PendingCall(IntPtr arg); | ||
| 39 | - private readonly PendingCall _collectAction; | ||
| 40 | - | ||
| 41 | 30 | private ConcurrentQueue<IPyDisposable> _objQueue = new ConcurrentQueue<IPyDisposable>(); | |
| 42 | 31 | private bool _pending = false; | |
| 43 | 32 | private readonly object _collectingLock = new object(); | |
| 44 | - private IntPtr _pendingArgs = IntPtr.Zero; | ||
| 33 | + private Task _finalizerTask; | ||
| 45 | 34 | ||
| 46 | 35 | #region FINALIZER_CHECK | |
| 47 | 36 | ||
@@ -84,23 +73,20 @@ private Finalizer() | |||
| 84 | 73 | { | |
| 85 | 74 | Enable = true; | |
| 86 | 75 | Threshold = 200; | |
| 87 | - _collectAction = OnPendingCollect; | ||
| 88 | 76 | } | |
| 89 | 77 | ||
| 90 | - public void CallPendingFinalizers() | ||
| 78 | + public void Collect(bool forceDispose = true) | ||
| 91 | 79 | { | |
| 92 | - if (Thread.CurrentThread.ManagedThreadId != Runtime.MainManagedThreadId) | ||
| 80 | + if (Instance._finalizerTask != null | ||
| 81 | + && !Instance._finalizerTask.IsCompleted) | ||
| 93 | 82 | { | |
| 94 | - throw new Exception("PendingCall should execute in main Python thread"); | ||
| 83 | + var ts = PythonEngine.BeginAllowThreads(); | ||
| 84 | + Instance._finalizerTask.Wait(); | ||
| 85 | + PythonEngine.EndAllowThreads(ts); | ||
| 95 | 86 | } | |
| 96 | - Runtime.Py_MakePendingCalls(); | ||
| 97 | - } | ||
| 98 | - | ||
| 99 | - public void Collect() | ||
| 100 | - { | ||
| 101 | - using (var gilState = new Py.GILState()) | ||
| 87 | + else if (forceDispose) | ||
| 102 | 88 | { | |
| 103 | - DisposeAll(); | ||
| 89 | + Instance.DisposeAll(); | ||
| 104 | 90 | } | |
| 105 | 91 | } | |
| 106 | 92 | ||
@@ -141,25 +127,7 @@ internal static void Shutdown() | |||
| 141 | 127 | Instance._objQueue = new ConcurrentQueue<IPyDisposable>(); | |
| 142 | 128 | return; | |
| 143 | 129 | } | |
| 144 | - Instance.DisposeAll(); | ||
| 145 | - if (Thread.CurrentThread.ManagedThreadId != Runtime.MainManagedThreadId) | ||
| 146 | - { | ||
| 147 | - if (Instance._pendingArgs == IntPtr.Zero) | ||
| 148 | - { | ||
| 149 | - Instance.ResetPending(); | ||
| 150 | - return; | ||
| 151 | - } | ||
| 152 | - // Not in main thread just cancel the pending operation to avoid error in different domain | ||
| 153 | - // It will make a memory leak | ||
| 154 | - unsafe | ||
| 155 | - { | ||
| 156 | - PendingArgs* args = (PendingArgs*)Instance._pendingArgs; | ||
| 157 | - args->cancelled = true; | ||
| 158 | - } | ||
| 159 | - Instance.ResetPending(); | ||
| 160 | - return; | ||
| 161 | - } | ||
| 162 | - Instance.CallPendingFinalizers(); | ||
| 130 | + Instance.Collect(forceDispose: true); | ||
| 163 | 131 | } | |
| 164 | 132 | ||
| 165 | 133 | private void AddPendingCollect() | |
@@ -171,16 +139,17 @@ private void AddPendingCollect() | |||
| 171 | 139 | if (!_pending) | |
| 172 | 140 | { | |
| 173 | 141 | _pending = true; | |
| 174 | - var args = new PendingArgs { cancelled = false }; | ||
| 175 | - _pendingArgs = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PendingArgs))); | ||
| 176 | - Marshal.StructureToPtr(args, _pendingArgs, false); | ||
| 177 | - IntPtr func = Marshal.GetFunctionPointerForDelegate(_collectAction); | ||
| 178 | - if (Runtime.Py_AddPendingCall(func, _pendingArgs) != 0) | ||
| 142 | + // should already be complete but just in case | ||
| 143 | + _finalizerTask?.Wait(); | ||
| 144 | + | ||
| 145 | + _finalizerTask = Task.Factory.StartNew(() => | ||
| 179 | 146 | { | |
| 180 | - // Full queue, append next time | ||
| 181 | - FreePendingArgs(); | ||
| 182 | - _pending = false; | ||
| 183 | - } | ||
| 147 | + using (Py.GIL()) | ||
| 148 | + { | ||
| 149 | + Instance.DisposeAll(); | ||
| 150 | + _pending = false; | ||
| 151 | + } | ||
| 152 | + }); | ||
| 184 | 153 | } | |
| 185 | 154 | } | |
| 186 | 155 | finally | |
@@ -190,29 +159,6 @@ private void AddPendingCollect() | |||
| 190 | 159 | } | |
| 191 | 160 | } | |
| 192 | 161 | ||
| 193 | - private static int OnPendingCollect(IntPtr arg) | ||
| 194 | - { | ||
| 195 | - Debug.Assert(arg == Instance._pendingArgs); | ||
| 196 | - try | ||
| 197 | - { | ||
| 198 | - unsafe | ||
| 199 | - { | ||
| 200 | - PendingArgs* pendingArgs = (PendingArgs*)arg; | ||
| 201 | - if (pendingArgs->cancelled) | ||
| 202 | - { | ||
| 203 | - return 0; | ||
| 204 | - } | ||
| 205 | - } | ||
| 206 | - Instance.DisposeAll(); | ||
| 207 | - } | ||
| 208 | - finally | ||
| 209 | - { | ||
| 210 | - Instance.FreePendingArgs(); | ||
| 211 | - Instance.ResetPending(); | ||
| 212 | - } | ||
| 213 | - return 0; | ||
| 214 | - } | ||
| 215 | - | ||
| 216 | 162 | private void DisposeAll() | |
| 217 | 163 | { | |
| 218 | 164 | CollectOnce?.Invoke(this, new CollectArgs() | |
@@ -246,23 +192,6 @@ private void DisposeAll() | |||
| 246 | 192 | } | |
| 247 | 193 | } | |
| 248 | 194 | ||
| 249 | - private void FreePendingArgs() | ||
| 250 | - { | ||
| 251 | - if (_pendingArgs != IntPtr.Zero) | ||
| 252 | - { | ||
| 253 | - Marshal.FreeHGlobal(_pendingArgs); | ||
| 254 | - _pendingArgs = IntPtr.Zero; | ||
| 255 | - } | ||
| 256 | - } | ||
| 257 | - | ||
| 258 | - private void ResetPending() | ||
| 259 | - { | ||
| 260 | - lock (_collectingLock) | ||
| 261 | - { | ||
| 262 | - _pending = false; | ||
| 263 | - } | ||
| 264 | - } | ||
| 265 | - | ||
| 266 | 195 | #if FINALIZER_CHECK | |
| 267 | 196 | private void ValidateRefCount() | |
| 268 | 197 | { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments