diff --git a/c_src/py_convert.c b/c_src/py_convert.c index aac7c0a..8f50b01 100644 --- a/c_src/py_convert.c +++ b/c_src/py_convert.c @@ -141,7 +141,7 @@ static inline bool is_numpy_ndarray(PyObject *obj) { * * @see term_to_py() for the reverse conversion */ -static ERL_NIF_TERM py_to_term(ErlNifEnv *env, PyObject *obj) { +ERL_NIF_TERM py_to_term(ErlNifEnv *env, PyObject *obj) { /* * Type check ordering optimized for web/ASGI workloads: * 1. Strings (most common in HTTP headers, bodies, JSON) diff --git a/c_src/py_event_loop.c b/c_src/py_event_loop.c index 5acb753..edcb0ea 100644 --- a/c_src/py_event_loop.c +++ b/c_src/py_event_loop.c @@ -2774,9 +2774,10 @@ bool event_loop_add_pending(erlang_event_loop_t *loop, event_type_t type, loop->pending_capacity = new_capacity; /* Note: Linked list doesn't need realloc, just the capacity limit */ } else { - /* At hard cap - log warning but don't drop silently */ - /* TODO: Add proper logging mechanism */ - return false; /* Queue at maximum capacity */ + /* At hard cap - warn and reject */ + fprintf(stderr, "event_loop_add_pending: queue at maximum capacity (%zu), rejecting event\n", + (size_t)MAX_PENDING_CAPACITY); + return false; } } diff --git a/c_src/py_nif.h b/c_src/py_nif.h index ae3e3f6..730bd6e 100644 --- a/c_src/py_nif.h +++ b/c_src/py_nif.h @@ -1403,7 +1403,7 @@ extern ERL_NIF_TERM ATOM_SPAN_EVENT; /**< @brief `span_event` atom */ * @note Does not consume a reference to obj * @note May return ATOM_ERROR on allocation failure */ -static ERL_NIF_TERM py_to_term(ErlNifEnv *env, PyObject *obj); +extern ERL_NIF_TERM py_to_term(ErlNifEnv *env, PyObject *obj); /** * @brief Convert an Erlang term to a Python object diff --git a/c_src/py_subinterp_thread.c b/c_src/py_subinterp_thread.c index 73614e9..831cf6c 100644 --- a/c_src/py_subinterp_thread.c +++ b/c_src/py_subinterp_thread.c @@ -24,6 +24,7 @@ */ #include "py_subinterp_thread.h" +#include "py_nif.h" #include #include #include @@ -429,6 +430,15 @@ static void *worker_thread_main(void *arg) { PyObject *result = NULL; PyObject *globals = ns ? ns->globals : PyDict_New(); PyObject *locals = ns ? ns->locals : PyDict_New(); + bool owns_globals = (ns == NULL); + bool owns_locals = (ns == NULL); + + /* Check allocation if we own the dicts */ + if ((owns_globals && globals == NULL) || (owns_locals && locals == NULL)) { + if (owns_globals) Py_XDECREF(globals); + if (owns_locals) Py_XDECREF(locals); + break; + } switch (header.req_type) { case REQ_CALL: @@ -447,6 +457,8 @@ static void *worker_thread_main(void *arg) { } else if (enif_get_atom(tmp_env, elements[0], mod_str, 256, ERL_NIF_LATIN1)) { /* Already filled */ } else { + if (owns_globals) Py_DECREF(globals); + if (owns_locals) Py_DECREF(locals); break; } @@ -458,6 +470,8 @@ static void *worker_thread_main(void *arg) { } else if (enif_get_atom(tmp_env, elements[1], func_str, 256, ERL_NIF_LATIN1)) { /* Already filled */ } else { + if (owns_globals) Py_DECREF(globals); + if (owns_locals) Py_DECREF(locals); break; } @@ -481,6 +495,8 @@ static void *worker_thread_main(void *arg) { if (module == NULL) { PyErr_Clear(); + if (owns_globals) Py_DECREF(globals); + if (owns_locals) Py_DECREF(locals); break; } @@ -490,6 +506,8 @@ static void *worker_thread_main(void *arg) { if (func == NULL) { PyErr_Clear(); + if (owns_globals) Py_DECREF(globals); + if (owns_locals) Py_DECREF(locals); break; } @@ -592,59 +610,13 @@ static void *worker_thread_main(void *arg) { break; } - /* Serialize result */ + /* Clean up owned dicts after switch completes */ + if (owns_globals) Py_DECREF(globals); + if (owns_locals) Py_DECREF(locals); + + /* Serialize result using py_to_term for full type support */ if (success && result != NULL) { - /* For now, just return ok atom */ - /* TODO: Proper py_to_term conversion and ETF serialization */ - ERL_NIF_TERM result_term; - if (result == Py_None) { - result_term = enif_make_atom(tmp_env, "none"); - } else if (PyLong_Check(result)) { - long val = PyLong_AsLong(result); - result_term = enif_make_long(tmp_env, val); - } else if (PyFloat_Check(result)) { - double val = PyFloat_AsDouble(result); - result_term = enif_make_double(tmp_env, val); - } else if (PyUnicode_Check(result)) { - Py_ssize_t size; - const char *str = PyUnicode_AsUTF8AndSize(result, &size); - if (str) { - ErlNifBinary bin; - if (enif_alloc_binary(size, &bin)) { - memcpy(bin.data, str, size); - result_term = enif_make_binary(tmp_env, &bin); - } else { - result_term = enif_make_atom(tmp_env, "conversion_error"); - } - } else { - result_term = enif_make_atom(tmp_env, "conversion_error"); - } - } else if (PyBool_Check(result)) { - result_term = result == Py_True ? - enif_make_atom(tmp_env, "true") : - enif_make_atom(tmp_env, "false"); - } else { - /* Fallback: convert to string representation */ - PyObject *str = PyObject_Str(result); - if (str) { - Py_ssize_t size; - const char *s = PyUnicode_AsUTF8AndSize(str, &size); - if (s) { - ErlNifBinary bin; - if (enif_alloc_binary(size, &bin)) { - memcpy(bin.data, s, size); - result_term = enif_make_binary(tmp_env, &bin); - } else { - result_term = enif_make_atom(tmp_env, "conversion_error"); - } - } else { - result_term = enif_make_atom(tmp_env, "conversion_error"); - } - Py_DECREF(str); - } else { - result_term = enif_make_atom(tmp_env, "pyobject"); - } - } + ERL_NIF_TERM result_term = py_to_term(tmp_env, result); Py_XDECREF(result); /* Wrap in {ok, Result} */