5252/* Capsule name for channel references */
5353#define CHANNEL_CAPSULE_NAME "erlang.channel_ref"
5454
55+ /* Capsule name for shared dict references */
56+ #define SHARED_DICT_CAPSULE_NAME "py_shared_dict"
57+
5558/**
5659 * @brief PyCapsule destructor for channel references
5760 *
@@ -65,6 +68,19 @@ static void channel_capsule_destructor(PyObject *capsule) {
6568 }
6669}
6770
71+ /**
72+ * @brief PyCapsule destructor for shared dict references
73+ *
74+ * Called when a PyCapsule wrapping a SharedDict resource is garbage collected.
75+ * Releases the resource reference that was kept when the capsule was created.
76+ */
77+ static void shared_dict_capsule_destructor (PyObject * capsule ) {
78+ void * ptr = PyCapsule_GetPointer (capsule , SHARED_DICT_CAPSULE_NAME );
79+ if (ptr != NULL ) {
80+ enif_release_resource (ptr );
81+ }
82+ }
83+
6884/* ============================================================================
6985 * Python to Erlang Conversion
7086 * ============================================================================ */
@@ -381,7 +397,16 @@ ERL_NIF_TERM py_to_term(ErlNifEnv *env, PyObject *obj) {
381397 py_channel_t * channel = (py_channel_t * )ptr ;
382398 return enif_make_resource (env , channel );
383399 }
384- /* Not a channel capsule, clear error and fall through */
400+ /* Not a channel capsule, clear error and try SharedDict */
401+ PyErr_Clear ();
402+
403+ ptr = PyCapsule_GetPointer (obj , SHARED_DICT_CAPSULE_NAME );
404+ if (ptr != NULL ) {
405+ /* This is a SharedDict reference capsule - convert back to resource term */
406+ py_shared_dict_t * sd = (py_shared_dict_t * )ptr ;
407+ return enif_make_resource (env , sd );
408+ }
409+ /* Not a SharedDict capsule either, clear error and fall through */
385410 PyErr_Clear ();
386411 }
387412
@@ -647,6 +672,21 @@ static PyObject *term_to_py(ErlNifEnv *env, ERL_NIF_TERM term) {
647672 return PyBuffer_from_resource (pybuf , pybuf );
648673 }
649674
675+ /* Check for shared dict resource - wrap in PyCapsule for Python access */
676+ py_shared_dict_t * shared_dict ;
677+ if (enif_get_resource (env , term , PY_SHARED_DICT_RESOURCE_TYPE , (void * * )& shared_dict )) {
678+ /* Create a PyCapsule wrapping the SharedDict pointer.
679+ * We increment the resource refcount so it stays valid while Python holds it.
680+ * The destructor will decrement it when the capsule is garbage collected. */
681+ enif_keep_resource (shared_dict );
682+ PyObject * capsule = PyCapsule_New (shared_dict , SHARED_DICT_CAPSULE_NAME , shared_dict_capsule_destructor );
683+ if (capsule == NULL ) {
684+ enif_release_resource (shared_dict );
685+ return NULL ;
686+ }
687+ return capsule ;
688+ }
689+
650690 /* Check for reference - serialize to binary for round-trip.
651691 * IMPORTANT: This must come AFTER all resource checks, because NIF
652692 * resource terms also satisfy enif_is_ref() but should be handled
0 commit comments