Skip to content

Commit 6fd9229

Browse files
committed
Remove auto mode, use worker as default
- Remove auto context mode, worker is now the default - subinterp and owngil must be explicitly requested - This avoids C extension crashes (_decimal) on Python 3.12/3.13 - subinterp requires Python 3.12+, owngil requires Python 3.14+
1 parent 89a7bed commit 6fd9229

8 files changed

Lines changed: 26 additions & 49 deletions

src/erlang_python_sup.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ start_link() ->
3434
init([]) ->
3535
NumContexts = application:get_env(erlang_python, num_contexts,
3636
erlang:system_info(schedulers)),
37-
ContextMode = application:get_env(erlang_python, context_mode, auto),
37+
ContextMode = application:get_env(erlang_python, context_mode, worker),
3838
NumAsyncWorkers = application:get_env(erlang_python, num_async_workers, 2),
3939

4040
%% Default executors: 4 (benchmarked sweet spot for most workloads)

src/py.erl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,8 +1319,7 @@ clear_traces() ->
13191319

13201320
%% @doc Start the process-per-context system with default settings.
13211321
%%
1322-
%% Creates one context per scheduler, using auto mode (subinterp on
1323-
%% Python 3.12+, worker otherwise).
1322+
%% Creates one context per scheduler using worker mode.
13241323
%%
13251324
%% @returns {ok, [Context]} | {error, Reason}
13261325
-spec start_contexts() -> {ok, [pid()]} | {error, term()}.
@@ -1331,7 +1330,7 @@ start_contexts() ->
13311330
%%
13321331
%% Options:
13331332
%% - `contexts' - Number of contexts to create (default: number of schedulers)
1334-
%% - `mode' - Context mode: `auto', `subinterp', or `worker' (default: `auto')
1333+
%% - `mode' - Context mode: `worker', `subinterp', or `owngil' (default: `worker')
13351334
%%
13361335
%% @param Opts Start options
13371336
%% @returns {ok, [Context]} | {error, Reason}

src/py_context.erl

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
%% Exported for py_reactor_context
6363
-export([extend_erlang_module_in_context/1]).
6464

65-
-type context_mode() :: auto | subinterp | worker | owngil.
65+
-type context_mode() :: worker | subinterp | owngil.
6666
-type context() :: pid().
6767

6868
-export_type([context_mode/0, context/0]).
@@ -82,14 +82,12 @@
8282
%% @doc Start a new py_context process.
8383
%%
8484
%% The process creates a Python context based on the mode:
85-
%% - `auto' - Detect best mode (subinterp on Python 3.12+, worker otherwise)
86-
%% - `subinterp' - Create a sub-interpreter with shared GIL (uses pool)
8785
%% - `worker' - Create a thread-state worker (main interpreter namespace)
88-
%% - `owngil' - Create a sub-interpreter with its own GIL (true parallelism)
86+
%% - `subinterp' - Create a sub-interpreter with shared GIL (Python 3.12+)
87+
%% - `owngil' - Create a sub-interpreter with its own GIL (Python 3.14+)
8988
%%
9089
%% The `owngil' mode creates a dedicated pthread for each context, allowing
91-
%% true parallel Python execution. This is useful for CPU-bound workloads.
92-
%% Requires Python 3.12+.
90+
%% true parallel Python execution. Requires Python 3.14+.
9391
%%
9492
%% @param Id Unique identifier for this context
9593
%% @param Mode Context mode
@@ -128,13 +126,13 @@ stop(Ctx) when is_pid(Ctx) ->
128126
%% @doc Create a new context with options map.
129127
%%
130128
%% Options:
131-
%% - `mode' - Context mode (auto | subinterp | worker | owngil), default: auto
129+
%% - `mode' - Context mode (worker | subinterp | owngil), default: worker
132130
%%
133131
%% @param Opts Options map
134132
%% @returns {ok, Pid} | {error, Reason}
135133
-spec new(map()) -> {ok, context()} | {error, term()}.
136134
new(Opts) when is_map(Opts) ->
137-
Mode = maps:get(mode, Opts, auto),
135+
Mode = maps:get(mode, Opts, worker),
138136
Id = erlang:unique_integer([positive]),
139137
start_link(Id, Mode).
140138

@@ -521,19 +519,10 @@ apply_registered_paths(Ref) ->
521519
end.
522520

523521
%% @private
524-
%% Auto mode uses subinterpreters only on Python 3.14+ where C extension
525-
%% global state bugs (e.g., _decimal) are fixed. On Python 3.12/3.13,
526-
%% use worker mode to avoid crashes from C extensions like _decimal.
527-
%% Users can still explicitly use subinterp mode if needed.
528-
create_context(auto) ->
529-
case py_nif:owngil_supported() of
530-
true -> create_context(subinterp);
531-
false -> create_context(worker)
532-
end;
533-
create_context(subinterp) ->
534-
py_nif:context_create(subinterp);
535522
create_context(worker) ->
536523
py_nif:context_create(worker);
524+
create_context(subinterp) ->
525+
py_nif:context_create(subinterp);
537526
create_context(owngil) ->
538527
%% OWN_GIL mode requires Python 3.14+ due to C extension bugs in earlier versions
539528
case py_nif:owngil_supported() of

src/py_context_init.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
%%% {erlang_python, [
3030
%%% {default_pool_size, 4}, % Number of contexts (default: schedulers)
3131
%%% {io_pool_size, 10}, % I/O pool size (default: 10)
32-
%%% {io_pool_mode, worker} % Mode for io pool (default: auto)
32+
%%% {io_pool_mode, worker} % Mode for io pool (default: worker)
3333
%%% ]}.
3434
%%% '''
3535
%%% @private
@@ -49,13 +49,13 @@
4949
start_link(Opts) ->
5050
%% Start default pool
5151
DefaultSize = maps:get(contexts, Opts, erlang:system_info(schedulers)),
52-
DefaultMode = maps:get(mode, Opts, auto),
52+
DefaultMode = maps:get(mode, Opts, worker),
5353

5454
case py_context_router:start_pool(default, DefaultSize, DefaultMode) of
5555
{ok, _DefaultContexts} ->
5656
%% Start I/O pool if configured
5757
IoSize = application:get_env(erlang_python, io_pool_size, 10),
58-
IoMode = application:get_env(erlang_python, io_pool_mode, auto),
58+
IoMode = application:get_env(erlang_python, io_pool_mode, worker),
5959
case py_context_router:start_pool(io, IoSize, IoMode) of
6060
{ok, _IoContexts} ->
6161
%% The contexts are supervised by py_context_sup

src/py_context_router.erl

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@
124124

125125
%% @doc Start the context router with default settings.
126126
%%
127-
%% Creates one context per scheduler, using auto mode (subinterp on
128-
%% Python 3.12+, worker otherwise).
127+
%% Creates one context per scheduler using worker mode.
129128
%%
130129
%% @returns {ok, [Context]} | {error, Reason}
131130
-spec start() -> {ok, [pid()]} | {error, term()}.
@@ -136,14 +135,14 @@ start() ->
136135
%%
137136
%% Options:
138137
%% - `contexts' - Number of contexts to create (default: number of schedulers)
139-
%% - `mode' - Context mode: `auto', `subinterp', or `worker' (default: `auto')
138+
%% - `mode' - Context mode: `worker', `subinterp', or `owngil' (default: `worker')
140139
%%
141140
%% @param Opts Start options
142141
%% @returns {ok, [Context]} | {error, Reason}
143142
-spec start(start_opts()) -> {ok, [pid()]} | {error, term()}.
144143
start(Opts) ->
145144
NumContexts = maps:get(contexts, Opts, erlang:system_info(schedulers)),
146-
Mode = maps:get(mode, Opts, auto),
145+
Mode = maps:get(mode, Opts, worker),
147146
%% Delegate to start_pool for the default pool
148147
start_pool(?DEFAULT_POOL, NumContexts, Mode).
149148

@@ -274,13 +273,13 @@ contexts(Pool) when is_atom(Pool) ->
274273
%% @returns {ok, [Context]} | {error, Reason}
275274
-spec start_pool(pool_name(), pos_integer()) -> {ok, [pid()]} | {error, term()}.
276275
start_pool(Pool, Size) ->
277-
start_pool(Pool, Size, auto).
276+
start_pool(Pool, Size, worker).
278277

279278
%% @doc Start a named pool with given size and mode.
280279
%%
281280
%% @param Pool Pool name (default, io, or custom)
282281
%% @param Size Number of contexts in the pool
283-
%% @param Mode Context mode (auto, subinterp, worker)
282+
%% @param Mode Context mode (worker, subinterp, owngil)
284283
%% @returns {ok, [Context]} | {error, Reason}
285284
-spec start_pool(pool_name(), pos_integer(), py_context:context_mode()) ->
286285
{ok, [pid()]} | {error, term()}.

src/py_context_sup.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ start_link() ->
4444
%% @doc Start a new py_context under this supervisor.
4545
%%
4646
%% @param Id Unique identifier for the context (integer or {Pool, N} tuple)
47-
%% @param Mode Context mode (auto | subinterp | worker)
47+
%% @param Mode Context mode (worker | subinterp | owngil)
4848
%% @returns {ok, Pid} | {error, Reason}
4949
-spec start_context(term(), py_context:context_mode()) ->
5050
{ok, pid()} | {error, term()}.

src/py_reactor_context.erl

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
%% @doc Start a new py_reactor_context process.
8080
%%
8181
%% @param Id Unique identifier for this context
82-
%% @param Mode Context mode (auto, subinterp, worker)
82+
%% @param Mode Context mode (worker, subinterp, owngil)
8383
%% @returns {ok, Pid} | {error, Reason}
8484
-spec start_link(pos_integer(), atom()) -> {ok, pid()} | {error, term()}.
8585
start_link(Id, Mode) ->
@@ -95,7 +95,7 @@ start_link(Id, Mode) ->
9595
%% (useful for setting up protocol factory)
9696
%%
9797
%% @param Id Unique identifier for this context
98-
%% @param Mode Context mode (auto, subinterp, worker)
98+
%% @param Mode Context mode (worker, subinterp, owngil)
9999
%% @param Opts Options map
100100
%% @returns {ok, Pid} | {error, Reason}
101101
-spec start_link(pos_integer(), atom(), map()) -> {ok, pid()} | {error, term()}.
@@ -180,18 +180,8 @@ handoff(Ctx, Fd, ClientInfo) when is_pid(Ctx), is_integer(Fd), is_map(ClientInfo
180180
init(Parent, Id, Mode, Opts) ->
181181
process_flag(trap_exit, true),
182182

183-
%% Determine mode
184-
ActualMode = case Mode of
185-
auto ->
186-
case py_nif:subinterp_supported() of
187-
true -> subinterp;
188-
false -> worker
189-
end;
190-
_ -> Mode
191-
end,
192-
193183
%% Create Python context
194-
case py_nif:context_create(ActualMode) of
184+
case py_nif:context_create(Mode) of
195185
{ok, Ref, _InterpId} ->
196186
%% Set up callback handler
197187
py_nif:context_set_callback_handler(Ref, self()),

test/py_import_SUITE.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ import_applied_to_new_context_test(_Config) ->
398398
ok = py_import:ensure_imported(json),
399399

400400
%% Create a new context
401-
{ok, Ctx} = py_context:new(#{mode => auto}),
401+
{ok, Ctx} = py_context:new(#{mode => worker}),
402402

403403
%% The json module should already be cached in the new context
404404
%% We can verify by calling a function from it
@@ -689,7 +689,7 @@ context_import_in_sys_modules_test(_Config) ->
689689
ok = py_import:clear_imports(),
690690

691691
%% Create a context
692-
{ok, Ctx} = py_context:new(#{mode => auto}),
692+
{ok, Ctx} = py_context:new(#{mode => worker}),
693693

694694
%% Call a function from a module (this imports it)
695695
{ok, _} = py_context:call(Ctx, textwrap, fill, [<<"Hello world this is a test">>, 10], #{}),
@@ -751,7 +751,7 @@ add_path_test(Config) ->
751751
?assertEqual(1, length(Paths)),
752752

753753
%% Create a new context to apply paths
754-
{ok, Ctx} = py_context:new(#{mode => auto}),
754+
{ok, Ctx} = py_context:new(#{mode => worker}),
755755

756756
%% Import and call the sample module
757757
{ok, Greeting} = py_context:call(Ctx, sample_module, greet, [<<"World">>], #{}),

0 commit comments

Comments
 (0)