@@ -47,9 +47,6 @@ static int g_pool_size = 0;
4747/** @brief Pool initialized flag */
4848static _Atomic bool g_pool_initialized = false;
4949
50- /** @brief Global import generation counter */
51- static _Atomic uint64_t g_import_generation = 0 ;
52-
5350/* ============================================================================
5451 * Forward declarations for functions defined in other modules
5552 * ============================================================================ */
@@ -199,9 +196,6 @@ int subinterp_pool_init(int size) {
199196 }
200197
201198 slot -> initialized = true;
202- atomic_store (& slot -> usage_count , 0 );
203- atomic_store (& slot -> marked_stale , false);
204- slot -> generation = atomic_load (& g_import_generation );
205199
206200 /* Swap back to main thread state before creating next subinterpreter */
207201 PyThreadState_Swap (main_tstate );
@@ -351,193 +345,4 @@ bool subinterp_pool_is_initialized(void) {
351345 return atomic_load (& g_pool_initialized );
352346}
353347
354- void subinterp_pool_acquire (int slot ) {
355- if (slot < 0 || slot >= g_pool_size ) {
356- return ;
357- }
358-
359- subinterp_slot_t * s = & g_subinterp_pool [slot ];
360- if (!s -> initialized ) {
361- return ;
362- }
363-
364- atomic_fetch_add (& s -> usage_count , 1 );
365- }
366-
367- /**
368- * @brief Reinitialize a pool slot with a fresh subinterpreter
369- *
370- * Called after destroying a stale subinterpreter to create a fresh one.
371- * Assumes GIL is already held.
372- *
373- * @param slot_idx Slot index
374- * @return 0 on success, -1 on failure
375- */
376- static int subinterp_pool_reinit_slot (int slot_idx ) {
377- subinterp_slot_t * slot = & g_subinterp_pool [slot_idx ];
378-
379- /* Save main thread state */
380- PyThreadState * main_tstate = PyThreadState_Get ();
381-
382- /* Configure subinterpreter with SHARED GIL */
383- PyInterpreterConfig config = {
384- .use_main_obmalloc = 0 ,
385- .allow_fork = 0 ,
386- .allow_exec = 0 ,
387- .allow_threads = 1 ,
388- .allow_daemon_threads = 0 ,
389- .check_multi_interp_extensions = 1 ,
390- .gil = PyInterpreterConfig_SHARED_GIL ,
391- };
392-
393- PyThreadState * tstate = NULL ;
394- PyStatus status = Py_NewInterpreterFromConfig (& tstate , & config );
395-
396- if (PyStatus_Exception (status ) || tstate == NULL ) {
397- fprintf (stderr , "subinterp_pool_reinit_slot: failed to create subinterp for slot %d\n" , slot_idx );
398- PyThreadState_Swap (main_tstate );
399- return -1 ;
400- }
401-
402- /* tstate is now the current thread state for the new interpreter */
403- slot -> interp = PyThreadState_GetInterpreter (tstate );
404- slot -> tstate = tstate ;
405-
406- /* Initialize globals/locals */
407- slot -> globals = PyDict_New ();
408- slot -> locals = PyDict_New ();
409- slot -> module_cache = PyDict_New ();
410-
411- if (slot -> globals == NULL || slot -> locals == NULL || slot -> module_cache == NULL ) {
412- Py_XDECREF (slot -> module_cache );
413- Py_XDECREF (slot -> globals );
414- Py_XDECREF (slot -> locals );
415- Py_EndInterpreter (tstate );
416- PyThreadState_Swap (main_tstate );
417- return -1 ;
418- }
419-
420- /* Import __builtins__ into globals */
421- PyObject * builtins = PyEval_GetBuiltins ();
422- PyDict_SetItemString (slot -> globals , "__builtins__" , builtins );
423-
424- /* Create erlang module in this subinterpreter */
425- if (create_erlang_module () >= 0 ) {
426- /* Register ReactorBuffer and PyBuffer */
427- ReactorBuffer_register_with_reactor ();
428- PyBuffer_register_with_module ();
429-
430- /* Import erlang module into globals */
431- PyObject * erlang_module = PyImport_ImportModule ("erlang" );
432- if (erlang_module != NULL ) {
433- PyDict_SetItemString (slot -> globals , "erlang" , erlang_module );
434- Py_DECREF (erlang_module );
435- }
436- }
437-
438- /* Initialize event loop for this subinterpreter */
439- init_subinterpreter_event_loop (NULL );
440-
441- slot -> initialized = true;
442- atomic_store (& slot -> usage_count , 0 );
443- atomic_store (& slot -> marked_stale , false);
444- slot -> generation = atomic_load (& g_import_generation );
445-
446- /* Swap back to main thread state */
447- PyThreadState_Swap (main_tstate );
448-
449- #ifdef DEBUG
450- fprintf (stderr , "subinterp_pool_reinit_slot: reinitialized slot %d\n" , slot_idx );
451- #endif
452-
453- return 0 ;
454- }
455-
456- void subinterp_pool_release (int slot ) {
457- if (slot < 0 || slot >= g_pool_size ) {
458- return ;
459- }
460-
461- subinterp_slot_t * s = & g_subinterp_pool [slot ];
462- if (!s -> initialized ) {
463- return ;
464- }
465-
466- int prev_count = atomic_fetch_sub (& s -> usage_count , 1 );
467-
468- /* If usage drops to 0 and slot is marked stale, destroy and reinitialize
469- * the subinterpreter. We need the GIL to safely do Python operations. */
470- if (prev_count == 1 && atomic_load (& s -> marked_stale )) {
471- /* Acquire the GIL */
472- PyGILState_STATE gstate = PyGILState_Ensure ();
473-
474- /* Save current thread state and swap to the subinterpreter */
475- PyThreadState * saved = PyThreadState_Swap (s -> tstate );
476-
477- /* Clean up Python objects */
478- Py_XDECREF (s -> module_cache );
479- Py_XDECREF (s -> globals );
480- Py_XDECREF (s -> locals );
481-
482- /* End the interpreter - this frees s->tstate */
483- Py_EndInterpreter (s -> tstate );
484-
485- /* Clear slot state */
486- s -> interp = NULL ;
487- s -> tstate = NULL ;
488- s -> globals = NULL ;
489- s -> locals = NULL ;
490- s -> module_cache = NULL ;
491- s -> initialized = false;
492- atomic_store (& s -> marked_stale , false);
493-
494- /* Swap back to saved thread state (may be NULL if we didn't have one) */
495- PyThreadState_Swap (saved );
496-
497- /* Reinitialize the slot with a fresh subinterpreter */
498- if (subinterp_pool_reinit_slot (slot ) < 0 ) {
499- /* Reinit failed - clear allocation bit so slot is skipped */
500- uint64_t mask = ~(1ULL << slot );
501- atomic_fetch_and (& g_pool_allocation , mask );
502- fprintf (stderr , "subinterp_pool_release: failed to reinit slot %d\n" , slot );
503- }
504- /* If reinit succeeded, slot stays allocated but now has fresh interp */
505-
506- /* Release the GIL */
507- PyGILState_Release (gstate );
508-
509- #ifdef DEBUG
510- fprintf (stderr , "subinterp_pool_release: recycled stale slot %d\n" , slot );
511- #endif
512- }
513- }
514-
515- uint64_t subinterp_pool_flush_generation (void ) {
516- if (!atomic_load (& g_pool_initialized )) {
517- return 0 ;
518- }
519-
520- /* Increment generation */
521- uint64_t new_gen = atomic_fetch_add (& g_import_generation , 1 ) + 1 ;
522-
523- /* Mark all initialized slots as stale */
524- for (int i = 0 ; i < g_pool_size ; i ++ ) {
525- subinterp_slot_t * slot = & g_subinterp_pool [i ];
526- if (slot -> initialized ) {
527- atomic_store (& slot -> marked_stale , true);
528- }
529- }
530-
531- #ifdef DEBUG
532- fprintf (stderr , "subinterp_pool_flush_generation: incremented to %llu, marked all slots stale\n" ,
533- (unsigned long long )new_gen );
534- #endif
535-
536- return new_gen ;
537- }
538-
539- uint64_t subinterp_pool_get_generation (void ) {
540- return atomic_load (& g_import_generation );
541- }
542-
543348#endif /* HAVE_SUBINTERPRETERS */
0 commit comments