@@ -108,25 +108,8 @@ static void py_env_resource_dtor(ErlNifEnv *env, void *obj) {
108108 PyGILState_STATE gstate = PyGILState_Ensure ();
109109
110110#ifdef HAVE_SUBINTERPRETERS
111- if (res -> pool_slot >= 0 ) {
112- /* Created in a shared-GIL subinterpreter - must DECREF in correct interpreter */
113- subinterp_slot_t * slot = subinterp_pool_get (res -> pool_slot );
114-
115- /* Verify slot is still valid and has same interpreter */
116- if (slot != NULL && slot -> initialized && slot -> interp != NULL ) {
117- int64_t slot_interp_id = PyInterpreterState_GetID (slot -> interp );
118- if (slot_interp_id == res -> interp_id ) {
119- /* Same interpreter, safe to DECREF */
120- PyThreadState * saved = PyThreadState_Swap (slot -> tstate );
121- Py_XDECREF (res -> globals );
122- Py_XDECREF (res -> locals );
123- PyThreadState_Swap (saved );
124- }
125- /* If interp_id mismatch, slot was reused - skip DECREF */
126- }
127- /* If slot invalid/not initialized, interpreter destroyed - skip DECREF */
128- } else if (res -> interp_id != 0 ) {
129- /* OWN_GIL subinterpreter: pool_slot == -1 but interp_id != 0
111+ if (res -> interp_id != 0 ) {
112+ /* OWN_GIL subinterpreter: interp_id != 0
130113 * These dicts were created in an OWN_GIL interpreter. We cannot safely
131114 * DECREF them here because:
132115 * 1. The interpreter might already be destroyed
@@ -290,7 +273,6 @@ static int is_inline_schedule_marker(PyObject *obj);
290273#include "py_event_loop.c"
291274#include "py_worker_pool.h"
292275#include "py_worker_pool.c"
293- #include "py_subinterp_pool.c"
294276#include "py_subinterp_thread.c"
295277#include "py_parallel_pool.c"
296278#include "py_reactor_buffer.c"
@@ -441,38 +423,6 @@ static void context_destructor(ErlNifEnv *env, void *obj) {
441423 }
442424#endif
443425
444- #ifdef HAVE_SUBINTERPRETERS
445- #ifndef ENABLE_PARALLEL_PYTHON
446- /* For subinterpreter mode: clean up context's own dictionaries and release pool slot */
447- if (ctx -> is_subinterp && ctx -> pool_slot >= 0 ) {
448- /* Clean up Python objects with GIL */
449- if (runtime_is_running ()) {
450- subinterp_slot_t * slot = subinterp_pool_get (ctx -> pool_slot );
451- if (slot != NULL && slot -> initialized ) {
452- PyGILState_STATE gstate = PyGILState_Ensure ();
453- PyThreadState * saved = PyThreadState_Swap (slot -> tstate );
454-
455- Py_XDECREF (ctx -> module_cache );
456- Py_XDECREF (ctx -> globals );
457- Py_XDECREF (ctx -> locals );
458-
459- PyThreadState_Swap (saved );
460- PyGILState_Release (gstate );
461- }
462- }
463- ctx -> module_cache = NULL ;
464- ctx -> globals = NULL ;
465- ctx -> locals = NULL ;
466-
467- subinterp_pool_free (ctx -> pool_slot );
468- ctx -> pool_slot = -1 ;
469- ctx -> destroyed = true;
470- atomic_fetch_add (& g_counters .ctx_destroyed , 1 );
471- return ;
472- }
473- #endif /* !ENABLE_PARALLEL_PYTHON */
474- #endif /* HAVE_SUBINTERPRETERS */
475-
476426 if (!runtime_is_running ()) {
477427 return ;
478428 }
@@ -1224,37 +1174,6 @@ static ERL_NIF_TERM nif_py_init(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg
12241174 /* Save main thread state and release GIL for other threads */
12251175 g_main_thread_state = PyEval_SaveThread ();
12261176
1227- /* Initialize subinterpreter pool (Python 3.12+) before starting executors */
1228- #ifdef HAVE_SUBINTERPRETERS
1229- #ifndef ENABLE_PARALLEL_PYTHON
1230- /* Only init shared-GIL pool if not in parallel mode */
1231- {
1232- int pool_size = DEFAULT_POOL_SIZE ; /* Default pool size */
1233- /* Check for config */
1234- if (argc > 0 && enif_is_map (env , argv [0 ])) {
1235- ERL_NIF_TERM key = enif_make_atom (env , "pool_size" );
1236- ERL_NIF_TERM value ;
1237- if (enif_get_map_value (env , argv [0 ], key , & value )) {
1238- enif_get_int (env , value , & pool_size );
1239- }
1240- }
1241-
1242- /* Restore GIL temporarily to create subinterpreters */
1243- PyEval_RestoreThread (g_main_thread_state );
1244- int pool_result = subinterp_pool_init (pool_size );
1245- g_main_thread_state = PyEval_SaveThread ();
1246-
1247- if (pool_result < 0 ) {
1248- PyEval_RestoreThread (g_main_thread_state );
1249- g_main_thread_state = NULL ;
1250- Py_Finalize ();
1251- atomic_store (& g_runtime_state , PY_STATE_STOPPED );
1252- return make_error (env , "subinterp_pool_init_failed" );
1253- }
1254- }
1255- #endif /* !ENABLE_PARALLEL_PYTHON */
1256- #endif /* HAVE_SUBINTERPRETERS */
1257-
12581177 /* Initialize parallel OWN_GIL pool (Python 3.14+) */
12591178#ifdef ENABLE_PARALLEL_PYTHON
12601179 {
@@ -1402,19 +1321,12 @@ static ERL_NIF_TERM nif_finalize(ErlNifEnv *env, int argc, const ERL_NIF_TERM ar
14021321 Py_XDECREF (g_numpy_ndarray_type );
14031322 g_numpy_ndarray_type = NULL ;
14041323
1405- #ifdef HAVE_SUBINTERPRETERS
1406- /* Step 4: Shutdown subinterpreter pool - must be done with GIL held */
1407- subinterp_pool_shutdown ();
1408- #endif
14091324 g_main_thread_state = PyEval_SaveThread ();
14101325 } else {
14111326 /* Fallback to PyGILState if no main thread state saved */
14121327 PyGILState_STATE gstate = PyGILState_Ensure ();
14131328 Py_XDECREF (g_numpy_ndarray_type );
14141329 g_numpy_ndarray_type = NULL ;
1415- #ifdef HAVE_SUBINTERPRETERS
1416- subinterp_pool_shutdown ();
1417- #endif
14181330 PyGILState_Release (gstate );
14191331 }
14201332#endif
@@ -4231,92 +4143,10 @@ static ERL_NIF_TERM nif_context_create(ErlNifEnv *env, int argc, const ERL_NIF_T
42314143 }
42324144 /* Fall through to worker mode for non-subinterp requests in parallel build */
42334145#endif /* ENABLE_PARALLEL_PYTHON */
4234-
4235- #ifndef ENABLE_PARALLEL_PYTHON
4236- if (use_owngil ) {
4237- /* OWN_GIL mode: create dedicated pthread with OWN_GIL subinterpreter */
4238- if (owngil_context_init (ctx ) != 0 ) {
4239- close (ctx -> callback_pipe [0 ]);
4240- close (ctx -> callback_pipe [1 ]);
4241- enif_release_resource (ctx );
4242- return make_error (env , "owngil_init_failed" );
4243- }
4244-
4245- ERL_NIF_TERM ref = enif_make_resource (env , ctx );
4246- enif_release_resource (ctx );
4247- atomic_fetch_add (& g_counters .ctx_created , 1 );
4248- return enif_make_tuple3 (env , ATOM_OK , ref , enif_make_uint (env , ctx -> interp_id ));
4249- } else if (use_subinterp ) {
4250- /* Allocate a slot from the subinterpreter pool */
4251- int slot = subinterp_pool_alloc ();
4252- if (slot < 0 ) {
4253- close (ctx -> callback_pipe [0 ]);
4254- close (ctx -> callback_pipe [1 ]);
4255- enif_release_resource (ctx );
4256- return make_error (env , "pool_exhausted" );
4257- }
4258-
4259- ctx -> pool_slot = slot ;
4260-
4261- /* Get the pool slot for interpreter access */
4262- subinterp_slot_t * pool_slot = subinterp_pool_get (slot );
4263- if (pool_slot == NULL || !pool_slot -> initialized ) {
4264- subinterp_pool_free (slot );
4265- close (ctx -> callback_pipe [0 ]);
4266- close (ctx -> callback_pipe [1 ]);
4267- enif_release_resource (ctx );
4268- return make_error (env , "pool_slot_invalid" );
4269- }
4270-
4271- /* Create context's own namespace dictionaries.
4272- * Each context needs its own globals/locals for isolation,
4273- * even though they share the interpreter. */
4274- PyGILState_STATE gstate = PyGILState_Ensure ();
4275- PyThreadState * saved = PyThreadState_Swap (pool_slot -> tstate );
4276-
4277- ctx -> globals = PyDict_New ();
4278- ctx -> locals = PyDict_New ();
4279- ctx -> module_cache = PyDict_New ();
4280-
4281- if (ctx -> globals == NULL || ctx -> locals == NULL || ctx -> module_cache == NULL ) {
4282- Py_XDECREF (ctx -> globals );
4283- Py_XDECREF (ctx -> locals );
4284- Py_XDECREF (ctx -> module_cache );
4285- PyThreadState_Swap (saved );
4286- PyGILState_Release (gstate );
4287- subinterp_pool_free (slot );
4288- close (ctx -> callback_pipe [0 ]);
4289- close (ctx -> callback_pipe [1 ]);
4290- enif_release_resource (ctx );
4291- return make_error (env , "dict_alloc_failed" );
4292- }
4293-
4294- /* Import __builtins__ into globals */
4295- PyObject * builtins = PyEval_GetBuiltins ();
4296- PyDict_SetItemString (ctx -> globals , "__builtins__" , builtins );
4297-
4298- /* Import erlang module into globals */
4299- PyObject * erlang_module = PyImport_ImportModule ("erlang" );
4300- if (erlang_module != NULL ) {
4301- PyDict_SetItemString (ctx -> globals , "erlang" , erlang_module );
4302- Py_DECREF (erlang_module );
4303- } else {
4304- PyErr_Clear ();
4305- }
4306-
4307- PyThreadState_Swap (saved );
4308- PyGILState_Release (gstate );
4309-
4310- #ifdef DEBUG
4311- fprintf (stderr , "[NIF] Created context %u using pool slot %d with own namespace\n" ,
4312- ctx -> interp_id , slot );
4313- fflush (stderr );
4314- #endif
4315- } else
4316- #endif /* !ENABLE_PARALLEL_PYTHON */
43174146#else
4318- /* Pre-3.12 Python - ignore subinterp mode request */
4147+ /* Pre-3.12 Python or non-parallel build - ignore subinterp mode request */
43194148 (void )use_subinterp ;
4149+ (void )use_owngil ;
43204150#endif /* HAVE_SUBINTERPRETERS */
43214151 {
43224152 /* Worker mode - create a thread state in main interpreter */
@@ -4378,59 +4208,6 @@ static ERL_NIF_TERM nif_context_destroy(ErlNifEnv *env, int argc, const ERL_NIF_
43784208 /* Mark as destroyed early to prevent new operations */
43794209 ctx -> destroyed = true;
43804210
4381- #ifdef HAVE_SUBINTERPRETERS
4382- #ifndef ENABLE_PARALLEL_PYTHON
4383- /* OWN_GIL mode: shutdown the dedicated thread */
4384- if (ctx -> uses_own_gil ) {
4385- owngil_context_shutdown (ctx );
4386- /* Close callback pipes */
4387- if (ctx -> callback_pipe [0 ] >= 0 ) {
4388- close (ctx -> callback_pipe [0 ]);
4389- ctx -> callback_pipe [0 ] = -1 ;
4390- }
4391- if (ctx -> callback_pipe [1 ] >= 0 ) {
4392- close (ctx -> callback_pipe [1 ]);
4393- ctx -> callback_pipe [1 ] = -1 ;
4394- }
4395- atomic_fetch_add (& g_counters .ctx_destroyed , 1 );
4396- return ATOM_OK ;
4397- }
4398- #endif /* !ENABLE_PARALLEL_PYTHON */
4399-
4400- if (ctx -> is_subinterp && ctx -> pool_slot >= 0 ) {
4401- /* Clean up context's own namespace dictionaries */
4402- if (runtime_is_running ()) {
4403- subinterp_slot_t * slot = subinterp_pool_get (ctx -> pool_slot );
4404- if (slot != NULL && slot -> initialized ) {
4405- PyGILState_STATE gstate = PyGILState_Ensure ();
4406- PyThreadState * saved = PyThreadState_Swap (slot -> tstate );
4407-
4408- Py_XDECREF (ctx -> module_cache );
4409- Py_XDECREF (ctx -> globals );
4410- Py_XDECREF (ctx -> locals );
4411-
4412- PyThreadState_Swap (saved );
4413- PyGILState_Release (gstate );
4414- }
4415- }
4416- ctx -> globals = NULL ;
4417- ctx -> locals = NULL ;
4418- ctx -> module_cache = NULL ;
4419-
4420- /* Release the pool slot back to the pool */
4421- subinterp_pool_free (ctx -> pool_slot );
4422- ctx -> pool_slot = -1 ;
4423-
4424- #ifdef DEBUG
4425- fprintf (stderr , "[NIF] Destroyed context %u, released pool slot\n" , ctx -> interp_id );
4426- fflush (stderr );
4427- #endif
4428-
4429- atomic_fetch_add (& g_counters .ctx_destroyed , 1 );
4430- return ATOM_OK ;
4431- }
4432- #endif
4433-
44344211 /* Worker mode - clean up Python objects with GIL */
44354212 if (runtime_is_running ()) {
44364213 PyGILState_STATE gstate = PyGILState_Ensure ();
0 commit comments