@@ -3281,365 +3281,6 @@ static PyObject *erlang_whereis_impl(PyObject *self, PyObject *args) {
32813281 return result ;
32823282}
32833283
3284- /* ============================================================================
3285- * SharedDict Python Methods
3286- *
3287- * These functions implement the Python-side API for SharedDict, allowing
3288- * Python code to access process-scoped shared dictionaries.
3289- * ============================================================================ */
3290-
3291- /**
3292- * Extract py_shared_dict_t* from a PyCapsule.
3293- * Returns NULL and sets exception on error.
3294- */
3295- static py_shared_dict_t * get_shared_dict_from_capsule (PyObject * capsule ) {
3296- if (!PyCapsule_IsValid (capsule , "py_shared_dict" )) {
3297- PyErr_SetString (PyExc_TypeError , "Invalid SharedDict handle" );
3298- return NULL ;
3299- }
3300- py_shared_dict_t * sd = (py_shared_dict_t * )PyCapsule_GetPointer (capsule , "py_shared_dict" );
3301- if (sd == NULL ) {
3302- PyErr_SetString (PyExc_ValueError , "SharedDict handle is NULL" );
3303- return NULL ;
3304- }
3305- if (atomic_load (& sd -> destroyed )) {
3306- PyErr_SetString (PyExc_RuntimeError , "SharedDict has been destroyed" );
3307- return NULL ;
3308- }
3309- return sd ;
3310- }
3311-
3312- /**
3313- * Python implementation of SharedDict.get(key, default=None)
3314- * Usage: erlang._shared_dict_get(handle, key)
3315- */
3316- static PyObject * py_shared_dict_get_impl (PyObject * self , PyObject * args ) {
3317- (void )self ;
3318- PyObject * handle ;
3319- const char * key ;
3320- Py_ssize_t key_len ;
3321-
3322- if (!PyArg_ParseTuple (args , "Os#" , & handle , & key , & key_len )) {
3323- return NULL ;
3324- }
3325-
3326- py_shared_dict_t * sd = get_shared_dict_from_capsule (handle );
3327- if (sd == NULL ) return NULL ;
3328-
3329- pthread_mutex_lock (& sd -> mutex );
3330-
3331- if (atomic_load (& sd -> destroyed ) || sd -> dict == NULL ) {
3332- pthread_mutex_unlock (& sd -> mutex );
3333- PyErr_SetString (PyExc_RuntimeError , "SharedDict has been destroyed" );
3334- return NULL ;
3335- }
3336-
3337- /* Create Python key from bytes */
3338- PyObject * py_key = PyBytes_FromStringAndSize (key , key_len );
3339- if (py_key == NULL ) {
3340- pthread_mutex_unlock (& sd -> mutex );
3341- return NULL ;
3342- }
3343-
3344- /* Look up pickled value */
3345- PyObject * pickled = PyDict_GetItem (sd -> dict , py_key );
3346- Py_DECREF (py_key );
3347-
3348- if (pickled == NULL ) {
3349- pthread_mutex_unlock (& sd -> mutex );
3350- Py_RETURN_NONE ;
3351- }
3352-
3353- /* Unpickle the value */
3354- PyObject * pickle_mod = PyImport_ImportModule ("pickle" );
3355- if (pickle_mod == NULL ) {
3356- pthread_mutex_unlock (& sd -> mutex );
3357- return NULL ;
3358- }
3359-
3360- PyObject * loads = PyObject_GetAttrString (pickle_mod , "loads" );
3361- Py_DECREF (pickle_mod );
3362- if (loads == NULL ) {
3363- pthread_mutex_unlock (& sd -> mutex );
3364- return NULL ;
3365- }
3366-
3367- PyObject * value = PyObject_CallFunctionObjArgs (loads , pickled , NULL );
3368- Py_DECREF (loads );
3369-
3370- pthread_mutex_unlock (& sd -> mutex );
3371- return value ;
3372- }
3373-
3374- /**
3375- * Python implementation of SharedDict.set(key, value)
3376- * Usage: erlang._shared_dict_set(handle, key, value)
3377- */
3378- static PyObject * py_shared_dict_set_impl (PyObject * self , PyObject * args ) {
3379- (void )self ;
3380- PyObject * handle ;
3381- const char * key ;
3382- Py_ssize_t key_len ;
3383- PyObject * value ;
3384-
3385- if (!PyArg_ParseTuple (args , "Os#O" , & handle , & key , & key_len , & value )) {
3386- return NULL ;
3387- }
3388-
3389- py_shared_dict_t * sd = get_shared_dict_from_capsule (handle );
3390- if (sd == NULL ) return NULL ;
3391-
3392- pthread_mutex_lock (& sd -> mutex );
3393-
3394- if (atomic_load (& sd -> destroyed ) || sd -> dict == NULL ) {
3395- pthread_mutex_unlock (& sd -> mutex );
3396- PyErr_SetString (PyExc_RuntimeError , "SharedDict has been destroyed" );
3397- return NULL ;
3398- }
3399-
3400- /* Pickle the value */
3401- PyObject * pickle_mod = PyImport_ImportModule ("pickle" );
3402- if (pickle_mod == NULL ) {
3403- pthread_mutex_unlock (& sd -> mutex );
3404- return NULL ;
3405- }
3406-
3407- PyObject * dumps = PyObject_GetAttrString (pickle_mod , "dumps" );
3408- Py_DECREF (pickle_mod );
3409- if (dumps == NULL ) {
3410- pthread_mutex_unlock (& sd -> mutex );
3411- return NULL ;
3412- }
3413-
3414- PyObject * pickled = PyObject_CallFunctionObjArgs (dumps , value , NULL );
3415- Py_DECREF (dumps );
3416- if (pickled == NULL ) {
3417- pthread_mutex_unlock (& sd -> mutex );
3418- return NULL ;
3419- }
3420-
3421- /* Create Python key */
3422- PyObject * py_key = PyBytes_FromStringAndSize (key , key_len );
3423- if (py_key == NULL ) {
3424- Py_DECREF (pickled );
3425- pthread_mutex_unlock (& sd -> mutex );
3426- return NULL ;
3427- }
3428-
3429- /* Store in dict */
3430- int result = PyDict_SetItem (sd -> dict , py_key , pickled );
3431- Py_DECREF (py_key );
3432- Py_DECREF (pickled );
3433-
3434- pthread_mutex_unlock (& sd -> mutex );
3435-
3436- if (result < 0 ) {
3437- return NULL ;
3438- }
3439- Py_RETURN_NONE ;
3440- }
3441-
3442- /**
3443- * Python implementation of SharedDict.delete(key)
3444- * Usage: erlang._shared_dict_del(handle, key)
3445- * Returns True if key existed, False otherwise
3446- */
3447- static PyObject * py_shared_dict_del_impl (PyObject * self , PyObject * args ) {
3448- (void )self ;
3449- PyObject * handle ;
3450- const char * key ;
3451- Py_ssize_t key_len ;
3452-
3453- if (!PyArg_ParseTuple (args , "Os#" , & handle , & key , & key_len )) {
3454- return NULL ;
3455- }
3456-
3457- py_shared_dict_t * sd = get_shared_dict_from_capsule (handle );
3458- if (sd == NULL ) return NULL ;
3459-
3460- pthread_mutex_lock (& sd -> mutex );
3461-
3462- if (atomic_load (& sd -> destroyed ) || sd -> dict == NULL ) {
3463- pthread_mutex_unlock (& sd -> mutex );
3464- PyErr_SetString (PyExc_RuntimeError , "SharedDict has been destroyed" );
3465- return NULL ;
3466- }
3467-
3468- /* Create Python key */
3469- PyObject * py_key = PyBytes_FromStringAndSize (key , key_len );
3470- if (py_key == NULL ) {
3471- pthread_mutex_unlock (& sd -> mutex );
3472- return NULL ;
3473- }
3474-
3475- /* Check if key exists */
3476- int exists = PyDict_Contains (sd -> dict , py_key );
3477- if (exists > 0 ) {
3478- PyDict_DelItem (sd -> dict , py_key );
3479- PyErr_Clear ();
3480- }
3481- Py_DECREF (py_key );
3482-
3483- pthread_mutex_unlock (& sd -> mutex );
3484-
3485- if (exists > 0 ) {
3486- Py_RETURN_TRUE ;
3487- }
3488- Py_RETURN_FALSE ;
3489- }
3490-
3491- /**
3492- * Python implementation of SharedDict.contains(key)
3493- * Usage: erlang._shared_dict_contains(handle, key)
3494- */
3495- static PyObject * py_shared_dict_contains_impl (PyObject * self , PyObject * args ) {
3496- (void )self ;
3497- PyObject * handle ;
3498- const char * key ;
3499- Py_ssize_t key_len ;
3500-
3501- if (!PyArg_ParseTuple (args , "Os#" , & handle , & key , & key_len )) {
3502- return NULL ;
3503- }
3504-
3505- py_shared_dict_t * sd = get_shared_dict_from_capsule (handle );
3506- if (sd == NULL ) return NULL ;
3507-
3508- pthread_mutex_lock (& sd -> mutex );
3509-
3510- if (atomic_load (& sd -> destroyed ) || sd -> dict == NULL ) {
3511- pthread_mutex_unlock (& sd -> mutex );
3512- PyErr_SetString (PyExc_RuntimeError , "SharedDict has been destroyed" );
3513- return NULL ;
3514- }
3515-
3516- /* Create Python key */
3517- PyObject * py_key = PyBytes_FromStringAndSize (key , key_len );
3518- if (py_key == NULL ) {
3519- pthread_mutex_unlock (& sd -> mutex );
3520- return NULL ;
3521- }
3522-
3523- int exists = PyDict_Contains (sd -> dict , py_key );
3524- Py_DECREF (py_key );
3525-
3526- pthread_mutex_unlock (& sd -> mutex );
3527-
3528- if (exists > 0 ) {
3529- Py_RETURN_TRUE ;
3530- } else if (exists == 0 ) {
3531- Py_RETURN_FALSE ;
3532- } else {
3533- return NULL ; /* Error occurred */
3534- }
3535- }
3536-
3537- /**
3538- * Python implementation of SharedDict.destroy()
3539- * Usage: erlang._shared_dict_destroy(handle)
3540- *
3541- * Explicitly destroys the SharedDict, invalidating all references.
3542- * This is idempotent - calling on already-destroyed dict returns None.
3543- */
3544- static PyObject * py_shared_dict_destroy_impl (PyObject * self , PyObject * args ) {
3545- (void )self ;
3546- PyObject * capsule ;
3547-
3548- if (!PyArg_ParseTuple (args , "O" , & capsule )) {
3549- return NULL ;
3550- }
3551-
3552- if (!PyCapsule_IsValid (capsule , "py_shared_dict" )) {
3553- PyErr_SetString (PyExc_TypeError , "Invalid SharedDict handle" );
3554- return NULL ;
3555- }
3556-
3557- py_shared_dict_t * sd = (py_shared_dict_t * )PyCapsule_GetPointer (capsule , "py_shared_dict" );
3558- if (sd == NULL ) {
3559- PyErr_SetString (PyExc_ValueError , "SharedDict handle is NULL" );
3560- return NULL ;
3561- }
3562-
3563- /* If already destroyed, return None (idempotent) */
3564- if (atomic_load (& sd -> destroyed )) {
3565- Py_RETURN_NONE ;
3566- }
3567-
3568- /* Mark as destroyed */
3569- atomic_store (& sd -> destroyed , true);
3570-
3571- /* Clear the Python dict */
3572- pthread_mutex_lock (& sd -> mutex );
3573- Py_CLEAR (sd -> dict );
3574- pthread_mutex_unlock (& sd -> mutex );
3575-
3576- Py_RETURN_NONE ;
3577- }
3578-
3579- /**
3580- * Python implementation of SharedDict.keys()
3581- * Usage: erlang._shared_dict_keys(handle)
3582- */
3583- static PyObject * py_shared_dict_keys_impl (PyObject * self , PyObject * args ) {
3584- (void )self ;
3585- PyObject * handle ;
3586-
3587- if (!PyArg_ParseTuple (args , "O" , & handle )) {
3588- return NULL ;
3589- }
3590-
3591- py_shared_dict_t * sd = get_shared_dict_from_capsule (handle );
3592- if (sd == NULL ) return NULL ;
3593-
3594- pthread_mutex_lock (& sd -> mutex );
3595-
3596- if (atomic_load (& sd -> destroyed ) || sd -> dict == NULL ) {
3597- pthread_mutex_unlock (& sd -> mutex );
3598- PyErr_SetString (PyExc_RuntimeError , "SharedDict has been destroyed" );
3599- return NULL ;
3600- }
3601-
3602- /* Get keys and decode them to strings */
3603- PyObject * keys = PyDict_Keys (sd -> dict );
3604- if (keys == NULL ) {
3605- pthread_mutex_unlock (& sd -> mutex );
3606- return NULL ;
3607- }
3608-
3609- Py_ssize_t len = PyList_Size (keys );
3610- PyObject * result = PyList_New (len );
3611- if (result == NULL ) {
3612- Py_DECREF (keys );
3613- pthread_mutex_unlock (& sd -> mutex );
3614- return NULL ;
3615- }
3616-
3617- for (Py_ssize_t i = 0 ; i < len ; i ++ ) {
3618- PyObject * py_key = PyList_GetItem (keys , i );
3619- if (PyBytes_Check (py_key )) {
3620- /* Decode bytes to string */
3621- PyObject * str_key = PyUnicode_DecodeUTF8 (
3622- PyBytes_AS_STRING (py_key ),
3623- PyBytes_GET_SIZE (py_key ),
3624- "replace" );
3625- if (str_key == NULL ) {
3626- Py_DECREF (result );
3627- Py_DECREF (keys );
3628- pthread_mutex_unlock (& sd -> mutex );
3629- return NULL ;
3630- }
3631- PyList_SET_ITEM (result , i , str_key );
3632- } else {
3633- Py_INCREF (py_key );
3634- PyList_SET_ITEM (result , i , py_key );
3635- }
3636- }
3637-
3638- Py_DECREF (keys );
3639- pthread_mutex_unlock (& sd -> mutex );
3640- return result ;
3641- }
3642-
36433284/* Python method definitions for erlang module */
36443285static PyMethodDef ErlangModuleMethods [] = {
36453286 {"call" , erlang_call_impl , METH_VARARGS ,
0 commit comments