Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9ae91ba

Browse files
author
Benoit Hudson
committed
UNI-63112: implement platform-aware native code for tp_traverse et al
1. Implement tp_traverse, tp_clear, and tp_is_gc in native code in memory that is *not* released upon domain reload. Effect: fixes the crash on domain reload. Side effect: leaks a page every domain reload. 2. Use python's platform package to determine what we're running on, so we can use the right mmap/mprotect or VirtualAlloc/VirtualProtect routines, the right flags for mmap, and (theoretically) the right native code. It should be easy to add another system, as long as it has the same kinds of functionality. 3. Enable the domain reload test. Added some unit tests for the new stuff. 4. Remove tp_traverse, tp_clear, and tp_is_gc where it was implemented. Note: I haven't actually tested on windows and linux yet, I'm checking in so I can do that easily.
1 parent d016b24 commit 9ae91ba

File tree

8 files changed

+417
-46
lines changed

8 files changed

+417
-46
lines changed

src/embed_tests/Python.EmbeddingTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
<Compile Include="TestPyWith.cs" />
105105
<Compile Include="TestRuntime.cs" />
106106
<Compile Include="TestPyScope.cs" />
107+
<Compile Include="TestTypeManager.cs" />
107108
</ItemGroup>
108109
<ItemGroup>
109110
<ProjectReference Include="..\runtime\Python.Runtime.csproj">

src/embed_tests/TestDomainReload.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ class TestDomainReload
4040
/// a hot reload).
4141
/// </summary>
4242
[Test]
43-
[Ignore("Test crashes")]
4443
public static void DomainReloadAndGC()
4544
{
4645
// We're set up to run in the directory that includes the bin directory.

src/embed_tests/TestRuntime.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,25 @@ namespace Python.EmbeddingTest
66
{
77
public class TestRuntime
88
{
9+
/// <summary>
10+
/// Test the cache of the information from the platform module.
11+
///
12+
/// Test fails on platforms we haven't implemented yet.
13+
/// </summary>
14+
[Test]
15+
public static void PlatformCache()
16+
{
17+
Runtime.Runtime.Initialize();
18+
19+
Assert.That(Runtime.Runtime.Machine, Is.Not.EqualTo(Runtime.Runtime.MachineType.Other));
20+
Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.MachineName));
21+
22+
Assert.That(Runtime.Runtime.OperatingSystem, Is.Not.EqualTo(Runtime.Runtime.OperatingSystemType.Other));
23+
Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.OperatingSystemName));
24+
25+
Runtime.Runtime.Shutdown();
26+
}
27+
928
[Test]
1029
public static void Py_IsInitializedValue()
1130
{

src/embed_tests/TestTypeManager.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using NUnit.Framework;
2+
using Python.Runtime;
3+
using System.Runtime.InteropServices;
4+
5+
namespace Python.EmbeddingTest
6+
{
7+
class TestTypeManager
8+
{
9+
[Test]
10+
public static void TestNativeCode()
11+
{
12+
Runtime.Runtime.Initialize();
13+
14+
Assert.That(() => { var _ = TypeManager.NativeCode.Active; }, Throws.Nothing);
15+
Assert.That(TypeManager.NativeCode.Active.Code.Length, Is.GreaterThan(0));
16+
17+
Runtime.Runtime.Shutdown();
18+
}
19+
20+
[Test]
21+
public static void TestMemoryMapping()
22+
{
23+
Runtime.Runtime.Initialize();
24+
25+
Assert.That(() => { var _ = TypeManager.CreateMemoryMapper(); }, Throws.Nothing);
26+
var mapper = TypeManager.CreateMemoryMapper();
27+
28+
// Allocate a read-write page.
29+
int len = 12;
30+
var page = mapper.MapWriteable(len);
31+
Assert.That(() => { Marshal.WriteInt64(page, 17); }, Throws.Nothing);
32+
Assert.That(Marshal.ReadInt64(page), Is.EqualTo(17));
33+
34+
// Mark it read-execute, now we can't write anymore (I'm not testing we can execute).
35+
// We should be getting AccessViolationException, but Mono translates
36+
// SIGSEGV to NullReferenceException instead, so just check for some exception.
37+
mapper.SetReadExec(page, len);
38+
Assert.That(Marshal.ReadInt64(page), Is.EqualTo(17));
39+
Assert.That(() => { Marshal.WriteInt64(page, 18); }, Throws.Exception);
40+
Assert.That(Marshal.ReadInt64(page), Is.EqualTo(17));
41+
42+
Runtime.Runtime.Shutdown();
43+
}
44+
}
45+
}

src/runtime/classbase.cs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -247,24 +247,6 @@ public static IntPtr tp_str(IntPtr ob)
247247
}
248248

249249

250-
/// <summary>
251-
/// Default implementations for required Python GC support.
252-
/// </summary>
253-
public static int tp_traverse(IntPtr ob, IntPtr func, IntPtr args)
254-
{
255-
return 0;
256-
}
257-
258-
public static int tp_clear(IntPtr ob)
259-
{
260-
return 0;
261-
}
262-
263-
public static int tp_is_gc(IntPtr type)
264-
{
265-
return 1;
266-
}
267-
268250
/// <summary>
269251
/// Standard dealloc implementation for instances of reflected types.
270252
/// </summary>

src/runtime/extensiontype.cs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -81,27 +81,6 @@ public static int tp_descr_set(IntPtr ds, IntPtr ob, IntPtr val)
8181
}
8282

8383

84-
/// <summary>
85-
/// Required Python GC support.
86-
/// </summary>
87-
public static int tp_traverse(IntPtr ob, IntPtr func, IntPtr args)
88-
{
89-
return 0;
90-
}
91-
92-
93-
public static int tp_clear(IntPtr ob)
94-
{
95-
return 0;
96-
}
97-
98-
99-
public static int tp_is_gc(IntPtr type)
100-
{
101-
return 1;
102-
}
103-
104-
10584
/// <summary>
10685
/// Default dealloc implementation.
10786
/// </summary>

src/runtime/runtime.cs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Runtime.InteropServices;
33
using System.Security;
44
using System.Text;
5+
using System.Collections.Generic;
56

67
namespace Python.Runtime
78
{
@@ -195,6 +196,57 @@ public class Runtime
195196
// .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
196197
internal static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
197198

199+
/// <summary>
200+
/// Operating system type as reported by Python.
201+
/// </summary>
202+
public enum OperatingSystemType
203+
{
204+
Windows,
205+
Darwin,
206+
Linux,
207+
Other
208+
}
209+
210+
static readonly Dictionary<string, OperatingSystemType> OperatingSystemTypeMapping = new Dictionary<string, OperatingSystemType>()
211+
{
212+
{ "Windows", OperatingSystemType.Windows },
213+
{ "Darwin", OperatingSystemType.Darwin },
214+
{ "Linux", OperatingSystemType.Linux },
215+
};
216+
217+
/// <summary>
218+
/// Gets the operating system as reported by python's platform.system().
219+
/// </summary>
220+
public static OperatingSystemType OperatingSystem { get; private set; }
221+
222+
/// <summary>
223+
/// Gets the operating system as reported by python's platform.system().
224+
/// </summary>
225+
public static string OperatingSystemName { get; private set; }
226+
227+
public enum MachineType
228+
{
229+
i386,
230+
x86_64,
231+
Other
232+
};
233+
234+
static readonly Dictionary<string, MachineType> MachineTypeMapping = new Dictionary<string, MachineType>()
235+
{
236+
{ "i386", MachineType.i386 },
237+
{ "x86_64", MachineType.x86_64 },
238+
};
239+
240+
/// <summary>
241+
/// Gets the machine architecture as reported by python's platform.machine().
242+
/// </summary>
243+
public static MachineType Machine { get; private set; }/* set in Initialize using python's platform.machine */
244+
245+
/// <summary>
246+
/// Gets the machine architecture as reported by python's platform.machine().
247+
/// </summary>
248+
public static string MachineName { get; private set; }
249+
198250
internal static bool IsPython2 = pyversionnumber < 30;
199251
internal static bool IsPython3 = pyversionnumber >= 30;
200252

@@ -331,6 +383,10 @@ internal static void Initialize()
331383
NativeMethods.FreeLibrary(dllLocal);
332384
}
333385
#endif
386+
// Initialize data about the platform we're running on. We need
387+
// this for the type manager and potentially other details. Must
388+
// happen after caching the python types, above.
389+
InitializePlatformData();
334390

335391
// Initialize modules that depend on the runtime class.
336392
AssemblyManager.Initialize();
@@ -348,6 +404,53 @@ internal static void Initialize()
348404
AssemblyManager.UpdatePath();
349405
}
350406

407+
/// <summary>
408+
/// Initializes the data about platforms.
409+
///
410+
/// This must be the last step when initializing the runtime:
411+
/// GetManagedString needs to have the cached values for types.
412+
/// But it must run before initializing anything outside the runtime
413+
/// because those rely on the platform data.
414+
/// </summary>
415+
private static void InitializePlatformData()
416+
{
417+
IntPtr op;
418+
IntPtr fn;
419+
IntPtr platformModule = PyImport_ImportModule("platform");
420+
IntPtr emptyTuple = PyTuple_New(0);
421+
422+
fn = PyObject_GetAttrString(platformModule, "system");
423+
op = PyObject_Call(fn, emptyTuple, IntPtr.Zero);
424+
OperatingSystemName = GetManagedString(op);
425+
XDecref(op);
426+
XDecref(fn);
427+
428+
fn = PyObject_GetAttrString(platformModule, "machine");
429+
op = PyObject_Call(fn, emptyTuple, IntPtr.Zero);
430+
MachineName = GetManagedString(op);
431+
XDecref(op);
432+
XDecref(fn);
433+
434+
XDecref(emptyTuple);
435+
XDecref(platformModule);
436+
437+
// Now convert the strings into enum values so we can do switch
438+
// statements rather than constant parsing.
439+
OperatingSystemType OSType;
440+
if (!OperatingSystemTypeMapping.TryGetValue(OperatingSystemName, out OSType))
441+
{
442+
OSType = OperatingSystemType.Other;
443+
}
444+
OperatingSystem = OSType;
445+
446+
MachineType MType;
447+
if (!MachineTypeMapping.TryGetValue(MachineName, out MType))
448+
{
449+
MType = MachineType.Other;
450+
}
451+
Machine = MType;
452+
}
453+
351454
internal static void Shutdown()
352455
{
353456
AssemblyManager.Shutdown();

0 commit comments

Comments
 (0)