From 640ea44ed59296df32b3f466e260103ccd00fbe3 Mon Sep 17 00:00:00 2001 From: Daniel Gorin Date: Tue, 21 Jan 2025 12:10:23 +0000 Subject: [PATCH] [pause_proc_timer][2/n] Add pause_proc_timer option to suspend_process/2 We add a new `pause_proc_timer` option to the `erlang:suspend_process/2` BIF. When given, the process is not only suspended, but its proc timer, if set, will be paused. This means that if the paused process is waiting on a `receive`, it will not timeout even if suspended for long. Each time pause_proc_timer is given, a counter is bumped in the suspend-process monitor. In order to decrease it, the (new) BIF `erlang:resume_process/2` needs to be called with the option `resume_proc_timer`. When the count reaches zero, the timer is resumed (even though the process may still be suspended). We add testcases for this functionality --- erts/emulator/beam/atom.names | 2 + erts/emulator/beam/bif.tab | 2 +- erts/emulator/beam/erl_monitor_link.c | 1 + erts/emulator/beam/erl_monitor_link.h | 1 + erts/emulator/beam/erl_proc_sig_queue.c | 28 ++-- erts/emulator/beam/erl_process.c | 104 ++++++++++-- erts/emulator/test/process_SUITE.erl | 202 +++++++++++++++++++++++- erts/preloaded/ebin/erlang.beam | Bin 40368 -> 40404 bytes erts/preloaded/ebin/erts_internal.beam | Bin 10376 -> 10376 bytes erts/preloaded/src/erlang.erl | 201 +++++++++++++---------- erts/preloaded/src/erts_internal.erl | 3 +- 11 files changed, 435 insertions(+), 109 deletions(-) diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 56b8562ebb26..d8b1ca5e98c1 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -558,6 +558,7 @@ atom parent atom Plus='+' atom PlusPlus='++' atom pause +atom pause_proc_timer atom pending atom pending_driver atom pending_process @@ -620,6 +621,7 @@ atom reset atom reset_seq_trace atom restart atom resume +atom resume_proc_timer atom return_from atom return_to atom return_to_trace diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 653e41a2c05a..f208b035a97c 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -220,7 +220,7 @@ bif erlang:seq_trace_info/1 bif erlang:seq_trace_print/1 bif erlang:seq_trace_print/2 bif erts_internal:suspend_process/2 -bif erlang:resume_process/1 +bif erlang:resume_process/2 bif erts_internal:process_display/2 bif erlang:bump_reductions/1 diff --git a/erts/emulator/beam/erl_monitor_link.c b/erts/emulator/beam/erl_monitor_link.c index 59506b10510c..2d53dead491e 100644 --- a/erts/emulator/beam/erl_monitor_link.c +++ b/erts/emulator/beam/erl_monitor_link.c @@ -1048,6 +1048,7 @@ erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name, msp->next = NULL; erts_atomic_init_relb(&msp->state, 0); + msp->ptimer_count = 0; erts_atomic32_init_nob(&mdp->refc, 2); break; } diff --git a/erts/emulator/beam/erl_monitor_link.h b/erts/emulator/beam/erl_monitor_link.h index d099800c9780..574da11e2b17 100644 --- a/erts/emulator/beam/erl_monitor_link.h +++ b/erts/emulator/beam/erl_monitor_link.h @@ -733,6 +733,7 @@ struct ErtsMonitorSuspend__ { ErtsMonitorData md; /* origin = suspender; target = suspendee */ ErtsMonitorSuspend *next; erts_atomic_t state; + int ptimer_count; }; #define ERTS_MSUSPEND_STATE_FLG_ACTIVE ((erts_aint_t) (((Uint) 1) << (sizeof(Uint)*8 - 1))) #define ERTS_MSUSPEND_STATE_COUNTER_MASK (~ERTS_MSUSPEND_STATE_FLG_ACTIVE) diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index ea0fa38848bd..b303f76cdc15 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -4992,6 +4992,17 @@ handle_process_info(Process *c_p, ErtsSigRecvTracing *tracing, return ((int) reds)*4 + 8; } +static void +activate_suspend_monitor(Process *c_p, ErtsMonitorSuspend *msp) +{ + erts_aint_t mstate; + + mstate = erts_atomic_read_bor_acqb(&msp->state, + ERTS_MSUSPEND_STATE_FLG_ACTIVE); + ASSERT(!(mstate & ERTS_MSUSPEND_STATE_FLG_ACTIVE)); (void) mstate; + erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); +} + static void handle_suspend(Process *c_p, ErtsMonitor *mon, int *yieldp) { @@ -5000,14 +5011,8 @@ handle_suspend(Process *c_p, ErtsMonitor *mon, int *yieldp) ASSERT(mon->type == ERTS_MON_TYPE_SUSPEND); if (!(state & ERTS_PSFLG_DIRTY_RUNNING)) { - ErtsMonitorSuspend *msp; - erts_aint_t mstate; - - msp = (ErtsMonitorSuspend *) erts_monitor_to_data(mon); - mstate = erts_atomic_read_bor_acqb(&msp->state, - ERTS_MSUSPEND_STATE_FLG_ACTIVE); - ASSERT(!(mstate & ERTS_MSUSPEND_STATE_FLG_ACTIVE)); (void) mstate; - erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); + ErtsMonitorSuspend *msp = (ErtsMonitorSuspend *) erts_monitor_to_data(mon); + activate_suspend_monitor(c_p, msp); *yieldp = !0; } else { @@ -5213,12 +5218,7 @@ erts_proc_sig_handle_pending_suspend(Process *c_p) msp->next = NULL; if (!(state & ERTS_PSFLG_EXITING) && erts_monitor_is_in_table(&msp->md.u.target)) { - erts_aint_t mstate; - - mstate = erts_atomic_read_bor_acqb(&msp->state, - ERTS_MSUSPEND_STATE_FLG_ACTIVE); - ASSERT(!(mstate & ERTS_MSUSPEND_STATE_FLG_ACTIVE)); (void) mstate; - erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL); + activate_suspend_monitor(c_p, msp); } erts_monitor_release(&msp->md.u.target); diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index fbf72b801857..aa7a58b6742f 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -8896,6 +8896,11 @@ erts_start_schedulers(void) } } +static Eterm +sched_pause_proc_timer(Process *c_p, void *vst, int *redsp, ErlHeapFragment **bp); +static Eterm +sched_resume_paused_proc_timer(Process *c_p, void *vst, int *redsp, ErlHeapFragment **bp); + BIF_RETTYPE erts_internal_suspend_process_2(BIF_ALIST_2) { @@ -8906,6 +8911,7 @@ erts_internal_suspend_process_2(BIF_ALIST_2) int sync = 0; int async = 0; int unless_suspending = 0; + int pause_proc_timer = 0; erts_aint_t mstate; ErtsMonitorSuspend *msp; ErtsMonitorData *mdp; @@ -8930,6 +8936,9 @@ erts_internal_suspend_process_2(BIF_ALIST_2) case am_asynchronous: async = 1; break; + case am_pause_proc_timer: + pause_proc_timer = 1; + break; default: { if (is_tuple_arity(arg, 2)) { Eterm *tp = tuple_val(arg); @@ -9029,15 +9038,31 @@ erts_internal_suspend_process_2(BIF_ALIST_2) sync = !async; } else { - noproc: - erts_monitor_tree_delete(&ERTS_P_MONITORS(BIF_P), &mdp->origin); - erts_monitor_release_both(mdp); - if (!async) - res = am_badarg; + goto noproc; } } } + if (pause_proc_timer) { + int proc_timer_already_paused = msp->ptimer_count++; + + if (!proc_timer_already_paused) { + erts_proc_sig_send_rpc_request(BIF_P, + BIF_ARG_1, + 0, /* no reply */ + sched_pause_proc_timer, + NULL); + } + } + + while(0) { + noproc: + erts_monitor_tree_delete(&ERTS_P_MONITORS(BIF_P), &mdp->origin); + erts_monitor_release_both(mdp); + if (!async) + res = am_badarg; + } + if (sync) { ASSERT(is_non_value(reply_tag)); reply_res = res; @@ -9052,22 +9077,43 @@ erts_internal_suspend_process_2(BIF_ALIST_2) } /* - * The erlang:resume_process/1 BIF + * The erlang:resume_process/2 BIF */ BIF_RETTYPE -resume_process_1(BIF_ALIST_1) +resume_process_2(BIF_ALIST_2) { ErtsMonitor *mon; ErtsMonitorSuspend *msp; erts_aint_t mstate; - + int prev_suspend_count; + int resume_proc_timer = 0; + if (BIF_P->common.id == BIF_ARG_1) BIF_ERROR(BIF_P, BADARG); if (!is_internal_pid(BIF_ARG_1)) BIF_ERROR(BIF_P, BADARG); + if (is_not_nil(BIF_ARG_2)) { + /* Parse option list */ + Eterm arg = BIF_ARG_2; + while (is_list(arg)) { + Eterm *lp = list_val(arg); + arg = CAR(lp); + switch (arg) { + case am_resume_proc_timer: + resume_proc_timer = 1; + break; + default: + BIF_ERROR(BIF_P, BADARG); + } + arg = CDR(lp); + } + if (is_not_nil(arg)) + BIF_ERROR(BIF_P, BADARG); + } + mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(BIF_P), BIF_ARG_1); if (!mon) { @@ -9078,11 +9124,32 @@ resume_process_1(BIF_ALIST_1) ASSERT(mon->type == ERTS_MON_TYPE_SUSPEND); msp = (ErtsMonitorSuspend *) erts_monitor_to_data(mon); + if (resume_proc_timer && msp->ptimer_count == 0) { + BIF_ERROR(BIF_P, BADARG); + } + mstate = erts_atomic_dec_read_relb(&msp->state); + prev_suspend_count = mstate & ERTS_MSUSPEND_STATE_COUNTER_MASK; - ASSERT((mstate & ERTS_MSUSPEND_STATE_COUNTER_MASK) >= 0); + ASSERT(prev_suspend_count >= 0); + + if (msp->ptimer_count == prev_suspend_count + 1 && !resume_proc_timer) { + erts_atomic_inc_nob(&msp->state); + BIF_ERROR(BIF_P, BADARG); + } - if ((mstate & ERTS_MSUSPEND_STATE_COUNTER_MASK) == 0) { + if (resume_proc_timer) { + int needs_to_resume_timer = --msp->ptimer_count == 0; + if (needs_to_resume_timer) { + erts_proc_sig_send_rpc_request(BIF_P, + BIF_ARG_1, + 0, /* no reply */ + sched_resume_paused_proc_timer, + NULL); + } + } + + if (prev_suspend_count == 0) { erts_monitor_tree_delete(&ERTS_P_MONITORS(BIF_P), mon); erts_proc_sig_send_demonitor(&BIF_P->common, BIF_P->common.id, 0, mon); } @@ -9090,6 +9157,23 @@ resume_process_1(BIF_ALIST_1) BIF_RET(am_true); } +static Eterm +sched_pause_proc_timer(Process *c_p, void *vst, int *redsp, ErlHeapFragment **bp) +{ + erts_pause_proc_timer(c_p); + *redsp = 1; + return THE_NON_VALUE; +} + +static Eterm +sched_resume_paused_proc_timer(Process *c_p, void *vst, int *redsp, ErlHeapFragment **bp) +{ + erts_resume_paused_proc_timer(c_p); + *redsp = 1; + return THE_NON_VALUE; +} + + BIF_RETTYPE erts_internal_is_process_executing_dirty_1(BIF_ALIST_1) { diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index becb2c983d6e..cd4c2d964d5d 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -24,6 +24,7 @@ %% exit/1 %% exit/2 %% process_info/1,2 +%% suspend_process/2 (partially) %% register/2 (partially) -include_lib("stdlib/include/assert.hrl"). @@ -56,6 +57,11 @@ process_info_msgq_len_no_very_long_delay/1, process_info_dict_lookup/1, process_info_label/1, + suspend_process_pausing_proc_timer/1, + suspend_process_pausing_proc_timer_after_suspended/1, + resume_process_resuming_proc_timer_can_resume_timer_early/1, + suspend_process_pausing_proc_timer_needs_balanced_resume_procs/1, + suspend_process_pausing_proc_timer_stress_test/1, bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1, otp_4725/1, dist_unlink_ack_exit_leak/1, bad_register/1, garbage_collect/1, otp_6237/1, @@ -132,6 +138,7 @@ all() -> otp_6237, {group, spawn_request}, {group, process_info_bif}, + {group, suspend_process_bif}, {group, processes_bif}, {group, otp_7738}, garb_other_running, {group, system_task}, @@ -187,6 +194,12 @@ groups() -> process_info_msgq_len_no_very_long_delay, process_info_dict_lookup, process_info_label]}, + {suspend_process_bif, [], + [suspend_process_pausing_proc_timer, + suspend_process_pausing_proc_timer_after_suspended, + resume_process_resuming_proc_timer_can_resume_timer_early, + suspend_process_pausing_proc_timer_needs_balanced_resume_procs, + suspend_process_pausing_proc_timer_stress_test]}, {otp_7738, [], [otp_7738_waiting, otp_7738_suspended, otp_7738_resume]}, @@ -1777,6 +1790,193 @@ proc_dict_helper() -> end, proc_dict_helper(). +suspend_process_pausing_proc_timer(_Config) -> + BeforeSuspend = fun(_Pid) -> ok end, + AfterResume = fun(_Pid) -> ok end, + suspend_process_pausing_proc_timer_aux(BeforeSuspend, AfterResume), + ok. + +suspend_process_pausing_proc_timer_after_suspended(_Config) -> + % We suspend the process once before using pause_proc_timer + BeforeSuspend = fun(Pid) -> true = erlang:suspend_process(Pid) end, + AfterResume = fun(Pid) -> true = erlang:resume_process(Pid) end, + suspend_process_pausing_proc_timer_aux(BeforeSuspend, AfterResume), + ok. + +suspend_process_pausing_proc_timer_aux(BeforeSuspend, AfterResume) -> + TcProc = self(), + Pid = erlang:spawn_link( + fun() -> + TcProc ! {sync, self()}, + receive go -> ok + after 2_000 -> exit(timer_not_paused) + end, + TcProc ! {sync, self()}, + receive _ -> error(unexpected) + after 2_000 -> ok + end, + TcProc ! {sync, self()} + end + ), + + WaitForSync = fun () -> + receive {sync, Pid} -> ok + after 10_000 -> error(timeout) + end + end, + EnsureWaiting = fun() -> + wait_until(fun () -> process_info(Pid, status) == {status, waiting} end) + end, + + WaitForSync(), + EnsureWaiting(), + + BeforeSuspend(Pid), + true = erlang:suspend_process(Pid, [pause_proc_timer]), + timer:sleep(5_000), + true = erlang:resume_process(Pid, [resume_proc_timer]), + AfterResume(Pid), + timer:sleep(1_000), + Pid ! go, + + WaitForSync(), + EnsureWaiting(), + + BeforeSuspend(Pid), + true = erlang:suspend_process(Pid, [pause_proc_timer]), + true = erlang:resume_process(Pid, [resume_proc_timer]), + AfterResume(Pid), + WaitForSync(), + ok. + +resume_process_resuming_proc_timer_can_resume_timer_early(_Config) -> + TcProc = self(), + Pid = erlang:spawn_link( + fun() -> + TcProc ! {sync, self()}, + receive go -> error(received_go) + after 2_000 -> TcProc ! {sync, self()} + end + end + ), + + WaitForSync = fun () -> + receive {sync, Pid} -> ok + after 10_000 -> error(timeout) + end + end, + EnsureWaiting = fun() -> + wait_until(fun () -> process_info(Pid, status) == {status, waiting} end) + end, + + + WaitForSync(), + EnsureWaiting(), + + % Suspend twice, but pause the proc timer only once + true = erlang:suspend_process(Pid), + true = erlang:suspend_process(Pid, [pause_proc_timer]), + + % Pid is suspended so will not process it just yet + Pid ! go, + + % At this point the process is still suspended but the timer is running again + true = erlang:resume_process(Pid, [resume_proc_timer]), + ?assertEqual({status, suspended}, process_info(Pid, status)), + + % The timer must have expired by now + timer:sleep(5_000), + + true = erlang:resume_process(Pid), + WaitForSync(), + + ok. + +suspend_process_pausing_proc_timer_needs_balanced_resume_procs(_Config) -> + Pid = erlang:spawn_link(timer, sleep, [infinity]), + + true = erlang:suspend_process(Pid), + ?assertEqual({status, suspended}, process_info(Pid, status)), + + % No pause_proc_timer so far, so fail + ?assertMatch({'EXIT', {badarg, _}}, + catch erlang:resume_process(Pid, [resume_proc_timer])), + ?assertEqual({status, suspended}, process_info(Pid, status)), + + + true = erlang:suspend_process(Pid), + true = erlang:suspend_process(Pid, [pause_proc_timer]), + true = erlang:suspend_process(Pid, [pause_proc_timer]), + + % It is ok to do out-of-order resumes; here one that doesn't resume the timer + true = erlang:resume_process(Pid), + ?assertEqual({status, suspended}, process_info(Pid, status)), + + % Do more resumes, in any order + true = erlang:resume_process(Pid, [resume_proc_timer]), + true = erlang:resume_process(Pid), + ?assertEqual({status, suspended}, process_info(Pid, status)), + + % Only one suspend remains, and it used pause_proc_timer, so fail if not resuming timer + ?assertMatch({'EXIT', {badarg, _}}, + catch erlang:resume_process(Pid)), + ?assertEqual({status, suspended}, process_info(Pid, status)), + + % Final resume, now running + true = erlang:resume_process(Pid, [resume_proc_timer]), + ?assertEqual({status, running}, process_info(Pid, status)), + + ok. + +suspend_process_pausing_proc_timer_stress_test(_Config) -> + TestCaseProc = self(), + RunTest = fun() -> + TestRunner = self(), + ActionDoneRef = erlang:make_ref(), + P = erlang:spawn(fun() -> + receive + _ -> + TestRunner ! {recv, ActionDoneRef} + after + 0 -> TestRunner ! {timeout, ActionDoneRef} + end + end), + + try erlang:suspend_process(P, [pause_proc_timer]) of + true -> ok + catch + error:badarg -> + false = erlang:is_process_alive(P), + ok; + error:exited -> + ok + end, + + P ! 'wake up!', + + try erlang:resume_process(P, [resume_proc_timer]) of + true -> ok + catch + error:badarg -> + false = erlang:is_process_alive(P), + ok + end, + + receive + {_, ActionDoneRef} -> TestCaseProc ! {ok, TestRunner} + end + end, + + NumInstances = 100_000, + TestRunners = [erlang:spawn(RunTest) || _ <- lists:seq(1, NumInstances)], + [ + receive {ok, P} -> ok + after 2_000 -> error({timeout, waiting_for_runner}) + end + || P <- TestRunners + ], + ok. + %% Tests erlang:bump_reductions/1. bump_reductions(Config) when is_list(Config) -> erlang:garbage_collect(), @@ -3160,7 +3360,7 @@ spawn_huge_arglist_test(Local, Node, ArgList) -> {'DOWN', R2, process, Pid2, Reason2} -> ArgList = Reason2 end, - + {Pid3, R3} = case Local of true -> spawn_opt(?MODULE, huge_arglist_child, ArgList, [monitor]); diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam index 2315ec378407aa3bc81f90fa042e2233e70b8263..797941d3e94f846f930660a05eaf6c3b2de37d31 100644 GIT binary patch delta 17961 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 B-j4tP delta 18411 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 delta 207 zcmV~$&ntrg0D$4B?I?f14m;VABzL=5k@*h9h358-vf8wXO!@V_5eLd@yI2h;TS+KQ zad1#d4y4>U@)vlXFbIQIx}!Wsnh7SEVU~FoSR_ZD0!2!!uu7SAHrQl`kbMrQQsaa= zXPk3ElPj*d;g&nv-1EQ_k393jD{s8>!6#pQ^TRKHMvU5J!niSe?X%y3q=OEbbl8+5 pjyi7Ol+$LM6SEdvvgopFZn^EQdmeh^u@&o{+O%ckvb(d?^AENIV^aVC diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 304f6bdcc7db..b02b6391ee5b 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -469,7 +469,7 @@ A list of binaries. This datatype is useful to use together with -export([processes_iterator/0, processes_next/1]). -export([put/2, raise/3, read_timer/1, read_timer/2, ref_to_list/1, register/2]). -export([send_after/3, send_after/4, start_timer/3, start_timer/4]). --export([registered/0, resume_process/1, round/1, self/0]). +-export([registered/0, resume_process/1, resume_process/2, round/1, self/0]). -export([seq_trace/2, seq_trace_print/1, seq_trace_print/2, setnode/2]). -export([setnode/3, size/1, spawn/3, spawn_link/3, split_binary/2]). -export([suspend_process/2, system_monitor/0]). @@ -800,7 +800,7 @@ atom_to_list(_Atom) -> -doc """ Extracts the part of the binary described by `PosLen`. -Negative length can be used to extract bytes at the end of a binary. +Negative length can be used to extract bytes at the end of a binary. For example: @@ -2189,12 +2189,12 @@ dt_put_tag(_IoData) -> TagData :: term(). dt_restore_tag(_TagData) -> erlang:nif_error(undefined). - + %% dt_spread_tag/1 -doc false. -spec dt_spread_tag(boolean()) -> TagData when TagData :: term(). -dt_spread_tag(_Bool) -> +dt_spread_tag(_Bool) -> erlang:nif_error(undefined). %% erase/0 @@ -3762,7 +3762,7 @@ list_to_existing_atom(_String) -> %% list_to_float/1 -doc """ -Returns the float whose text representation is `String`. +Returns the float whose text representation is `String`. For example: @@ -3946,7 +3946,7 @@ identifier. String :: string(). list_to_port(_String) -> erlang:nif_error(undefined). - + %% list_to_ref/1 -doc """ Returns a reference whose text representation is a `String`. @@ -5539,6 +5539,21 @@ registered() -> %% resume_process/1 -doc """ Decreases the suspend count on the process identified by `Suspendee`. +Equivalent to calling [`erlang:resume_process(Suspendee, [])`](`resume_process/2`). + +> #### Warning {: .warning } +> +> This BIF is intended for debugging only. +""". +-doc #{ category => processes }. +-spec resume_process(Suspendee) -> true when + Suspendee :: pid(). +resume_process(Suspendee) -> + resume_process(Suspendee, []). + +%% resume_process/2 +-doc """ +Decreases the suspend count on the process identified by `Suspendee`. `Suspendee` is previously to have been suspended through [`erlang:suspend_process/2`](`suspend_process/2`) or @@ -5547,6 +5562,11 @@ Decreases the suspend count on the process identified by `Suspendee`. reaches zero, `Suspendee` is resumed, that is, its state is changed from suspended into the state it had before it was suspended. +Options (`Opt`s): + +- **`resume_proc_timer`** - Decrease the paused time count. If it reaches + zero, the timer will be resumed. + > #### Warning {: .warning } > > This BIF is intended for debugging only. @@ -5559,14 +5579,25 @@ Failures: previously increased the suspend count on the process identified by `Suspendee`. +- **`badarg`** - If the `resume_proc_timer` `Opt` is given, but the paused + timer count is already 0; or if it was not given and the paused timer + counte equals the suspended count. Intuitively, the usages of the + `pause_proc_timer` option of [`suspend_process/2`] and `resume_proc_timer` + need to balance out. + - **`badarg`** - If the process identified by `Suspendee` is not alive. + +- **`badarg`** - If `OptList` is not a proper list of valid `Opt`s. """. -doc #{ group => processes }. --spec resume_process(Suspendee) -> true when - Suspendee :: pid(). -resume_process(_Suspendee) -> +-spec resume_process(Suspendee, OptList) -> true when + Suspendee :: pid(), + OptList :: [Opt], + Opt :: resume_proc_timer. +resume_process(_Suspendee, _OptList) -> erlang:nif_error(undefined). + %% round/1 %% Shadowed by erl_bif_types: erlang:round/1 -doc """ @@ -5918,6 +5949,11 @@ Options (`Opt`s): Apart from the reply message, the `{asynchronous, ReplyTag}` option behaves exactly the same as the `asynchronous` option without reply tag. +- **`pause_proc_timer`** - If `Suspendee` is waiting on a message, pause the timer + associated with the `after` clause. The paused timer count is increased, so + a corresponding call to [`resume_process/2`] will need to use the `resume_proc_timer` + option to decrease it. + - **`unless_suspending`** - The process identified by `Suspendee` is suspended unless the calling process already is suspending `Suspendee`. If `unless_suspending` is combined with option `asynchronous`, a suspend request @@ -5964,7 +6000,8 @@ Failures: -spec suspend_process(Suspendee, OptList) -> boolean() when Suspendee :: pid(), OptList :: [Opt], - Opt :: unless_suspending | asynchronous | {asynchronous, term()}. + Opt :: unless_suspending | pause_proc_timer + | asynchronous | {asynchronous, term()}. suspend_process(Suspendee, OptList) -> case case erts_internal:suspend_process(Suspendee, OptList) of Ref when erlang:is_reference(Ref) -> @@ -7390,7 +7427,7 @@ of the flag. process_flag(async_dist, boolean()) ``` {: #process_flag_async_dist } - + Enable or disable _fully asynchronous distributed signaling_ for the calling process. When disabled, which is the default, the process sending a distributed signal will block in the send operation if the buffer for the distribution @@ -7400,7 +7437,7 @@ of the flag. operations of distributed signals will always buffer the signal on the outgoing distribution channel and then immediately return. That is, these send operations will _never_ block the sending process. - + > #### Note {: .info } > > Since no flow control is enforced by the runtime system when `async_dist` @@ -7409,14 +7446,14 @@ of the flag. > Unlimited signaling with `async_dist` enabled in the absence of flow control > will typically cause the sending runtime system to crash on an out of memory > condition. - + Blocking due to disabled `async_dist` can be monitored by [`trace:system()`](`trace:system/3`) using the [`busy_dist_port`](`m:trace#busy_dist_port`) option. Only data buffered by processes which (at the time of sending a signal) have disabled `async_dist` will be counted when determining whether or not an operation should block the caller. - + The `async_dist` flag can also be set on a new process when spawning it using the [`spawn_opt()`](`spawn_opt/4`) BIF with the option [`{async_dist, Enable}`](#spawn_opt_async_dist). The default @@ -7424,192 +7461,192 @@ of the flag. command line argument [`+pad `](erl_cmd.md#%2Bpad) when starting the runtime system. If the `+pad ` command line argument is not passed, the default value of the `async_dist` flag will be `false`. - + You can inspect the state of the `async_dist` process flag of a process by calling [`process_info(Pid, async_dist)`](#process_info_async_dist). - + - ```erlang process_flag(trap_exit, boolean()) ``` {: #process_flag_trap_exit } - + When `trap_exit` is set to `true`, exit signals arriving to a process are converted to `{'EXIT', From, Reason}` messages, which can be received as ordinary messages. If `trap_exit` is set to `false`, the process exits if it receives an exit signal other than `normal` and the exit signal is propagated to its linked processes. Application processes are normally not to trap exits. - + See also `exit/2`. - + - ```erlang process_flag(error_handler, module()) ``` {: #process_flag_error_handler } - + Used by a process to redefine the `m:error_handler` for undefined function calls and undefined registered processes. Use this flag with substantial caution, as code auto-loading depends on the correct operation of the error handling module. - + - ```erlang process_flag(fullsweep_after, non_neg_integer()) ``` - + Changes the maximum number of generational collections before forcing a fullsweep for the calling process. - + - ```erlang process_flag(min_heap_size, non_neg_integer()) ``` {: #process_flag_min_heap_size } - + Changes the minimum heap size for the calling process. - + - ```erlang process_flag(min_bin_vheap_size, non_neg_integer()) ``` - + Changes the minimum binary virtual heap size for the calling process. - + - ```erlang process_flag(max_heap_size, max_heap_size()) ``` {: #process_flag_max_heap_size } - + This flag sets the maximum heap size for the calling process. If `MaxHeapSize` is an integer, the system default values for `kill` and `error_logger` are used. - + For details on how the heap grows, see [Sizing the heap](GarbageCollection.md#sizing-the-heap) in the ERTS internal documentation. - + - **`size`** - The maximum size in words of the process. If set to zero, the heap size limit is disabled. `badarg` is be thrown if the value is smaller than [`min_heap_size`](#process_flag_min_heap_size). The size check is only done when a garbage collection is triggered. - + `size` is the entire heap of the process when garbage collection is triggered. This includes all generational heaps, the process stack, any [messages that are considered to be part of the heap](#process_flag_message_queue_data), and any extra memory that the garbage collector needs during collection. - + `size` is the same as can be retrieved using [`erlang:process_info(Pid, total_heap_size)`](#process_info_total_heap_size), or by adding `heap_block_size`, `old_heap_block_size` and `mbuf_size` from [`erlang:process_info(Pid, garbage_collection_info)`](#process_info_garbage_collection_info). - + - **`kill`** - When set to `true`, the runtime system sends an untrappable exit signal with reason `kill` to the process if the maximum heap size is reached. The garbage collection that triggered the `kill` is not completed, instead the process exits as soon as possible. When set to `false`, no exit signal is sent to the process, instead it continues executing. - + If `kill` is not defined in the map, the system default will be used. The default system default is `true`. It can be changed by either option [\+hmaxk](erl_cmd.md#%2Bhmaxk) in [erl](erl_cmd.md), or [`erlang:system_flag(max_heap_size, MaxHeapSize)`](#system_flag_max_heap_size). - + - **`error_logger`** - When set to `true`, the runtime system logs an error event via `m:logger`, containing details about the process when the maximum heap size is reached. One log event is sent each time the limit is reached. - + If `error_logger` is not defined in the map, the system default is used. The default system default is `true`. It can be changed by either the option [\+hmaxel](erl_cmd.md#%2Bhmaxel) int [erl](erl_cmd.md), or [`erlang:system_flag(max_heap_size, MaxHeapSize)`](#system_flag_max_heap_size). - + - **`include_shared_binaries`** - When set to `true`, off-heap binaries are included in the total sum compared against the `size` limit. Off-heap binaries are typically larger binaries that may be shared between processes. The size of a shared binary is included by all processes that are referring it. Also, the entire size of a large binary may be included even if only a smaller part of it is referred by the process. - + If `include_shared_binaries` is not defined in the map, the system default is used. The default system default is `false`. It can be changed by either the option [\+hmaxib](erl_cmd.md#%2Bhmaxib) in [erl](erl_cmd.md), or [`erlang:system_flag(max_heap_size, MaxHeapSize)`](#system_flag_max_heap_size). - + The heap size of a process is quite hard to predict, especially the amount of memory that is used during the garbage collection. When contemplating using this option, it is recommended to first run it in production with `kill` set to `false` and inspect the log events to see what the normal peak sizes of the processes in the system is and then tune the value accordingly. - + - ```erlang process_flag(message_queue_data, message_queue_data()) ``` {: #process_flag_message_queue_data } - + Determines how messages in the message queue are stored, as follows: - + - **`off_heap`** - _All_ messages in the message queue will be stored outside the process heap. This implies that _no_ messages in the message queue will be part of a garbage collection of the process. - + - **`on_heap`** - All messages in the message queue will eventually be placed on the process heap. They can, however, be temporarily stored off the heap. This is how messages have always been stored up until ERTS 8.0. - + The default value of the `message_queue_data` process flag is determined by the command-line argument [`+hmqd`](erl_cmd.md#%2Bhmqd) in [erl](erl_cmd.md). - + If the process may potentially accumulate a large number of messages in its queue it is recommended to set the flag value to `off_heap`. This is due to the fact that the garbage collection of a process that has a large number of messages stored on the heap can become extremely expensive and the process can consume large amounts of memory. The performance of the actual message passing is, however, generally better when the flag value is `on_heap`. - + Changing the flag value causes any existing messages to be moved. The move operation is initiated, but not necessarily completed, by the time the function returns. - + - ```erlang process_flag(priority, priority_level()) ``` {: #process_flag_priority } - + Sets the process priority. `Level` is an atom. Four priority levels exist: `low`, `normal`, `high`, and `max`. Default is `normal`. - + > #### Note {: .info } > > Priority level `max` is reserved for internal use in the Erlang runtime > system, and is _not_ to be used by others. - + Internally in each priority level, processes are scheduled in a round robin fashion. - + Execution of processes on priority `normal` and `low` are interleaved. Processes on priority `low` are selected for execution less frequently than processes on priority `normal`. - + When runnable processes on priority `high` exist, no processes on priority `low` or `normal` are selected for execution. Notice however that this does _not_ mean that no processes on priority `low` or `normal` can run when processes are running on priority `high`. When using multiple schedulers, more processes can be running in parallel than processes on priority `high`. That is, a `low` and a `high` priority process can execute at the same time. - + When runnable processes on priority `max` exist, no processes on priority `low`, `normal`, or `high` are selected for execution. As with priority `high`, processes on lower priorities can execute in parallel with processes on priority `max`. - + Scheduling is pre-emptive. Regardless of priority, a process is pre-empted when it has consumed more than a certain number of reductions since the last time it was selected for execution. - + > #### Note {: .info } > > Do not depend on the scheduling to remain exactly as it is today. Scheduling > is likely to be changed in a future release to use available processor cores > better. - + There is _no_ automatic mechanism for avoiding priority inversion, such as priority inheritance or priority ceilings. When using priorities, take this into account and handle such scenarios by yourself. - + Making calls from a `high` priority process into code that you has no control over can cause the `high` priority process to wait for a process with lower priority. That is, effectively decreasing the priority of the `high` priority @@ -7617,17 +7654,17 @@ of the flag. code that you have no control over, it can be the case in a future version of it. This can, for example, occur if a `high` priority process triggers code loading, as the code server runs on priority `normal`. - + Other priorities than `normal` are normally not needed. When other priorities are used, use them with care, _especially_ priority `high`. A process on priority `high` is only to perform work for short periods. Busy looping for long periods in a `high` priority process causes most likely problems, as important OTP servers run on priority `normal`. - + - ```erlang process_flag(save_calls, 0..10000) ``` - + `N` must be an integer in the interval 0..10000. If `N` > 0, call saving is made active for the process. This means that information about the `N` most recent global function calls, BIF calls, sends, and receives made by the process are @@ -7635,40 +7672,40 @@ of the flag. [`process_info(Pid, last_calls)`](`process_info/2`). A global function call is one in which the module of the function is explicitly mentioned. Only a fixed amount of information is saved, as follows: - + - A tuple `{Module, Function, Arity}` for function calls - The atoms `send`, `'receive'`, and `timeout` for sends and receives (`'receive'` when a message is received and `timeout` when a receive times out) - + If `N` = 0, call saving is disabled for the process, which is the default. Whenever the size of the call saving list is set, its contents are reset. - + - ```erlang process_flag(sensitive, boolean()) ``` - + Sets or clears flag `sensitive` for the current process. When a process has been marked as sensitive by calling [`process_flag(sensitive, true)`](`process_flag/2`), features in the runtime system that can be used for examining the data or inner working of the process are silently disabled. - + Features that are disabled include (but are not limited to) the following: - + - Tracing. Trace flags can still be set for the process, but no trace messages of any kind are generated. (If flag `sensitive` is turned off, trace messages are again generated if any trace flags are set.) - Sequential tracing. The sequential trace token is propagated as usual, but no sequential trace messages are generated. - + `process_info/1,2` cannot be used to read out the message queue or the process dictionary (both are returned as empty lists). - + Stack back-traces cannot be displayed for the process. - + In crash dumps, the stack, messages, and the process dictionary are omitted. - + If `{save_calls,N}` has been set for the process, no function calls are saved to the call saving list. (The call saving list is not cleared. Also, send, receive, and time-out events are still added to the list.) @@ -8746,7 +8783,7 @@ The possible flags are: ActiveTime :: non_neg_integer(), TotalTime :: non_neg_integer(); (total_active_tasks) -> ActiveTasks when - ActiveTasks :: non_neg_integer(); + ActiveTasks :: non_neg_integer(); (total_active_tasks_all) -> ActiveTasks when ActiveTasks :: non_neg_integer(); (total_run_queue_lengths) -> TotalRunQueueLengths when @@ -9358,7 +9395,7 @@ Currently supported options: compression either does or does not produce a smaller result than level 1 compression. -- **`{minor_version, Version}`**(Since R11B-4) +- **`{minor_version, Version}`**(Since R11B-4) The option can be used to control some encoding details. Valid values for `Version` are: @@ -9381,7 +9418,7 @@ Currently supported options: unconditionally encoded using utf8. Erlang/OTP systems as of R16B can decode this representation. -- **`deterministic`**(Since OTP 24.1) +- **`deterministic`**(Since OTP 24.1) This option can be used to ensure that, within the same major release of Erlang/OTP, the same encoded representation is returned for the same term. There is still no guarantee that the encoded representation remains the same @@ -9389,7 +9426,7 @@ Currently supported options: This option cannot be combined with the `local` option. -- **`local`[](){: #term_to_binary_local } **(Since OTP 26.0) +- **`local`[](){: #term_to_binary_local } **(Since OTP 26.0) This option will cause encoding of `Term` to an alternative local version of the external term format which when decoded by the same runtime system instance will produce a term identical to the encoded term even when the node name and/or [creation](#system_info_creation) @@ -10185,7 +10222,7 @@ spawn_monitor(N,M,F,A) when erlang:is_atom(N), {spawn_reply, Ref, error, badopt} -> badarg_with_info([N, M, F, A]); {spawn_reply, Ref, error, noconnection} -> - try + try erlang:spawn_opt(erts_internal,crasher, [N,M,F,A,[monitor], noconnection], @@ -10363,7 +10400,7 @@ spawn_opt(N, M, F, A, O) when erlang:is_atom(N), end; spawn_opt(N,M,F,A,O) -> badarg_with_info([N,M,F,A,O]). - + %% %% spawn_request/1 %% @@ -11610,7 +11647,7 @@ port_info(Port, Item) -> -spec port_set_data(Port, Data) -> 'true' when Port :: port() | atom(), Data :: term(). - + port_set_data(_Port, _Data) -> erlang:nif_error(undefined). @@ -11969,14 +12006,14 @@ integer_to_binary(_I, _Base) -> %% erlang:set_cpu_topology/1 is for internal use only! %% -%% erlang:system_flag(cpu_topology, CpuTopology) traps to +%% erlang:system_flag(cpu_topology, CpuTopology) traps to %% erlang:set_cpu_topology(CpuTopology). -doc false. set_cpu_topology(CpuTopology) -> try format_cpu_topology(erlang:system_flag(internal_cpu_topology, cput_e2i(CpuTopology))) catch - Class:Exception when Class =/= error; Exception =/= internal_error -> + Class:Exception when Class =/= error; Exception =/= internal_error -> badarg_with_info([CpuTopology]) end. @@ -12001,9 +12038,9 @@ cput_e2i(undefined) -> cput_e2i(E) -> rvrs(cput_e2i(E, -1, -1, #cpu{}, 0, cput_e2i_clvl(E, 0), [])). -cput_e2i([], _NId, _PId, _IS, _PLvl, _Lvl, Res) -> +cput_e2i([], _NId, _PId, _IS, _PLvl, _Lvl, Res) -> Res; -cput_e2i([E|Es], NId0, PId, IS, PLvl, Lvl, Res0) -> +cput_e2i([E|Es], NId0, PId, IS, PLvl, Lvl, Res0) -> case cput_e2i(E, NId0, PId, IS, PLvl, Lvl, Res0) of [] -> cput_e2i(Es, NId0, PId, IS, PLvl, Lvl, Res0); @@ -12638,7 +12675,7 @@ gc_info(_Ref, 0, {Colls,Recl}) -> {Colls,Recl,0}; gc_info(Ref, N, {OrigColls,OrigRecl}) -> receive - {Ref, {_,Colls, Recl}} -> + {Ref, {_,Colls, Recl}} -> gc_info(Ref, N-1, {Colls+OrigColls,Recl+OrigRecl}) end. diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 344a3be05f77..626dedf0a3f8 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -841,7 +841,8 @@ gather_carrier_info(_) -> Result :: boolean() | 'badarg' | reference(), Suspendee :: pid(), OptList :: [Opt], - Opt :: unless_suspending | asynchronous | {asynchronous, term()}. + Opt :: unless_suspending | pause_proc_timer + | asynchronous | {asynchronous, term()}. suspend_process(_Suspendee, _OptList) -> erlang:nif_error(undefined).