Skip to content

Commit

Permalink
Use function pointers interface of static interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
atifaziz committed Sep 18, 2024
1 parent 17906fd commit d584188
Showing 1 changed file with 10 additions and 36 deletions.
46 changes: 10 additions & 36 deletions src/CSnakes.Runtime/Python/Pack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,21 @@
namespace CSnakes.Runtime.Python;

/// <summary>
/// These methods are used internally to create a PyObject where the Dispose() call will dispose all items in
/// These methods are used internally to create a PyObject where the Dispose() call will dispose all items in
/// the collection inside the same call stack. This avoids the .NET GC Finalizer thread from disposing the items
/// that were created and creating a GIL contention issue when other code is running.
/// </summary>
internal static class Pack
{
internal static PyObject CreateTuple(Span<PyObject> items) =>
PyObject.Create(CreateListOrTuple<TupleBuilder>(items));
internal static unsafe PyObject CreateTuple(Span<PyObject> items) =>
PyObject.Create(CreateListOrTuple(items, &CPythonAPI.PyTuple_New, &CPythonAPI.PyTuple_SetItemRaw));

internal static PyObject CreateList(Span<PyObject> items) =>
PyObject.Create(CreateListOrTuple<ListBuilder>(items));
internal static unsafe PyObject CreateList(Span<PyObject> items) =>
PyObject.Create(CreateListOrTuple(items, &CPythonAPI.PyList_New, &CPythonAPI.PyList_SetItemRaw));

public interface IListOrTupleBuilder
{
static abstract nint New(nint size);
static abstract int SetItemRaw(nint ob, nint pos, nint o);
}

public sealed class ListBuilder : IListOrTupleBuilder
{
// As per Python/C API docs for `PyList_New`:
//
// > If len is greater than zero, the returned list object's items are set to `NULL`. Thus
// > you cannot use abstract API functions such as `PySequence_SetItem()` or expose the
// > object to Python code before setting all items to a real object with
// > `PyList_SetItem()`.
//
// Source: https://docs.python.org/3/c-api/list.html#c.PyList_New

public static IntPtr New(IntPtr size) => CPythonAPI.PyList_New(size);
public static int SetItemRaw(IntPtr ob, IntPtr pos, IntPtr o) => CPythonAPI.PyList_SetItemRaw(ob, pos, o);
}

public sealed class TupleBuilder : IListOrTupleBuilder
{
public static IntPtr New(IntPtr size) => size == 0 ? CPythonAPI.GetPyEmptyTuple() : CPythonAPI.PyTuple_New(size);
public static int SetItemRaw(IntPtr ob, IntPtr pos, IntPtr o) => CPythonAPI.PyTuple_SetItemRaw(ob, pos, o);
}

public static nint CreateListOrTuple<TBuilder>(Span<PyObject> items)
where TBuilder : IListOrTupleBuilder
public static unsafe nint CreateListOrTuple(Span<PyObject> items,
delegate* <nint, nint> newObject,
delegate* <nint, nint, nint, int> setItemRaw)
{
nint obj = 0;

Expand All @@ -70,12 +44,12 @@ public static nint CreateListOrTuple<TBuilder>(Span<PyObject> items)

Debug.Assert(uninitializedHandles.Length == 0);

obj = TBuilder.New(items.Length);
obj = newObject(items.Length);

var i = 0;
foreach (var handle in handles)
{
int result = TBuilder.SetItemRaw(obj, i++, handle);
int result = setItemRaw(obj, i++, handle);
if (result == -1)
{
throw PyObject.ThrowPythonExceptionAsClrException();
Expand Down

0 comments on commit d584188

Please sign in to comment.