Skip to content

Commit 807ab22

Browse files
authored
Merge pull request #7 from pythonnet/refactor-netfx
Refactor netfx
2 parents e512c7a + 367d708 commit 807ab22

File tree

4 files changed

+127
-35
lines changed

4 files changed

+127
-35
lines changed

clr_loader/ffi/netfx.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
typedef void* pyclr_domain;
66
typedef int (*entry_point)(void* buffer, int size);
77
8+
void pyclr_initialize();
89
void* pyclr_create_appdomain(const char* name, const char* config_file);
910
entry_point pyclr_get_function(pyclr_domain domain, const char* assembly_path, const char* class_name, const char* function);
1011
void pyclr_close_appdomain(pyclr_domain domain);
12+
void pyclr_finalize();
1113
"""
1214
]

clr_loader/netfx.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1+
import atexit
12
from .ffi import ffi, load_netfx
23

3-
44
_FW = None
55

66

77
class NetFx:
88
def __init__(self, name=None, config_file=None):
9-
global _FW
10-
if _FW is None:
11-
_FW = load_netfx()
12-
9+
initialize()
1310
self._domain = _FW.pyclr_create_appdomain(
1411
name or ffi.NULL, config_file or ffi.NULL
1512
)
@@ -27,3 +24,21 @@ def get_callable(self, assembly_path, typename, function):
2724
def __del__(self):
2825
if self._domain and _FW:
2926
_FW.pyclr_close_appdomain(self._domain)
27+
28+
29+
def initialize():
30+
global _FW
31+
if _FW is not None:
32+
return
33+
34+
_FW = load_netfx()
35+
_FW.pyclr_initialize()
36+
37+
atexit.register(_release)
38+
39+
40+
def _release():
41+
global _FW
42+
if _FW is not None:
43+
_FW.pyclr_finalize()
44+
_FW = None

netfx_loader/ClrLoader.cs

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
using System;
2-
using System.Diagnostics;
3-
using System.Globalization;
2+
using System.Collections.Generic;
43
using System.IO;
5-
using System.Reflection;
64
using System.Runtime.InteropServices;
75
using NXPorts.Attributes;
86

97
namespace ClrLoader
108
{
119
public static class ClrLoader
1210
{
13-
delegate int EntryPoint(IntPtr buffer, int size);
11+
static bool _initialized = false;
12+
static List<DomainData> _domains = new List<DomainData>();
13+
14+
[DllExport("pyclr_initialize", CallingConvention.Cdecl)]
15+
public static void Initialize()
16+
{
17+
if (!_initialized)
18+
{
19+
_domains.Add(new DomainData(AppDomain.CurrentDomain));
20+
_initialized = true;
21+
}
22+
}
1423

1524
[DllExport("pyclr_create_appdomain", CallingConvention.Cdecl)]
1625
public static IntPtr CreateAppDomain(
@@ -29,11 +38,9 @@ public static IntPtr CreateAppDomain(
2938

3039
Print($"Located domain {domain}");
3140

32-
var handle = GCHandle.Alloc(domain, GCHandleType.Pinned);
33-
34-
Print($"Created handle {handle}");
35-
36-
return handle.AddrOfPinnedObject();
41+
var domainData = new DomainData(domain);
42+
_domains.Add(domainData);
43+
return new IntPtr(_domains.Count - 1);
3744
}
3845
else
3946
{
@@ -51,18 +58,8 @@ public static IntPtr GetFunction(
5158
{
5259
try
5360
{
54-
var domainObj = AppDomain.CurrentDomain;
55-
if (domain != IntPtr.Zero)
56-
{
57-
var handle = GCHandle.FromIntPtr(domain);
58-
domainObj = (AppDomain)handle.Target;
59-
}
60-
61-
var assembly = domainObj.Load(AssemblyName.GetAssemblyName(assemblyPath));
62-
var type = assembly.GetType(typeName, throwOnError: true);
63-
Print($"Loaded type {type}");
64-
var deleg = Delegate.CreateDelegate(typeof(EntryPoint), type, function);
65-
61+
var domainData = _domains[(int)domain];
62+
var deleg = domainData.GetEntryPoint(assemblyPath, typeName, function);
6663
return Marshal.GetFunctionPointerForDelegate(deleg);
6764
}
6865
catch (Exception exc)
@@ -77,21 +74,38 @@ public static void CloseAppDomain(IntPtr domain)
7774
{
7875
if (domain != IntPtr.Zero)
7976
{
80-
var handle = GCHandle.FromIntPtr(domain);
81-
var domainObj = (AppDomain)handle.Target;
82-
AppDomain.Unload(domainObj);
83-
handle.Free();
77+
try
78+
{
79+
var domainData = _domains[(int)domain];
80+
domainData.Dispose();
81+
}
82+
catch (Exception exc)
83+
{
84+
Print($"Exception in {nameof(CloseAppDomain)}: {exc.GetType().Name} {exc.Message}\n{exc.StackTrace}");
85+
}
86+
}
87+
}
88+
89+
[DllExport("pyclr_finalize", CallingConvention.Cdecl)]
90+
public static void Close()
91+
{
92+
foreach (var domainData in _domains)
93+
{
94+
domainData.Dispose();
8495
}
96+
97+
_domains.Clear();
98+
_initialized = false;
8599
}
86100

87-
#if DEBUG
88-
static void Print(string s)
101+
#if DEBUG
102+
internal static void Print(string s)
89103
{
90104
Console.WriteLine(s);
91105
}
92-
#else
93-
static void Print(string s) {}
94-
#endif
106+
#else
107+
internal static void Print(string s) { }
108+
#endif
95109
}
96110

97111
}

netfx_loader/DomainData.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
5+
namespace ClrLoader
6+
{
7+
using static ClrLoader;
8+
9+
class DomainData : IDisposable
10+
{
11+
public delegate int EntryPoint(IntPtr buffer, int size);
12+
13+
bool _disposed = false;
14+
15+
public AppDomain Domain { get; }
16+
public Dictionary<(string, string, string), EntryPoint> _delegates;
17+
18+
public DomainData(AppDomain domain)
19+
{
20+
Domain = domain;
21+
_delegates = new Dictionary<(string, string, string), EntryPoint>();
22+
}
23+
24+
public EntryPoint GetEntryPoint(string assemblyPath, string typeName, string function)
25+
{
26+
if (_disposed)
27+
throw new InvalidOperationException("Domain is already disposed");
28+
29+
var key = (assemblyPath, typeName, function);
30+
31+
EntryPoint result;
32+
33+
if (!_delegates.TryGetValue(key, out result))
34+
{
35+
var assembly = Domain.Load(AssemblyName.GetAssemblyName(assemblyPath));
36+
var type = assembly.GetType(typeName, throwOnError: true);
37+
38+
Print($"Loaded type {type}");
39+
result = (EntryPoint)Delegate.CreateDelegate(typeof(EntryPoint), type, function);
40+
41+
_delegates[key] = result;
42+
}
43+
44+
return result;
45+
}
46+
47+
public void Dispose()
48+
{
49+
if (!_disposed)
50+
{
51+
_delegates.Clear();
52+
53+
if (Domain != AppDomain.CurrentDomain)
54+
AppDomain.Unload(Domain);
55+
56+
_disposed = true;
57+
}
58+
}
59+
60+
}
61+
}

0 commit comments

Comments
 (0)