From bcd8415644bd7a0b1d9878092588ae46995b9564 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Tue, 21 Sep 2021 22:01:20 -0700 Subject: [PATCH] added a workaround for warning in threading module after TestInterrupt avoid using dynamic in GetPythonThreadID --- src/embed_tests/TestInterrupt.cs | 58 +++++++++++++++++++++++++------- src/runtime/pythonengine.cs | 5 +-- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/embed_tests/TestInterrupt.cs b/src/embed_tests/TestInterrupt.cs index a40407782..e344a8ccb 100644 --- a/src/embed_tests/TestInterrupt.cs +++ b/src/embed_tests/TestInterrupt.cs @@ -11,22 +11,51 @@ namespace Python.EmbeddingTest { public class TestInterrupt { - private IntPtr _threadState; - + PyObject threading; [OneTimeSetUp] public void SetUp() { PythonEngine.Initialize(); - _threadState = PythonEngine.BeginAllowThreads(); + // workaround for assert tlock.locked() warning + threading = Py.Import("threading"); } [OneTimeTearDown] public void Dispose() { - PythonEngine.EndAllowThreads(_threadState); + threading.Dispose(); PythonEngine.Shutdown(); } + [Test] + public void PythonThreadIDStable() + { + long pythonThreadID = 0; + long pythonThreadID2 = 0; + var asyncCall = Task.Factory.StartNew(() => + { + using (Py.GIL()) + { + Interlocked.Exchange(ref pythonThreadID, (long)PythonEngine.GetPythonThreadID()); + Interlocked.Exchange(ref pythonThreadID2, (long)PythonEngine.GetPythonThreadID()); + } + }); + + var timeout = Stopwatch.StartNew(); + + IntPtr threadState = PythonEngine.BeginAllowThreads(); + while (Interlocked.Read(ref pythonThreadID) == 0 || Interlocked.Read(ref pythonThreadID2) == 0) + { + Assert.Less(timeout.Elapsed, TimeSpan.FromSeconds(5), "thread IDs were not assigned in time"); + } + PythonEngine.EndAllowThreads(threadState); + + Assert.IsTrue(asyncCall.Wait(TimeSpan.FromSeconds(5)), "Async thread has not finished in time"); + + Assert.AreEqual(pythonThreadID, pythonThreadID2); + Assert.NotZero(pythonThreadID); + } + [Test] public void InterruptTest() { @@ -39,26 +68,31 @@ public void InterruptTest() return PythonEngine.RunSimpleString(@" import time -while True: - time.sleep(0.2)"); +try: + while True: + time.sleep(0.2) +except KeyboardInterrupt: + pass"); } }); var timeout = Stopwatch.StartNew(); + + IntPtr threadState = PythonEngine.BeginAllowThreads(); while (Interlocked.Read(ref pythonThreadID) == 0) { Assert.Less(timeout.Elapsed, TimeSpan.FromSeconds(5), "thread ID was not assigned in time"); } + PythonEngine.EndAllowThreads(threadState); - using (Py.GIL()) - { - int interruptReturnValue = PythonEngine.Interrupt((ulong)Interlocked.Read(ref pythonThreadID)); - Assert.AreEqual(1, interruptReturnValue); - } + int interruptReturnValue = PythonEngine.Interrupt((ulong)Interlocked.Read(ref pythonThreadID)); + Assert.AreEqual(1, interruptReturnValue); + threadState = PythonEngine.BeginAllowThreads(); Assert.IsTrue(asyncCall.Wait(TimeSpan.FromSeconds(5)), "Async thread was not interrupted in time"); + PythonEngine.EndAllowThreads(threadState); - Assert.AreEqual(-1, asyncCall.Result); + Assert.AreEqual(0, asyncCall.Result); } } } diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 4b72dabd7..cd608fe93 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -570,8 +570,9 @@ internal static void Exec(string code, BorrowedReference globals, BorrowedRefere /// The Python thread ID. public static ulong GetPythonThreadID() { - dynamic threading = Py.Import("threading"); - return threading.InvokeMethod("get_ident"); + using PyObject threading = Py.Import("threading"); + using PyObject id = threading.InvokeMethod("get_ident"); + return id.As(); } ///