Skip to content

Commit

Permalink
Release GIL when accessing Properties & Fields as we do for methods (#91
Browse files Browse the repository at this point in the history
)

* Fix deadlock accessing properties/fields

* Version bump to 2.0.36
  • Loading branch information
Martin-Molinero committed Apr 23, 2024
1 parent 6172c79 commit 72d052e
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 24 deletions.
23 changes: 13 additions & 10 deletions src/embed_tests/Inheritance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,18 +182,21 @@ public int XProp
{
get
{
using (var scope = Py.CreateScope())
using(Py.GIL())
{
scope.Set("this", this);
try
using (var scope = Py.CreateScope())
{
return scope.Eval<int>($"super(this.__class__, this).{nameof(XProp)}");
}
catch (PythonException ex) when (PythonReferenceComparer.Instance.Equals(ex.Type, Exceptions.AttributeError))
{
if (this.extras.TryGetValue(nameof(this.XProp), out object value))
return (int)value;
throw;
scope.Set("this", this);
try
{
return scope.Eval<int>($"super(this.__class__, this).{nameof(XProp)}");
}
catch (PythonException ex) when (PythonReferenceComparer.Instance.Equals(ex.Type, Exceptions.AttributeError))
{
if (this.extras.TryGetValue(nameof(this.XProp), out object value))
return (int)value;
throw;
}
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions src/embed_tests/QCTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,22 @@ public void TearDown()
/// https://quantconnect.slack.com/archives/G51920EN4/p1615418516028900
public void ParamTest()
{
var output = (bool)module.TestA();
Assert.IsTrue(output);
using (Py.GIL())
{
var output = (bool)module.TestA();
Assert.IsTrue(output);
}
}

[TestCase("AAPL", false)]
[TestCase("SPY", true)]
public void ContainsTest(string key, bool expected)
{
var dic = new Dictionary<string, object> { { "SPY", new object() } };
Assert.AreEqual(expected, (bool)containsTest(key, dic));
using (Py.GIL())
{
Assert.AreEqual(expected, (bool)containsTest(key, dic));
}
}

[Test]
Expand Down
6 changes: 6 additions & 0 deletions src/embed_tests/TestUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ public class TestUtil
[TestCase("PERatio10YearAverage", "pe_ratio_10_year_average")]
[TestCase("CAPERatio", "cape_ratio")]
[TestCase("EVToEBITDA3YearGrowth", "ev_to_ebitda_3_year_growth")]
[TestCase("EVToForwardEBITDA", "ev_to_forward_ebitda")]
[TestCase("EVToRevenue", "ev_to_revenue")]
[TestCase("EVToPreTaxIncome", "ev_to_pre_tax_income")]
[TestCase("EVToTotalAssets", "ev_to_total_assets")]
[TestCase("EVToFCF", "ev_to_fcf")]
[TestCase("EVToEBIT", "ev_to_ebit")]
[TestCase("", "")]
public void ConvertsNameToSnakeCase(string name, string expected)
{
Expand Down
4 changes: 2 additions & 2 deletions src/perf_tests/Python.PerformanceTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.*" />
<PackageReference Include="quantconnect.pythonnet" Version="2.0.35" GeneratePathProperty="true">
<PackageReference Include="quantconnect.pythonnet" Version="2.0.36" GeneratePathProperty="true">
<IncludeAssets>compile</IncludeAssets>
</PackageReference>
</ItemGroup>
Expand All @@ -25,7 +25,7 @@
</Target>

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

<Target Name="CopyNewBuild" AfterTargets="Build">
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
[assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
[assembly: InternalsVisibleTo("Python.Test, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]

[assembly: AssemblyVersion("2.0.35")]
[assembly: AssemblyFileVersion("2.0.35")]
[assembly: AssemblyVersion("2.0.36")]
[assembly: AssemblyFileVersion("2.0.36")]
9 changes: 9 additions & 0 deletions src/runtime/Py.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,21 @@ namespace Python.Runtime;

public static class Py
{
public static IDisposable AllowThreads() => new AllowThreadsState();
public static GILState GIL() => PythonEngine.DebugGIL ? new DebugGILState() : new GILState();

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

public sealed class AllowThreadsState : IDisposable
{
private readonly IntPtr ts = PythonEngine.BeginAllowThreads();
public void Dispose()
{
PythonEngine.EndAllowThreads(ts);
}
}

public class GILState : IDisposable
{
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/Python.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<RootNamespace>Python.Runtime</RootNamespace>
<AssemblyName>Python.Runtime</AssemblyName>
<PackageId>QuantConnect.pythonnet</PackageId>
<Version>2.0.35</Version>
<Version>2.0.36</Version>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/pythonnet/pythonnet</RepositoryUrl>
Expand Down
23 changes: 19 additions & 4 deletions src/runtime/Types/FieldObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,18 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference
// Fasterflect does not support constant fields
if (info.IsLiteral && !info.IsInitOnly)
{
result = info.GetValue(null);
using (Py.AllowThreads())
{
result = info.GetValue(null);
}
}
else
{
result = self.GetMemberGetter(info.DeclaringType)(info.DeclaringType);
var getter = self.GetMemberGetter(info.DeclaringType);
using (Py.AllowThreads())
{
result = getter(info.DeclaringType);
}
}

return Converter.ToPython(result, info.FieldType);
Expand All @@ -92,12 +99,20 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference
// Fasterflect does not support constant fields
if (info.IsLiteral && !info.IsInitOnly)
{
result = info.GetValue(co.inst);
using (Py.AllowThreads())
{
result = info.GetValue(co.inst);
}
}
else
{
var type = co.inst.GetType();
result = self.GetMemberGetter(type)(self.IsValueType(type) ? co.inst.WrapIfValueType() : co.inst);
var getter = self.GetMemberGetter(type);
var argument = self.IsValueType(type) ? co.inst.WrapIfValueType() : co.inst;
using (Py.AllowThreads())
{
result = getter(argument);
}
}

return Converter.ToPython(result, info.FieldType);
Expand Down
11 changes: 9 additions & 2 deletions src/runtime/Types/PropertyObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference

try
{
result = self.GetMemberGetter(info.DeclaringType)(info.DeclaringType);
var getterFunc = self.GetMemberGetter(info.DeclaringType);
using (Py.AllowThreads())
{
result = getterFunc(info.DeclaringType);
}
return Converter.ToPython(result, info.PropertyType);
}
catch (Exception e)
Expand All @@ -93,7 +97,10 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference

try
{
result = getter.Invoke(co.inst, Array.Empty<object>());
using (Py.AllowThreads())
{
result = getter.Invoke(co.inst, Array.Empty<object>());
}
return Converter.ToPython(result, info.PropertyType);
}
catch (Exception e)
Expand Down

0 comments on commit 72d052e

Please sign in to comment.