Skip to content

Commit 7d95a38

Browse files
committed
Extract SharedDict to separate file
Move SharedDict implementation from py_nif.c and py_callback.c to dedicated py_shared_dict.c for better code organization.
1 parent feef974 commit 7d95a38

3 files changed

Lines changed: 885 additions & 857 deletions

File tree

c_src/py_callback.c

Lines changed: 0 additions & 359 deletions
Original file line numberDiff line numberDiff line change
@@ -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 */
36443285
static PyMethodDef ErlangModuleMethods[] = {
36453286
{"call", erlang_call_impl, METH_VARARGS,

0 commit comments

Comments
 (0)