Current section
Files
Jump to
Current section
Files
src/py_subinterp_worker.erl
%% Copyright 2026 Benoit Chesneau
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%% @doc Sub-interpreter Python worker process.
%%%
%%% Each worker has its own Python sub-interpreter with its own GIL,
%%% allowing true parallel execution on Python 3.12+.
%%%
%%% @private
-module(py_subinterp_worker).
-export([
start_link/0,
init/1
]).
%%% ============================================================================
%%% API
%%% ============================================================================
-spec start_link() -> {ok, pid()}.
start_link() ->
Pid = spawn_link(?MODULE, init, [self()]),
receive
{Pid, ready} -> {ok, Pid};
{Pid, {error, Reason}} -> {error, Reason}
after 10000 ->
exit(Pid, kill),
{error, timeout}
end.
%%% ============================================================================
%%% Worker Process
%%% ============================================================================
init(Parent) ->
%% Create sub-interpreter worker with its own GIL
case py_nif:subinterp_worker_new() of
{ok, WorkerRef} ->
Parent ! {self(), ready},
loop(WorkerRef, Parent);
{error, Reason} ->
Parent ! {self(), {error, Reason}}
end.
loop(WorkerRef, Parent) ->
receive
{py_subinterp_request, Request} ->
handle_request(WorkerRef, Request),
loop(WorkerRef, Parent);
shutdown ->
py_nif:subinterp_worker_destroy(WorkerRef),
ok;
_Other ->
loop(WorkerRef, Parent)
end.
%%% ============================================================================
%%% Request Handling
%%% ============================================================================
handle_request(WorkerRef, {call, Ref, Caller, Module, Func, Args, Kwargs}) ->
ModuleBin = to_binary(Module),
FuncBin = to_binary(Func),
Result = py_nif:subinterp_call(WorkerRef, ModuleBin, FuncBin, Args, Kwargs),
send_response(Caller, Ref, Result).
%%% ============================================================================
%%% Internal Functions
%%% ============================================================================
send_response(Caller, Ref, Result) ->
py_util:send_response(Caller, Ref, Result).
to_binary(Term) ->
py_util:to_binary(Term).