Skip to content

Commit 72d052e

Browse files
Release GIL when accessing Properties & Fields as we do for methods (#91)
* Fix deadlock accessing properties/fields * Version bump to 2.0.36
1 parent 6172c79 commit 72d052e

File tree

9 files changed

+70
-24
lines changed

9 files changed

+70
-24
lines changed

src/embed_tests/Inheritance.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -182,18 +182,21 @@ public int XProp
182182
{
183183
get
184184
{
185-
using (var scope = Py.CreateScope())
185+
using(Py.GIL())
186186
{
187-
scope.Set("this", this);
188-
try
187+
using (var scope = Py.CreateScope())
189188
{
190-
return scope.Eval<int>($"super(this.__class__, this).{nameof(XProp)}");
191-
}
192-
catch (PythonException ex) when (PythonReferenceComparer.Instance.Equals(ex.Type, Exceptions.AttributeError))
193-
{
194-
if (this.extras.TryGetValue(nameof(this.XProp), out object value))
195-
return (int)value;
196-
throw;
189+
scope.Set("this", this);
190+
try
191+
{
192+
return scope.Eval<int>($"super(this.__class__, this).{nameof(XProp)}");
193+
}
194+
catch (PythonException ex) when (PythonReferenceComparer.Instance.Equals(ex.Type, Exceptions.AttributeError))
195+
{
196+
if (this.extras.TryGetValue(nameof(this.XProp), out object value))
197+
return (int)value;
198+
throw;
199+
}
197200
}
198201
}
199202
}

src/embed_tests/QCTest.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,22 @@ public void TearDown()
102102
/// https://quantconnect.slack.com/archives/G51920EN4/p1615418516028900
103103
public void ParamTest()
104104
{
105-
var output = (bool)module.TestA();
106-
Assert.IsTrue(output);
105+
using (Py.GIL())
106+
{
107+
var output = (bool)module.TestA();
108+
Assert.IsTrue(output);
109+
}
107110
}
108111

109112
[TestCase("AAPL", false)]
110113
[TestCase("SPY", true)]
111114
public void ContainsTest(string key, bool expected)
112115
{
113116
var dic = new Dictionary<string, object> { { "SPY", new object() } };
114-
Assert.AreEqual(expected, (bool)containsTest(key, dic));
117+
using (Py.GIL())
118+
{
119+
Assert.AreEqual(expected, (bool)containsTest(key, dic));
120+
}
115121
}
116122

117123
[Test]

src/embed_tests/TestUtil.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ public class TestUtil
3333
[TestCase("PERatio10YearAverage", "pe_ratio_10_year_average")]
3434
[TestCase("CAPERatio", "cape_ratio")]
3535
[TestCase("EVToEBITDA3YearGrowth", "ev_to_ebitda_3_year_growth")]
36+
[TestCase("EVToForwardEBITDA", "ev_to_forward_ebitda")]
37+
[TestCase("EVToRevenue", "ev_to_revenue")]
38+
[TestCase("EVToPreTaxIncome", "ev_to_pre_tax_income")]
39+
[TestCase("EVToTotalAssets", "ev_to_total_assets")]
40+
[TestCase("EVToFCF", "ev_to_fcf")]
41+
[TestCase("EVToEBIT", "ev_to_ebit")]
3642
[TestCase("", "")]
3743
public void ConvertsNameToSnakeCase(string name, string expected)
3844
{

src/perf_tests/Python.PerformanceTests.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1414
</PackageReference>
1515
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.*" />
16-
<PackageReference Include="quantconnect.pythonnet" Version="2.0.35" GeneratePathProperty="true">
16+
<PackageReference Include="quantconnect.pythonnet" Version="2.0.36" GeneratePathProperty="true">
1717
<IncludeAssets>compile</IncludeAssets>
1818
</PackageReference>
1919
</ItemGroup>
@@ -25,7 +25,7 @@
2525
</Target>
2626

2727
<Target Name="CopyBaseline" AfterTargets="Build">
28-
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.35\lib\net5.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
28+
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.36\lib\net6.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
2929
</Target>
3030

3131
<Target Name="CopyNewBuild" AfterTargets="Build">

src/runtime/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
[assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
55
[assembly: InternalsVisibleTo("Python.Test, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
66

7-
[assembly: AssemblyVersion("2.0.35")]
8-
[assembly: AssemblyFileVersion("2.0.35")]
7+
[assembly: AssemblyVersion("2.0.36")]
8+
[assembly: AssemblyFileVersion("2.0.36")]

src/runtime/Py.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,21 @@ namespace Python.Runtime;
1010

1111
public static class Py
1212
{
13+
public static IDisposable AllowThreads() => new AllowThreadsState();
1314
public static GILState GIL() => PythonEngine.DebugGIL ? new DebugGILState() : new GILState();
1415

1516
public static PyModule CreateScope() => new();
1617
public static PyModule CreateScope(string name)
1718
=> new(name ?? throw new ArgumentNullException(nameof(name)));
1819

20+
public sealed class AllowThreadsState : IDisposable
21+
{
22+
private readonly IntPtr ts = PythonEngine.BeginAllowThreads();
23+
public void Dispose()
24+
{
25+
PythonEngine.EndAllowThreads(ts);
26+
}
27+
}
1928

2029
public class GILState : IDisposable
2130
{

src/runtime/Python.Runtime.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<RootNamespace>Python.Runtime</RootNamespace>
66
<AssemblyName>Python.Runtime</AssemblyName>
77
<PackageId>QuantConnect.pythonnet</PackageId>
8-
<Version>2.0.35</Version>
8+
<Version>2.0.36</Version>
99
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
1010
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1111
<RepositoryUrl>https://github.com/pythonnet/pythonnet</RepositoryUrl>

src/runtime/Types/FieldObject.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,18 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference
6464
// Fasterflect does not support constant fields
6565
if (info.IsLiteral && !info.IsInitOnly)
6666
{
67-
result = info.GetValue(null);
67+
using (Py.AllowThreads())
68+
{
69+
result = info.GetValue(null);
70+
}
6871
}
6972
else
7073
{
71-
result = self.GetMemberGetter(info.DeclaringType)(info.DeclaringType);
74+
var getter = self.GetMemberGetter(info.DeclaringType);
75+
using (Py.AllowThreads())
76+
{
77+
result = getter(info.DeclaringType);
78+
}
7279
}
7380

7481
return Converter.ToPython(result, info.FieldType);
@@ -92,12 +99,20 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference
9299
// Fasterflect does not support constant fields
93100
if (info.IsLiteral && !info.IsInitOnly)
94101
{
95-
result = info.GetValue(co.inst);
102+
using (Py.AllowThreads())
103+
{
104+
result = info.GetValue(co.inst);
105+
}
96106
}
97107
else
98108
{
99109
var type = co.inst.GetType();
100-
result = self.GetMemberGetter(type)(self.IsValueType(type) ? co.inst.WrapIfValueType() : co.inst);
110+
var getter = self.GetMemberGetter(type);
111+
var argument = self.IsValueType(type) ? co.inst.WrapIfValueType() : co.inst;
112+
using (Py.AllowThreads())
113+
{
114+
result = getter(argument);
115+
}
101116
}
102117

103118
return Converter.ToPython(result, info.FieldType);

src/runtime/Types/PropertyObject.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference
7676

7777
try
7878
{
79-
result = self.GetMemberGetter(info.DeclaringType)(info.DeclaringType);
79+
var getterFunc = self.GetMemberGetter(info.DeclaringType);
80+
using (Py.AllowThreads())
81+
{
82+
result = getterFunc(info.DeclaringType);
83+
}
8084
return Converter.ToPython(result, info.PropertyType);
8185
}
8286
catch (Exception e)
@@ -93,7 +97,10 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference
9397

9498
try
9599
{
96-
result = getter.Invoke(co.inst, Array.Empty<object>());
100+
using (Py.AllowThreads())
101+
{
102+
result = getter.Invoke(co.inst, Array.Empty<object>());
103+
}
97104
return Converter.ToPython(result, info.PropertyType);
98105
}
99106
catch (Exception e)

0 commit comments

Comments
 (0)