Skip to content

Commit 5656db8

Browse files
committed
Fix reactor_close_fd to use context for subinterpreter support
Added context parameter to reactor_close_fd NIF so it can properly switch to the subinterpreter's thread state before calling Python.
1 parent d100f77 commit 5656db8

5 files changed

Lines changed: 30 additions & 22 deletions

File tree

c_src/py_event_loop.c

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3267,7 +3267,7 @@ ERL_NIF_TERM nif_reactor_init_connection(ErlNifEnv *env, int argc,
32673267
}
32683268

32693269
/**
3270-
* reactor_close_fd(FdRef) -> ok | {error, Reason}
3270+
* reactor_close_fd(ContextRef, FdRef) -> ok | {error, Reason}
32713271
*
32723272
* Close an FD and clean up the protocol handler.
32733273
* Calls Python's erlang_reactor.close_connection(fd) if registered.
@@ -3276,8 +3276,13 @@ ERL_NIF_TERM nif_reactor_close_fd(ErlNifEnv *env, int argc,
32763276
const ERL_NIF_TERM argv[]) {
32773277
(void)argc;
32783278

3279+
py_context_t *ctx;
3280+
if (!enif_get_resource(env, argv[0], PY_CONTEXT_RESOURCE_TYPE, (void **)&ctx)) {
3281+
return make_error(env, "invalid_context");
3282+
}
3283+
32793284
fd_resource_t *fd_res;
3280-
if (!enif_get_resource(env, argv[0], FD_RESOURCE_TYPE, (void **)&fd_res)) {
3285+
if (!enif_get_resource(env, argv[1], FD_RESOURCE_TYPE, (void **)&fd_res)) {
32813286
return make_error(env, "invalid_fd_ref");
32823287
}
32833288

@@ -3293,20 +3298,21 @@ ERL_NIF_TERM nif_reactor_close_fd(ErlNifEnv *env, int argc,
32933298

32943299
/* Call Python to clean up protocol handler */
32953300
if (fd >= 0) {
3296-
gil_guard_t guard = gil_acquire();
3297-
3298-
PyObject *reactor_module = PyImport_ImportModule("erlang.reactor");
3299-
if (reactor_module != NULL) {
3300-
PyObject *result = PyObject_CallMethod(reactor_module,
3301-
"close_connection", "i", fd);
3302-
Py_XDECREF(result);
3303-
Py_DECREF(reactor_module);
3304-
PyErr_Clear(); /* Ignore errors during cleanup */
3305-
} else {
3306-
PyErr_Clear();
3307-
}
3301+
py_context_guard_t guard = py_context_acquire(ctx);
3302+
if (guard.acquired) {
3303+
PyObject *reactor_module = PyImport_ImportModule("erlang.reactor");
3304+
if (reactor_module != NULL) {
3305+
PyObject *result = PyObject_CallMethod(reactor_module,
3306+
"close_connection", "i", fd);
3307+
Py_XDECREF(result);
3308+
Py_DECREF(reactor_module);
3309+
PyErr_Clear(); /* Ignore errors during cleanup */
3310+
} else {
3311+
PyErr_Clear();
3312+
}
33083313

3309-
gil_release(guard);
3314+
py_context_release(&guard);
3315+
}
33103316
}
33113317

33123318
/* Take ownership for cleanup */

c_src/py_event_loop.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,7 @@ ERL_NIF_TERM nif_reactor_init_connection(ErlNifEnv *env, int argc,
862862
/**
863863
* @brief Close FD and cleanup Python protocol
864864
*
865-
* NIF: reactor_close_fd(FdRef) -> ok | {error, Reason}
865+
* NIF: reactor_close_fd(ContextRef, FdRef) -> ok | {error, Reason}
866866
*/
867867
ERL_NIF_TERM nif_reactor_close_fd(ErlNifEnv *env, int argc,
868868
const ERL_NIF_TERM argv[]);

c_src/py_nif.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3876,7 +3876,7 @@ static ErlNifFunc nif_funcs[] = {
38763876
{"reactor_on_read_ready", 2, nif_reactor_on_read_ready, ERL_NIF_DIRTY_JOB_CPU_BOUND},
38773877
{"reactor_on_write_ready", 2, nif_reactor_on_write_ready, ERL_NIF_DIRTY_JOB_CPU_BOUND},
38783878
{"reactor_init_connection", 3, nif_reactor_init_connection, ERL_NIF_DIRTY_JOB_CPU_BOUND},
3879-
{"reactor_close_fd", 1, nif_reactor_close_fd, 0}
3879+
{"reactor_close_fd", 2, nif_reactor_close_fd, 0}
38803880
};
38813881

38823882
ERL_NIF_INIT(py_nif, nif_funcs, load, NULL, upgrade, unload)

src/py_nif.erl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@
188188
reactor_on_read_ready/2,
189189
reactor_on_write_ready/2,
190190
reactor_init_connection/3,
191-
reactor_close_fd/1
191+
reactor_close_fd/2
192192
]).
193193

194194
-on_load(load_nif/0).
@@ -1524,8 +1524,9 @@ reactor_init_connection(_ContextRef, _Fd, _ClientInfo) ->
15241524
%% Calls Python's erlang_reactor.close_connection(fd) to clean up
15251525
%% the protocol handler, then closes the FD.
15261526
%%
1527+
%% @param ContextRef Context resource reference
15271528
%% @param FdRef FD resource reference
15281529
%% @returns ok | {error, Reason}
1529-
-spec reactor_close_fd(reference()) -> ok | {error, term()}.
1530-
reactor_close_fd(_FdRef) ->
1530+
-spec reactor_close_fd(reference(), reference()) -> ok | {error, term()}.
1531+
reactor_close_fd(_ContextRef, _FdRef) ->
15311532
?NIF_STUB.

src/py_reactor_context.erl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,12 +468,13 @@ handle_async_write_ready(Fd, State) ->
468468
%% @private
469469
close_connection(Fd, FdRes, State) ->
470470
#state{
471+
ref = Ref,
471472
connections = Conns,
472473
active_connections = Active
473474
} = State,
474475

475476
%% Close via NIF (cleans up Python protocol handler)
476-
py_nif:reactor_close_fd(FdRes),
477+
py_nif:reactor_close_fd(Ref, FdRes),
477478

478479
%% Remove from connections map
479480
NewConns = maps:remove(Fd, Conns),
@@ -489,7 +490,7 @@ cleanup(State) ->
489490

490491
%% Close all connections
491492
maps:foreach(fun(_Fd, #{fd_ref := FdRef}) ->
492-
py_nif:reactor_close_fd(FdRef)
493+
py_nif:reactor_close_fd(Ref, FdRef)
493494
end, Conns),
494495

495496
%% Destroy Python context

0 commit comments

Comments
 (0)