Skip to content

Commit 5fd77b1

Browse files
authored
Add PythonEngine.Interrupt (#1337)
Also added GetPythonThreadID
1 parent 0f33f71 commit 5fd77b1

File tree

5 files changed

+97
-2
lines changed

5 files changed

+97
-2
lines changed

AUTHORS.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@
6666
- Ville M. Vainio ([@vivainio](https://github.com/vivainio))
6767
- Virgil Dupras ([@hsoft](https://github.com/hsoft))
6868
- Wenguang Yang ([@yagweb](https://github.com/yagweb))
69-
- William Sardar ([@williamsardar])(https://github.com/williamsardar)
69+
- William Sardar ([@williamsardar](https://github.com/williamsardar))
7070
- Xavier Dupré ([@sdpython](https://github.com/sdpython))
7171
- Zane Purvis ([@zanedp](https://github.com/zanedp))
72-
- ([@amos402]https://github.com/amos402)
72+
- ([@amos402](https://github.com/amos402))
7373
- ([@bltribble](https://github.com/bltribble))
7474
- ([@civilx64](https://github.com/civilx64))
7575
- ([@GSPP](https://github.com/GSPP))
@@ -82,3 +82,4 @@
8282
- ([@testrunner123](https://github.com/testrunner123))
8383
- ([@DanBarzilian](https://github.com/DanBarzilian))
8484
- ([@alxnull](https://github.com/alxnull))
85+
- ([@gpetrou](https://github.com/gpetrou))

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1111

1212
- Ability to instantiate new .NET arrays using `Array[T](dim1, dim2, ...)` syntax
1313
- Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]).
14+
- Add GetPythonThreadID and Interrupt methods in PythonEngine
1415

1516
### Changed
1617
- Drop support for Python 2, 3.4, and 3.5

src/embed_tests/TestInterrupt.cs

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
using System;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
6+
using NUnit.Framework;
7+
8+
using Python.Runtime;
9+
10+
namespace Python.EmbeddingTest
11+
{
12+
public class TestInterrupt
13+
{
14+
private IntPtr _threadState;
15+
16+
[OneTimeSetUp]
17+
public void SetUp()
18+
{
19+
PythonEngine.Initialize();
20+
_threadState = PythonEngine.BeginAllowThreads();
21+
}
22+
23+
[OneTimeTearDown]
24+
public void Dispose()
25+
{
26+
PythonEngine.EndAllowThreads(_threadState);
27+
PythonEngine.Shutdown();
28+
}
29+
30+
[Test]
31+
public void InterruptTest()
32+
{
33+
int runSimpleStringReturnValue = int.MinValue;
34+
ulong pythonThreadID = ulong.MinValue;
35+
Task.Factory.StartNew(() =>
36+
{
37+
using (Py.GIL())
38+
{
39+
pythonThreadID = PythonEngine.GetPythonThreadID();
40+
runSimpleStringReturnValue = PythonEngine.RunSimpleString(@"
41+
import time
42+
43+
while True:
44+
time.sleep(0.2)");
45+
}
46+
});
47+
48+
Thread.Sleep(200);
49+
50+
Assert.AreNotEqual(ulong.MinValue, pythonThreadID);
51+
52+
using (Py.GIL())
53+
{
54+
int interruptReturnValue = PythonEngine.Interrupt(pythonThreadID);
55+
Assert.AreEqual(1, interruptReturnValue);
56+
}
57+
58+
Thread.Sleep(300);
59+
60+
Assert.AreEqual(-1, runSimpleStringReturnValue);
61+
}
62+
}
63+
}

src/runtime/pythonengine.cs

+24
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,30 @@ public static void Exec(string code, IntPtr? globals = null, IntPtr? locals = nu
567567
}
568568
}
569569

570+
/// <summary>
571+
/// Gets the Python thread ID.
572+
/// </summary>
573+
/// <returns>The Python thread ID.</returns>
574+
public static ulong GetPythonThreadID()
575+
{
576+
dynamic threading = Py.Import("threading");
577+
return threading.InvokeMethod("get_ident");
578+
}
579+
580+
/// <summary>
581+
/// Interrupts the execution of a thread.
582+
/// </summary>
583+
/// <param name="pythonThreadID">The Python thread ID.</param>
584+
/// <returns>The number of thread states modified; this is normally one, but will be zero if the thread id isn’t found.</returns>
585+
public static int Interrupt(ulong pythonThreadID)
586+
{
587+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
588+
{
589+
return Runtime.PyThreadState_SetAsyncExcLLP64((uint)pythonThreadID, Exceptions.KeyboardInterrupt);
590+
}
591+
592+
return Runtime.PyThreadState_SetAsyncExcLP64(pythonThreadID, Exceptions.KeyboardInterrupt);
593+
}
570594

571595
/// <summary>
572596
/// RunString Method. Function has been deprecated and will be removed.

src/runtime/runtime.cs

+6
Original file line numberDiff line numberDiff line change
@@ -2143,6 +2143,12 @@ internal static void Py_CLEAR(ref IntPtr ob)
21432143
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
21442144
internal static extern int Py_AddPendingCall(IntPtr func, IntPtr arg);
21452145

2146+
[DllImport(_PythonDll, EntryPoint = "PyThreadState_SetAsyncExc", CallingConvention = CallingConvention.Cdecl)]
2147+
internal static extern int PyThreadState_SetAsyncExcLLP64(uint id, IntPtr exc);
2148+
2149+
[DllImport(_PythonDll, EntryPoint = "PyThreadState_SetAsyncExc", CallingConvention = CallingConvention.Cdecl)]
2150+
internal static extern int PyThreadState_SetAsyncExcLP64(ulong id, IntPtr exc);
2151+
21462152
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
21472153
internal static extern int Py_MakePendingCalls();
21482154

0 commit comments

Comments
 (0)