Skip to content

Fixed Second PythonEngine.Initialize call, all sensitive static variables now reseted. #534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].

### Fixed

- Fixed secondary PythonEngine.Initialize call, all sensitive static variables now reseted.
This is a hidden bug. Once python cleaning up enough memory, objects from previous engine run becomes corrupted. ([#534][p534])
- Fixed Visual Studio 2017 compat ([#434][i434]) for setup.py
- Fixed crashes when integrating pythonnet in Unity3d ([#714][i714]),
related to unloading the Application Domain
Expand Down Expand Up @@ -695,3 +697,4 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
[i131]: https://github.com/pythonnet/pythonnet/issues/131
[p531]: https://github.com/pythonnet/pythonnet/pull/531
[i755]: https://github.com/pythonnet/pythonnet/pull/755
[p534]: https://github.com/pythonnet/pythonnet/pull/534
21 changes: 21 additions & 0 deletions src/embed_tests/GlobalTestsSetup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using NUnit.Framework;
using Python.Runtime;

namespace Python.EmbeddingTest
{

// As the SetUpFixture, the OneTimeTearDown of this class is executed after
// all tests have run.
[SetUpFixture]
public class GlobalTestsSetup
{
[OneTimeTearDown]
public void FinalCleanup()
{
if (PythonEngine.IsInitialized)
{
PythonEngine.Shutdown();
}
}
}
}
1 change: 1 addition & 0 deletions src/embed_tests/Python.EmbeddingTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
<Compile Include="TestRuntime.cs" />
<Compile Include="TestPyScope.cs" />
<Compile Include="TestTypeManager.cs" />
<Compile Include="GlobalTestsSetup.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\runtime\Python.Runtime.csproj">
Expand Down
39 changes: 36 additions & 3 deletions src/embed_tests/TestPythonEngineProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,41 @@ public static void GetPythonHomeDefault()
[Test]
public void SetPythonHome()
{
// We needs to ensure that engine was started and shutdown at least once before setting dummy home.
// Otherwise engine will not run with dummy path with random problem.
if (!PythonEngine.IsInitialized)
{
PythonEngine.Initialize();
}

PythonEngine.Shutdown();

var pythonHomeBackup = PythonEngine.PythonHome;

var pythonHome = "/dummypath/";

PythonEngine.PythonHome = pythonHome;
PythonEngine.Initialize();

Assert.AreEqual(pythonHome, PythonEngine.PythonHome);
PythonEngine.Shutdown();

// Restoring valid pythonhome.
PythonEngine.PythonHome = pythonHomeBackup;
}

[Test]
public void SetPythonHomeTwice()
{
// We needs to ensure that engine was started and shutdown at least once before setting dummy home.
// Otherwise engine will not run with dummy path with random problem.
if (!PythonEngine.IsInitialized)
{
PythonEngine.Initialize();
}
PythonEngine.Shutdown();

var pythonHomeBackup = PythonEngine.PythonHome;

var pythonHome = "/dummypath/";

PythonEngine.PythonHome = "/dummypath2/";
Expand All @@ -129,18 +152,29 @@ public void SetPythonHomeTwice()

Assert.AreEqual(pythonHome, PythonEngine.PythonHome);
PythonEngine.Shutdown();

PythonEngine.PythonHome = pythonHomeBackup;
}

[Test]
public void SetProgramName()
{
if (PythonEngine.IsInitialized)
{
PythonEngine.Shutdown();
}

var programNameBackup = PythonEngine.ProgramName;

var programName = "FooBar";

PythonEngine.ProgramName = programName;
PythonEngine.Initialize();

Assert.AreEqual(programName, PythonEngine.ProgramName);
PythonEngine.Shutdown();

PythonEngine.ProgramName = programNameBackup;
}

[Test]
Expand All @@ -156,7 +190,7 @@ public void SetPythonPath()
string path = PythonEngine.PythonPath;
PythonEngine.Shutdown();

PythonEngine.ProgramName = path;
PythonEngine.PythonPath = path;
PythonEngine.Initialize();

Assert.AreEqual(path, PythonEngine.PythonPath);
Expand All @@ -171,7 +205,6 @@ public void SetPythonPathExceptionOn27()
Assert.Pass();
}

// Get previous path to avoid crashing Python
PythonEngine.Initialize();
string path = PythonEngine.PythonPath;
PythonEngine.Shutdown();
Expand Down
16 changes: 13 additions & 3 deletions src/embed_tests/TestRuntime.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
using System;
using System;
using NUnit.Framework;
using Python.Runtime;

namespace Python.EmbeddingTest
{
public class TestRuntime
{
[OneTimeSetUp]
public void SetUp()
{
// We needs to ensure that no any engines are running.
if (PythonEngine.IsInitialized)
{
PythonEngine.Shutdown();
}
}

/// <summary>
/// Test the cache of the information from the platform module.
///
Expand All @@ -24,12 +34,12 @@ public static void PlatformCache()

// Don't shut down the runtime: if the python engine was initialized
// but not shut down by another test, we'd end up in a bad state.
}
}

[Test]
public static void Py_IsInitializedValue()
{
Runtime.Runtime.Py_Finalize(); // In case another test left it on.
Runtime.Runtime.Py_Finalize();
Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized());
Runtime.Runtime.Py_Initialize();
Assert.AreEqual(1, Runtime.Runtime.Py_IsInitialized());
Expand Down
9 changes: 3 additions & 6 deletions src/runtime/assemblymanager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ internal class AssemblyManager
// than it can end up referring to assemblies that are already unloaded (default behavior after unload appDomain -
// unless LoaderOptimization.MultiDomain is used);
// So for multidomain support it is better to have the dict. recreated for each app-domain initialization
private static ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>> namespaces;

private static ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>> namespaces =
new ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>>();
//private static Dictionary<string, Dictionary<string, string>> generics;
private static AssemblyLoadEventHandler lhandler;
private static ResolveEventHandler rhandler;

// updated only under GIL?
private static Dictionary<string, int> probed;
private static Dictionary<string, int> probed = new Dictionary<string, int>(32);

// modified from event handlers below, potentially triggered from different .NET threads
private static ConcurrentQueue<Assembly> assemblies;
Expand All @@ -48,9 +48,6 @@ private AssemblyManager()
/// </summary>
internal static void Initialize()
{
namespaces = new ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>>();
probed = new Dictionary<string, int>(32);
//generics = new Dictionary<string, Dictionary<string, string>>();
assemblies = new ConcurrentQueue<Assembly>();
pypath = new List<string>(16);

Expand Down
9 changes: 8 additions & 1 deletion src/runtime/classderived.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

Expand Down Expand Up @@ -32,6 +33,12 @@ static ClassDerivedObject()
moduleBuilders = new Dictionary<Tuple<string, string>, ModuleBuilder>();
}

public static void Reset()
{
assemblyBuilders = new Dictionary<string, AssemblyBuilder>();
moduleBuilders = new Dictionary<Tuple<string, string>, ModuleBuilder>();
}

internal ClassDerivedObject(Type tp) : base(tp)
{
}
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/classmanager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ static ClassManager()
dtype = typeof(MulticastDelegate);
}

public static void Reset()
{
cache = new Dictionary<Type, ClassBase>(128);
}

/// <summary>
/// Return the ClassBase-derived instance that implements a particular
/// reflected managed type, creating it if it doesn't yet exist.
Expand Down
6 changes: 6 additions & 0 deletions src/runtime/genericutil.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Resources;

namespace Python.Runtime
{
Expand All @@ -20,6 +21,11 @@ static GenericUtil()
mapping = new Dictionary<string, Dictionary<string, List<string>>>();
}

public static void Reset()
{
mapping = new Dictionary<string, Dictionary<string, List<string>>>();
}

/// <summary>
/// Register a generic type that appears in a given namespace.
/// </summary>
Expand Down
11 changes: 11 additions & 0 deletions src/runtime/moduleobject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,17 @@ public CLRModule() : base("clr")
}
}

public static void Reset()
{
hacked = false;
interactive_preload = true;
preload = false;

// XXX Test performance of new features //
_SuppressDocs = false;
_SuppressOverloads = false;
}

/// <summary>
/// The initializing of the preload hook has to happen as late as
/// possible since sys.ps1 is created after the CLR module is
Expand Down
7 changes: 6 additions & 1 deletion src/runtime/pyscope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -537,10 +537,15 @@ public void Dispose()

public class PyScopeManager
{
public readonly static PyScopeManager Global = new PyScopeManager();
public static PyScopeManager Global;

private Dictionary<string, PyScope> NamedScopes = new Dictionary<string, PyScope>();

internal static void Reset()
{
Global = new PyScopeManager();
}

internal PyScope NewScope(string name)
{
if (name == null)
Expand Down
9 changes: 2 additions & 7 deletions src/runtime/pythonengine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,19 +309,14 @@ public static void Shutdown()
{
if (initialized)
{
PyScopeManager.Global.Clear();

// If the shutdown handlers trigger a domain unload,
// don't call shutdown again.
AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;

ExecuteShutdownHandlers();

Marshal.FreeHGlobal(_pythonHome);
_pythonHome = IntPtr.Zero;
Marshal.FreeHGlobal(_programName);
_programName = IntPtr.Zero;
Marshal.FreeHGlobal(_pythonPath);
_pythonPath = IntPtr.Zero;

initialized = false;
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/runtime/runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,15 @@ internal static void Initialize()
PyEval_InitThreads();
}

IsFinalizing = false;

CLRModule.Reset();
GenericUtil.Reset();
PyScopeManager.Reset();
ClassManager.Reset();
ClassDerivedObject.Reset();
TypeManager.Reset();

IntPtr op;
IntPtr dict;
if (IsPython3)
Expand Down
4 changes: 4 additions & 0 deletions src/runtime/typemanager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ static TypeManager()
cache = new Dictionary<Type, IntPtr>(128);
}

public static void Reset()
{
cache = new Dictionary<Type, IntPtr>(128);
}

/// <summary>
/// Given a managed Type derived from ExtensionType, get the handle to
Expand Down