Current section

Files

Jump to
erlang_python test py_context_async_env_SUITE.erl
Raw

test/py_context_async_env_SUITE.erl

%%% @doc Pin the async-with-env dispatch path.
%%%
%%% v3.0 introduced an async dispatch path for call / eval / exec that
%%% returns {enqueued, RequestId} from the NIF and lets the Erlang side
%%% wait in a normal receive. The env-bearing variants
%%% (py_context:call/5, eval/5 with EnvRef, exec/3) used to take a
%%% blocking sync dispatch with a 30-second pthread_cond_timedwait,
%%% returning {error, worker_timeout} for long-running Python while
%%% the worker kept going.
%%%
%%% These cases verify the env path now uses the async dispatch and
%%% completes correctly.
-module(py_context_async_env_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([
all/0,
init_per_suite/1,
end_per_suite/1
]).
-export([
async_env_call_returns_correct_result/1,
env_call_does_not_dispatch_timeout/1
]).
all() ->
[
async_env_call_returns_correct_result,
env_call_does_not_dispatch_timeout
].
init_per_suite(Config) ->
{ok, _} = application:ensure_all_started(erlang_python),
{ok, _} = py:start_contexts(),
Config.
end_per_suite(_Config) ->
ok = application:stop(erlang_python),
ok.
async_env_call_returns_correct_result(_Config) ->
%% py:call/3 wraps an EnvRef under the hood, so a successful
%% round-trip proves the new context_call_with_env_async path is
%% wired and the worker delivers a {py_result, _, _} for it.
{ok, 4.0} = py:call(math, sqrt, [16]),
{ok, 5.0} = py:call(math, sqrt, [25]),
ok.
env_call_does_not_dispatch_timeout(_Config) ->
%% Have the Python side block for 1 second. Under the old sync
%% dispatch this exercised the 30-second pthread_cond_timedwait;
%% now it's an Erlang-side receive on {py_result, _, _} so latency
%% should track wall-clock and never produce {error, worker_timeout}.
Ctx = py:context(1),
EnvRef = py:get_local_env(Ctx),
ok = py_context:exec(Ctx, <<
"import time\n"
"def _slow_round(x):\n"
" time.sleep(1.0)\n"
" return x * 2\n"
>>, EnvRef),
Start = erlang:monotonic_time(millisecond),
{ok, 14} = py_context:call(Ctx, '__main__', '_slow_round', [7], #{},
infinity, EnvRef),
Elapsed = erlang:monotonic_time(millisecond) - Start,
ct:pal("env-async call elapsed: ~p ms", [Elapsed]),
true = Elapsed >= 900,
true = Elapsed < 5000,
ok.