8000 Decref the members of Runtime(split from #958) by amos402 · Pull Request #1019 · pythonnet/pythonnet · GitHub
[go: up one dir, main page]

Skip to content

Decref the members of Runtime(split from #958) #1019

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 4 commits into from
Jan 22, 2020
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
25 changes: 2 additions & 23 deletions src/runtime/importhook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,6 @@ internal static void ReleaseModuleDef()
}
#endif

/// <summary>
/// Get a <i>New reference</i> to the builtins module.
/// </summary>
static IntPtr GetNewRefToBuiltins()
{
if (Runtime.IsPython3)
{
return Runtime.PyImport_ImportModule("builtins");
}
else
{
// dict is a borrowed ref, no need to decref
IntPtr dict = Runtime.PyImport_GetModuleDict();

// GetItemString is a borrowed ref; incref to get a new ref
IntPtr builtins = Runtime.PyDict_GetItemString(dict, "__builtin__");
Runtime.XIncref(builtins);
return builtins;
}
}

/// <summary>
/// Initialize just the __import__ hook itself.
/// </summary>
Expand All @@ -64,7 +43,7 @@ static void InitImport()
// We replace the built-in Python __import__ with our own: first
// look in CLR modules, then if we don't find any call the default
// Python __import__.
IntPtr builtins = GetNewRefToBuiltins();
IntPtr builtins = Runtime.GetBuiltins();
py_import = Runtime.PyObject_GetAttrString(builtins, "__import__");
PythonException.ThrowIfIsNull(py_import);

Expand All @@ -80,7 +59,7 @@ static void InitImport()
/// </summary>
static void RestoreImport()
{
IntPtr builtins = GetNewRefToBuiltins();
IntPtr builtins = Runtime.GetBuiltins();

int res = Runtime.PyObject_SetAttrString(builtins, "__import__", py_import);
PythonException.ThrowIfIsNotZero(res);
Expand Down
201 changes: 137 additions & 64 deletions src/runtime/runtime.cs
8000
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ public class Runtime
/// </summary>
internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32;

private static PyReferenceCollection _pyRefs = new PyReferenceCollection();

/// <summary>
/// Initialize the runtime...
/// </summary>
Expand All @@ -194,99 +196,116 @@ internal static void Initialize(bool initSigs = false)
TypeManager.Reset();

IntPtr op;
IntPtr dict;
if (IsPython3)
{
op = PyImport_ImportModule("builtins");
dict = PyObject_GetAttrString(op, "__dict__");
}
else // Python2
{
dict = PyImport_GetModuleDict();
op = PyDict_GetItemString(dict, "__builtin__");
var builtins = GetBuiltins();
SetPyMember(ref PyNotImplemented, PyObject_GetAttrString(builtins, "NotImplemented"),
() => PyNotImplemented = IntPtr.Zero);

SetPyMember(ref PyBaseObjectType, PyObject_GetAttrString(builtins, "object"),
() => PyBaseObjectType = IntPtr.Zero);

SetPyMember(ref PyNone, PyObject_GetAttrString(builtins, "None"),
() => PyNone = IntPtr.Zero);
SetPyMember(ref PyTrue, PyObject_GetAttrString(builtins, "True"),
() => PyTrue = IntPtr.Zero);
SetPyMember(ref PyFalse, PyObject_GetAttrString(builtins, "False"),
() => PyFalse = IntPtr.Zero);

SetPyMember(ref PyBoolType, PyObject_Type(PyTrue),
() => PyBoolType = IntPtr.Zero);
SetPyMember(ref PyNoneType, PyObject_Type(PyNone),
() => PyNoneType = IntPtr.Zero);
SetPyMember(ref PyTypeType, PyObject_Type(PyNoneType),
() => PyTypeType = IntPtr.Zero);

op = PyObject_GetAttrString(builtins, "len");
SetPyMember(ref PyMethodType, PyObject_Type(op),
() => PyMethodType = IntPtr.Zero);
XDecref(op);

// For some arcane reason, builtins.__dict__.__setitem__ is *not*
// a wrapper_descriptor, even though dict.__setitem__ is.
//
// object.__init__ seems safe, though.
op = PyObject_GetAttrString(PyBaseObjectType, "__init__");
SetPyMember(ref PyWrapperDescriptorType, PyObject_Type(op),
() => PyWrapperDescriptorType = IntPtr.Zero);
XDecref(op);

SetPyMember(ref PySuper_Type, PyObject_GetAttrString(builtins, "super"),
() => PySuper_Type = IntPtr.Zero);

XDecref(builtins);
}
PyNotImplemented = PyObject_GetAttrString(op, "NotImplemented");
PyBaseObjectType = PyObject_GetAttrString(op, "object");

PyNone = PyObject_GetAttrString(op, "None");
PyTrue = PyObject_GetAttrString(op, "True");
PyFalse = PyObject_GetAttrString(op, "False");

PyBoolType = PyObject_Type(PyTrue);
PyNoneType = PyObject_Type(PyNone);
PyTypeType = PyObject_Type(PyNoneType);

op = PyObject_GetAttrString(dict, "keys");
PyMethodType = PyObject_Type(op);
XDecref(op);

// For some arcane reason, builtins.__dict__.__setitem__ is *not*
// a wrapper_descriptor, even though dict.__setitem__ is.
//
// object.__init__ seems safe, though.
op = PyObject_GetAttrString(PyBaseObjectType, "__init__");
PyWrapperDescriptorType = PyObject_Type(op);
XDecref(op);

#if PYTHON3
XDecref(dict);
#endif

op = PyString_FromString("string");
PyStringType = PyObject_Type(op);
SetPyMember(ref PyStringType, PyObject_Type(op),
() => PyStringType = IntPtr.Zero);
XDecref(op);

op = PyUnicode_FromString("unicode");
PyUnicodeType = PyObject_Type(op);
SetPyMember(ref PyUnicodeType, PyObject_Type(op),
() => PyUnicodeType = IntPtr.Zero);
XDecref(op);

#if PYTHON3
op = PyBytes_FromString("bytes");
PyBytesType = PyObject_Type(op);
SetPyMember(ref PyBytesType, PyObject_Type(op),
() => PyBytesType = IntPtr.Zero);
XDecref(op);
#endif

op = PyTuple_New(0);
PyTupleType = PyObject_Type(op);
SetPyMember(ref PyTupleType, PyObject_Type(op),
() => PyTupleType = IntPtr.Zero);
XDecref(op);

op = PyList_New(0);
PyListType = PyObject_Type(op);
SetPyMember(ref PyListType, PyObject_Type(op),
() => PyListType = IntPtr.Zero);
XDecref(op);

op = PyDict_New();
PyDictType = PyObject_Type(op);
SetPyMember(ref PyDictType, PyObject_Type(op),
() => PyDictType = IntPtr.Zero);
XDecref(op);

op = PyInt_FromInt32(0);
PyIntType = PyObject_Type(op);
SetPyMember(ref PyIntType, PyObject_Type(op),
() => PyIntType = IntPtr.Zero);
XDecref(op);

op = PyLong_FromLong(0);
PyLongType = PyObject_Type(op);
SetPyMember(ref PyLongType, PyObject_Type(op),
() => PyLongType = IntPtr.Zero);
XDecref(op);

op = PyFloat_FromDouble(0);
PyFloatType = PyObject_Type(op);
SetPyMember(ref PyFloatType, PyObject_Type(op),
() => PyFloatType = IntPtr.Zero);
XDecref(op);

#if PYTHON3
#if !PYTHON2
PyClassType = IntPtr.Zero;
PyInstanceType = IntPtr.Zero;
#elif PYTHON2
IntPtr s = PyString_FromString("_temp");
IntPtr d = PyDict_New();
#else
{
IntPtr s = PyString_FromString("_temp");
IntPtr d = PyDict_New();

IntPtr c = PyClass_New(IntPtr.Zero, d, s);
PyClassType = PyObject_Type(c);
IntPtr c = PyClass_New(IntPtr.Zero, d, s);
SetPyMember(ref PyClassType, PyObject_Type(c),
() => PyClassType = IntPtr.Zero);

IntPtr i = PyInstance_New(c, IntPtr.Zero, IntPtr.Zero);
PyInstanceType = PyObject_Type(i);
IntPtr i = PyInstance_New(c, IntPtr.Zero, IntPtr.Zero);
SetPyMember(ref PyInstanceType, PyObject_Type(i),
() => PyInstanceType = IntPtr.Zero);

XDecref(s);
XDecref(i);
XDecref(c);
XDecref(d);
XDecref(s);
XDecref(i);
XDecref(c);
XDecref(d);
}
#endif

Error = new IntPtr(-1);
Expand Down Expand Up @@ -380,6 +399,9 @@ internal static void Shutdown()
Exceptions.Shutdown();
ImportHook.Shutdown();
Finalizer.Shutdown();
// TOOD: PyCLRMetaType's release operation still in #958
PyCLRMetaType = IntPtr.Zero;
ResetPyMembers();
Py_Finalize();
}

Expand All @@ -393,6 +415,19 @@ internal static int AtExit()
return 0;
}

private static void SetPyMember(ref IntPtr obj, IntPtr value, Action onRelease)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: obj can be out IntPtr

{
// XXX: For current usages, value should not be null.
PythonException.ThrowIfIsNull(value);
obj = value;
_pyRefs.Add(value, onRelease);
}

private static void ResetPyMembers()
{
_pyRefs.Release();
}

internal static IntPtr Py_single_input = (IntPtr)256;
internal static IntPtr Py_file_input = (IntPtr)257;
internal static IntPtr Py_eval_input = (IntPtr)258;
Expand All @@ -401,6 +436,7 @@ internal static int AtExit()
internal static IntPtr PyModuleType;
internal static IntPtr PyClassType;
internal static IntPtr PyInstanceType;
internal static IntPtr PySuper_Type;
internal static IntPtr PyCLRMetaType;
internal static IntPtr PyMethodType;
internal static IntPtr PyWrapperDescriptorType;
Expand Down Expand Up @@ -963,7 +999,7 @@ internal static int PyObject_Compare(IntPtr value1, IntPtr value2)

internal static long PyObject_Size(IntPtr pointer)
{
return (long) _PyObject_Size(pointer);
return (long)_PyObject_Size(pointer);
}

[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyObject_Size")]
Expand Down Expand Up @@ -1079,7 +1115,7 @@ internal static bool PyLong_Check(IntPtr ob)

internal static IntPtr PyLong_FromUnsignedLong(object value)
{
if(Is32Bit || IsWindows)
if (Is32Bit || IsWindows)
return PyLong_FromUnsignedLong32(Convert.ToUInt32(value));
else
return PyLong_FromUnsignedLong64(Convert.ToUInt64(value));
Expand Down Expand Up @@ -1269,7 +1305,7 @@ internal static int PySequence_DelSlice(IntPtr pointer, long i1, long i2)

internal static long PySequence_Size(IntPtr pointer)
{
return (long) _PySequence_Size(pointer);
return (long)_PySequence_Size(pointer);
}

[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PySequence_Size")]
Expand All @@ -1294,7 +1330,7 @@ internal static IntPtr PySequence_Repeat(IntPtr pointer, long count)

internal static long PySequence_Count(IntPtr pointer, IntPtr value)
{
return (long) _PySequence_Count(pointer, value);
return (long)_PySequence_Count(pointer, value);
}

[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PySequence_Count")]
Expand Down Expand Up @@ -1337,7 +1373,7 @@ internal static IntPtr PyString_FromString(string value)

internal static long PyBytes_Size(IntPtr op)
{
return (long) _PyBytes_Size(op);
return (long)_PyBytes_Size(op);
}

[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyBytes_Size")]
Expand Down Expand Up @@ -1568,7 +1604,7 @@ internal static bool PyDict_Check(IntPtr ob)

internal static long PyDict_Size(IntPtr pointer)
{
return (long) _PyDict_Size(pointer);
return (long)_PyDict_Size(pointer);
}

[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyDict_Size")]
Expand Down Expand Up @@ -1646,7 +1682,7 @@ internal static int PyList_SetSlice(IntPtr pointer, long start, long end, IntPtr

internal static long PyList_Size(IntPtr pointer)
{
return (long) _PyList_Size(pointer);
return (long)_PyList_Size(pointer);
}

[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyList_Size")]
Expand Down Expand Up @@ -1695,7 +1731,7 @@ internal static IntPtr PyTuple_GetSlice(IntPtr pointer, long start, long end)

internal static long PyTuple_Size(IntPtr pointer)
{
return (long) _PyTuple_Size(pointer);
return (long)_PyTuple_Size(pointer);
}

[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyTuple_Size")]
Expand Down Expand Up @@ -1746,6 +1782,9 @@ internal static bool PyIter_Check(IntPtr pointer)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyImport_Import(IntPtr name);

/// <summary>
/// Return value: New reference.
/// </summary>
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyImport_ImportModule(string name);

Expand Down Expand Up @@ -1945,5 +1984,39 @@ internal static void SetNoSiteFlag()
}
}
}

/// <summary>
/// Return value: New reference.
/// </summary>
internal static IntPtr GetBuiltins()
{
return IsPython3 ? PyImport_ImportModule("builtins")
: PyImport_ImportModule("__builtin__");
}
}


class PyReferenceCollection
{
private List<KeyValuePair<IntPtr, Action>> _actions = new List<KeyValuePair<IntPtr, Action>>();

/// <summary>
/// Record obj's address to release the obj in the future,
/// obj must alive before calling Release.
/// </summary>
public void Add(IntPtr ob, Action onRelease)
{
_actions.Add(new KeyValuePair<IntPtr, Action>(ob, onRelease));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: use Tuple<T1, T2>, e.g. just _actions.Add((ob, onRelease))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why using Tuple? As my experience, with two elements cases, KeyValue would always faster than tuple(unless the struct is too big).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simply for readability. This code is not a hot path, so a little performance difference hardly matters.
Also, once we retarget to newer framework, C# will use ValueTuple anyway.

}

public void Release()
{
foreach (var item in _actions)
{
Runtime.XDecref(item.Key);
item.Value?.Invoke();
}
Comment on lines +2014 to +2018
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: use foreach(var (ob, onRelease) in _actions) (requires modification from previous comment).

_actions.Clear();
}
}
}
0