From 188faf19b9589112ca53fd0f5a1c0b3819dcdf43 Mon Sep 17 00:00:00 2001 From: Daniel Gorin Date: Wed, 27 Nov 2024 15:01:01 +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 | 30 ++--- erts/emulator/beam/erl_process.c | 108 +++++++++++++++-- erts/emulator/test/process_SUITE.erl | 151 +++++++++++++++++++++++- erts/preloaded/ebin/erlang.beam | Bin 40020 -> 40060 bytes erts/preloaded/ebin/erts_internal.beam | Bin 10000 -> 10000 bytes erts/preloaded/src/erlang.erl | 47 +++++++- erts/preloaded/src/erts_internal.erl | 3 +- 11 files changed, 313 insertions(+), 32 deletions(-) diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index c264082e7561..d122cac4ba4e 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -553,6 +553,7 @@ atom parent atom Plus='+' atom PlusPlus='++' atom pause +atom pause_proc_timer atom pending atom pending_driver atom pending_process @@ -615,6 +616,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 c7aa2bff1c78..1b2823351a98 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..3f19a1558e71 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -4992,6 +4992,19 @@ 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; + + ASSERT(msp->ptimer_count == 0); + + 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 +5013,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 +5220,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 0798b8ea650b..754630fd9de4 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,35 @@ 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) { + Eterm res; + res = erts_proc_sig_send_rpc_request(BIF_P, + BIF_ARG_1, + 0, /* no reply */ + sched_pause_proc_timer, + NULL); + if (is_non_value(res)) { + goto noproc; } } } + 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 +9081,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,18 +9128,56 @@ 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(prev_suspend_count >= 0); - ASSERT((mstate & ERTS_MSUSPEND_STATE_COUNTER_MASK) >= 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 (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); } + 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); + } + } + 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 9680eb5836fd..4d884ec49a76 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,10 @@ 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_needs_balanced_resume_procs/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, @@ -131,6 +136,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}, @@ -185,6 +191,11 @@ 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_needs_balanced_resume_procs]}, {otp_7738, [], [otp_7738_waiting, otp_7738_suspended, otp_7738_resume]}, @@ -1775,6 +1786,144 @@ 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_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. + %% Tests erlang:bump_reductions/1. bump_reductions(Config) when is_list(Config) -> erlang:garbage_collect(), @@ -3139,7 +3288,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 aed9bed6838349c5fb5c2d61bf304db0043e8170..7f3e5241be2fe1bd147e95cdbe1815030d3de495 100644 GIT binary patch delta 18578 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 delta 17880 zcma)Dd3+Pa+s}4WsHK&(n>KmM(Ug=nByHHxA}#0AQdpEgp+HsS5|pK|h!znQ2#7~K zRtu}*hzf`Y9w>?j9(dvf9(e1|15oisJiuFczcblFLH~I9u$d>{edc+ddFGktnapng z-kkQ%=CnaWM~#WOTsMuVsT&+0F>}rtE>~uW>+nCf&*hSr@~^`$8=f4#X879SPYr*1 z_?>5T2o$?;0yhkYSS;~8k^4_Jq z_bBfZ%KMb^zM#B4l=nU5?WMf^ly`vg{-L~sl<%heV#@DM`5NU9ru>nVKZ^2aQT}Yo zKacVkQ2qmy{~-CY$d^sNAo+^P7a?DN@(mzgJ^99xZ*+oujC`xew}E{3k#86Ic9TDo z{2j<&Nd5r%Yso)^{B;!DNU;Yg_7M5cBmV;OUrqjN$p0w$Ham`B zd_p}wr5?Xfk6$V5qOe5aA_{k-a1(`RQFt+h6BK@n!XHrhLyGt)LKLZ{NMDMKrAT5t zMQ*0ZEfo2HA|Fy&4wdCn*#IgVNM$pq%%HM!scb%#t*5fPsO()TdymRHPAXpEv26dgoSjiM(}bOc4GP;@Fqn<+YvqRS|H2}Lia=#><` znxZyEuc7EFirzxeH56S((Kjf%gQAa8bTdVtr05okZl&l86x~G8hba0EMYmCOJ4JU= zbQeW;Q*;kSzobO;Yl`lr=st@6Owj`r{f(l(Q}j=Y{zcJ$D0+x`x~OLw^-QOpG3q&y zdVWehcT&&Yq#j3V2U4?1^^%%TYFAPVNez-(MrxGQ6G-h(>L5~wlB$y$Cv_yLCz5&+ zsiR3fjns*xP9ilij@0p_Hj;WWsi%`VjnwI+&LGtwwTaYOq?)8QlRA&o3rSr_>Jm~f zA$2LK%SgSF)D}{glWLQ?l2k_OO{A_R^)^y(C-qKJHk@^U! zkCOT}sqd2dL4wp>q<%r_SEPPT>i4AnK_&>MqS!4ITTQXG6kA8J`!)^CT&v^-PzXG{8sOg}E?2tW z<&rMjw7<(QT@t-y7P@St$0db+V4-AMZszkIDYQ@H@Z|#jk%g`RJP8~>3HV9@|I9*H z0p6-gp$Dx3}NypO;|m)r?Y2_EyK; za$j758m~*P@p|0-DIimq^GO#cCu%Rr>;0nsjF0!x*>|fLN{gvGCFjtm-KeI^@^mo zx91KhOk}XoD%iQ*Bk9Kp^Katj>zOQcGvHTMN$()wTLhfNLaPD41~^;5YXp2e3#|qG zdO*@U3iwt5dst{4;5WdXBjDQvoXbMDb6AsfB_Z%T1ny;_JAv;}cgk#vcY+f-<*M*SXImZGS?XdxFwmFi%0hQ>GvJZJtbj+DA$zqnc<}DS zW&|NH;EQ*I8AULosI3`At!5NUdXYbl;dCSZ85Cv|!;Df)^vrk(Oz1A@-E#x5pu`h! z)p^qtEs^;q!u%f0RX6l0^#nWu9_9+NjTveHScrx0g@JFmB)x~0`8K#WjPzvmkn}JM z-N$dh@XxTFAnks%4Y{* zQ#1kry|iA6mKIEP+Bg!OB)zAdb7p>(uCmYrTpw(TVZt=UjledFG7z@1s_!DsJ0)A1ziQz2t67WxfpD6H`1%46>y#jbA zTr)zz+XNhEq3wWofqSHYUls5u7J3cvZou^deqF$$S?CSGp8*~t;2i>PV4*hwe-3zT zY8k&J@Nq2kHt;Wij~DFk2)L1j-UYk|13Fp2?+N%67J46W2=J)_{y@MJSm;B*GXS3^ z;QtADA`5*4_)Ey1B;bz)JUPKap8)>~_!NPED)6Z+v=i{xfKM0jE&)$tq1}ML0X$v6 zp9%O37Wy3Uw}8(S@D~D}!9sfge+SqQ@RtIf$wFTN{vL3XCMwp~0-wb~-*6n3&lc?8 z3fN?!?*Q)w`y2s(FW|FSXfNO&0M8Zh4+1`$h4um72lyNT|0v*Q7WxVBkAUY1_-6s1 z%Mzhqfd2%1zQFej{5%#q0QhIX3k3YDfX`>4-vIvt3oj7x?*hJ%h5q2MD(MRa{HK5y zvCvEv02LLb8dLPbQg zUw@awDBfPd^fVa$I}E>)k5iY$c&=i)8}J{1Edi$s_-dxN1NeeoMmYF5G>D-Wl1 z>;YD`mYN$SeWgEsy`V@ir;S)`jt>xvZMtk=#Z`PJndD(*U zvc;BkZ)_#P;szsvOgTpIMgcU%SqBk}j;#-K-2 zAZP%#WtEctemwqx;t2%mlwoTspe3l~Doh_ZQp@Nq=^s?bKZIjHR01`ME2zdl4Av+f z(f+H)pj3|!JsLz|V=pU;{cYS+^FO9*FyS=}=%aZ2<9`omFa~sjq<>r;{{#d2q;)`_ zI0O0w13J+!eZmJcOaGMVwOkqov@rg&(hVohPn4zTDKeKnnJ4HWd`VdS}2N?fFb9|319W@=pHdcr!sl(9wNctXb;t>5y zrVr(PVB%M5{9Cy>{;eWqV{e?=feJrk8~XKOUk_`qWmL^f?Loihd(dzA9<=>8q7Zz` z?I`(<={lE*Q2Abs@0D>Wed2qA8cbmN@Ra_&{3lb#ADDh3ShJvhUp&6w-aW$C{YOpV zQ=1B)lc27Tv*3R`vfzKQcMZ$S`Z=|leuk%hZoA%pO0A|}`Fb~;_5Ra;toNUfUGKk$ z)wI7legKnuKuj*;=RgqADq8M*5#Mbr9FZE&0Y3Bw{0cO^&<4-YE zGmR&XLTBgv=YJ%QznV>#y?tb-?tg2++QXAMs(pP%Uq|QP|8JfDq;$$$r*Ln*r2q3D zIu9P3tPXMa8m?+nQu^ZJ%d|o@r3x%l(Ru=EYGJoAv3?{>lne>s(shiHmh!KGJreh` zf$hy`o-#DaNQa+mBW63fvDTxS849joAG2LW%D-9#s>c|Sjf{4PZoAIJ2ud=*1SW=cngl#XoZ$cBy%ZFP(j3Bu@rThJj`xDIcY zHtZQF8Q(@pmQa$-l{6gB^hUmBprm6;Nlr>h7bxihB{>yY^_o_JpHqip$KJC0RM>4LgJsZb#=_tU}mv3hd}VbBHDn8DJ`sk=xoMFugs> zqdY`16rb5iUTqf0TKIULt!v}})`2Sb3z`K!ZH1Yaxy|%ShBsmsC_`f4!zMD1?-gZH z{EWPQvPt-H{;=r}cQo@QBVTal8AD8;eZ!b;*{6!7onrQJSGfJ)u=kBA%_PCnmF z=hk;t3jEy0PQ*Vu^F(8G;Z7USmFW}sDudGsMFBgF7ce8h^obm=xTy z#`>Jyg&9mOhu(JKg#3Yg3C@*_pf>&J-372>jvd}z0O&53LkVoc3<=xpm1BE^Cc}sU znmoPQES3A3RmeiqU2-oF1I&3lJnP*HlRr`%`W!Y2OxK_0@#)qVs z9UI>*yO-bWAsH3KKP&Ai<4aHL?X2w+m_GGHOhg~1Pu8bPM%9VcRWb|z| z2gp)(AmG8?UZiEiYxWbBBP~_xra|Yx)VYk&59cywfBTt1lwtH2F*E=b&^KZr)2DNR z0m&HDY}P=a7HIQRD}O;s`nr{YBC zRY|03gzeyeS0$sS*&Jr?YV>zc<>|CqvBx=6EZCIJ+W)uCAt{~1xz37IB{YWoht9fV zDxon{#O5#*wSw`6?#u{IMnu*y z2>gCDN7)6ZbPL6^v?CpjN^_)^hGjUjjekeBG6%h4*6Nww#C5}Yqg8V(&uRWKB7e_H zr8Iu@WQ^hYtbu31ng-ErY?gLZ_GJ2OC>hkt94GVNw8l7u?#T%M9J3J_YrJHP_bd^= zq3gV|y>@be)MyVswYczP^y78s6sDWpih9X7HI-r}^Auxm#B;9+N9U9|Vlb(kGT}&0 znIv+`#8j+IM666~n^R6p&D&I-Q^dR>U7YqGIc4IpIc1WVx5>6KA((p>G!N)yPLYL6 zrbNus?VBe=5;(~bvv@kw&*Ed5#`L)ui-IL3j&Ss|MHW4S>E{6UBB7qi^yZPS47hv- z)8_%q11Jt~^m7I8Os3E0Fhn$oM0}orXEA*NhXax!&TsVd0arGZxw43#Voj+W zEPD}uvgXBBYB9D_HPz;Pq|f=O9Xj8@Ma+4@UJ-M?(&+VQ#X%nN{2bA2#<=DIw|_d# ztMM=AYC#zxF`ql(B@p$JYW1AUE)^Fg{jT3aXvP~<^|XccNt||AY$%9 zrZ44vVe7)|`oPg!zq)XzU=X!%5z{Y1>8_nQq(gef5S+NAW^2*m-I%!qJ3z`xq#`AO zNJ+HW0Gf+ak+PI;#%828fX3o~Z^n$oF`F@CiHH=OF^OQfi0RAV{XgLSWjLu9$J^$` zIIm{<#qegGlSMD)S@aT~MH9v)Vvsn9x&(vl32oue1?D#^IqZzd(<3gVwv@}n2ys)%XHnN7KPxL{ZwO`PG^72A&w z%uX0rGyO8I9WK5Gp>WjZn(4{I+HJ^b@tG^2eL1u*Z>xQItM+RpW4YgKk&G3@KW(A? zS|o;*{85K_J!<21sEyZ2#&sfRTyL+M9FSJpPfqSM`v#_84nw-Y4eTg4F#QVnZj{}NX9z9d8=gHM*Q=3q3(`|c_&wg zrzysr|9)&@tY_183(5=O_q+b>_p9Oe;lhLsFyU^Wc`r=32PWLp)`WXnO}I}o?(v&< zOUAv#KQ{;y?u(fB+x@2obMF_v-pKT8pb*b&j0fzqr{;}sfsPY}j)$S+L7({$bZml- zO>K2-YSr(XBYF zc?&c@?lYf+<|m-}iME=bXx03bWIW+FAD4_LiGOYpnx9&2K7Cj-uRl-oM~rx8RadOo zFHX)$;H=4p2}PLQXWP#1wV2(L#IRq0^5=Z!Rw#cS%Aaqm{P|YpFG|Mqe)BoW*h>8K z1)==Ki1`xVLOM^$_%oKw9GPVlVebn6zJzH{NXE-dzm8h~AnBAJaFYXAA@VQ8j;pc@Ln-*GtAv z;z%k@vHfRyihpMMgPaTUe67t^=6J*YW|$Z&5ccnVG*3#24f4wEjFLrdZhd(kMFZ_W;8 zz9}Vf?&hkq*Uj}N(wP149adXu!{k7?uXZ;7n#J^I@nb5P&GhGZi}QwOFvrP`On)90_VY+dPsGZRd*NPA zYMwO-^RU$&uw;AbjDSx`_gMX1QZkq6cx)Q(H&;q3RaU;ec}C~Ni|)A=zU1*e3O{qL zQPhf%$NZ{}B9#d3dabUaqBPYYOn)$&P6AJTSnrIp+4u^SAfHw^dVAyzZcW&N7LxCXur zhWlED@NZ}Mw*dYnDM=A4x3M9^KQ(0KjDz>3de4md6-`QZg0Ldahc?Ffn7-Yzpqh$+9I*m$q>m0A zV)~mxBPJ*Zkr1fCF;>JXLfG-Un0+Z(R8vtOj9A_HBnk}k*v+%1<>7GuxtLs!>|3Hj z8r8$>=qx1*yu!Dmq12AgD$II|n^%MVIn&>^7tA^#!3R-{Nt3l{JS@92{T&3;K#!Cx zIlSyjnf@*oSr2ECl{R@(&ol8B1m5S+-%GvEkqj~Yee@lKzC9vVScGmk6}n9S!0od7 zXhkR@O*n|j@Ko?n)2wb~;EpgI55&WR;IOhPD=H6`Ya3Quk=nW80THX*9yL49UC#9X z*~aX^6je$_E3F8(wI|c@z&t!?ij-8VtXObxa9VQ*EA|Ygw9~X@5i2Gy^8}XEO{=KT znuG18Sm+vySiNL8E@D+GnN5{eWiZQAuhqJ>3XJ1pjIqDn)69=?qk0|T;X%^Ls_btx$m%1q3v!>DBYUU)$?kbjf zjcFB_^aPl`o6Gdndu~&F0k^kKk!8M-OyWG}Gx%??OG@_P9k@@cnEpAint+tV$<7zZ zj0mSr{AR@Ji@X}H)iTfJjD2zSV>;d*2oHgg{j012@;W)8R9XWXl^wV_0B;qw3s{4s zBu;d`PoGlaF4h(6t{*k9q%NBhq7MDK4F~} z9%W4kkG4*glBc2waS0P@pbPD3C`i1Wh#JJ(Ni|9fZztC%OL#k_Mw!dosWr+J-kx5g z)bnr&?!9$us$*kD8#Tmg&FR9nLA4bcU3if$&9RR9e%WP{gsy zZ%{Y^3Y)5|IgUaz*dO`WW0|}Z&Q`5iQgRko&c#J6vm$(2DqYkB191EA@bMY;HRt$d z&gM)h2C&L0Z6=x~DLJRon&~LTISk%i2~UL5xvAY9KUNu*!5_5#31Lk&?toI`R$&D% z$j#h}b5!eWDS0+ZZw1r;vO6{xN`Kouo7KW|P>})svnX9dX=|zAk4a(F!A0rnn#N#@ ztu^PJ-l8?iEqphbY=#EDDVfJ~y!aBHtV+pqxy*iwb-r&(c#5?kJk>fce7ZGXO3u#; zhyy*@i%Nx1VulOGUC0bcWG8nEgeo=OmLN0I?EB|+8McON;d$u1fA!>Xh@S4FdIr!l z-LVs0gEd~Yf*I}XQ|5)s7W_*@`;>@`qeRSwh~uEVq~QF2b;wM0=wR=g*DY}YGw||C zcp7|uVU?9|CVH_m(Tll@7irc)DY-B;hl?vRw_^?=K3iC*v;=u>yHH9lI+o%1V;B~v z_!5=Y1fjrB&84p?$S=Q7{lbBg>;=d(Xp#86H@C36>MDqycLwlGjC$ zwNy$jtF)FlvMy#u4n&;^QI}L%mvT`R)z-yWA{Pe(yf{@r7+%ta?RUuvGjiRkb*`3Z z6?zGTURr5g>`1$e849G$KpZ5iEDII4B3Nx*RnZq0wkxjMnhFF*t#uWG!?3Ol&$O-x zH(8fU$;-LBo%3is9)s~-7{4_2Ybaf{lCbZJBRqD~{0@nu?_PQ2E{x+6^8Y2_s&YG(Lg?JUT?rp&TaslUovq1>v#>J{)oOE7?uV-2=cWX{yWxje!m}Oy6{y1SlOM|Ga`VHpm88eR41(j5np?q4Tmz>qPx+P^1xMp6 zDy^%b#Kv_kGdgkezYKbmo--)9bNr2HW+_=z<<|;Qo~n z>jtb>={{#>ZfN_PbR(85^I5Bq3~!YC;!^pW)e-APrLh?A5aYq2f>)_Ex0mk@jIJ2U zHCQGa8{I8AK^{+D{P46^!fi!2`Yk3UR}ufbi5Z1lI}Ewm+0_{Vd-#Hq$twZf!i*x^ z#jm^MYNQA}nG94^S?lpyTWv$NwZ67SuAf#TPigLtZIQ>ildqt)s$eE_^Po`PQxB^UyB9bv~{Oa9xONp>?~IypIzZaG8hHt{b+?Vw_uFca zpO{f%UweL$5BG0pM(LvTHES9xT5M!V6_$kTSI;k1aPJOg^bq%^;T|)>cDoBo3xMC* z7b6X54Sen6>-tLT7Tk{5!!8IU?qUXiS!phOg^kEPsc1wP+~fSX3ok-Um-G2oByV`o3 z|K?G{mmuvllv+ z3k%cxaxM1Sg-YTnjNzHEwPjj!mS+n8mC=A1=!Y42s;v>vLO?CjP(#Fet|4<~N^HZl zng*9ZYCVCub-`&o=V9ibl9JD*ID*(;xEdh6wxRV$2Bi0IBRwI;_lNgWmythSZp|Mrr}76g28wjyZOQRsnIjyK0s~y} z(XlW}O1|v3UXqfp5dYl9j6u+zL_losWo_dDfl{W0FGoyhPTXTx6Xf|$weZGQ@>OOG zMvp6C?`sk3bz55O>-&0JL#5;!KI=`GxdUeIIC9q63aAllN2|>>_L#+0iPs=&hu?Zb zO1??_^DSo7LdBJsZgF&J4B@9w$##z?{bDKka^ZUHQgE{%Et z?_$Piz{>%Pqf29qfInkK11!Y(XHpzp8e;|g1vAD04glW6Pc)730{)U2jhs6HDPIZv zWPyLpj8ix};BN$cs(`;`27ixZ1>o-lEZ*Zte$R}F;PwOFE8t0j`v+!B27EVQ@pnwd z6aoLpjH!U}q%JA`j>$NEq$lB$l0P$J8id=x#UC;m(*^r}W}E@|TEOBDnT#_9{3|nN z0KN{e_(LYc5b*EJm)3jK*vM|IG~kba!|PqlI2*7Qkk(5AK1aZ5%xDID17KOe^91Z>#<_qQ;B*1c7jQdf zoCo+u!0iRR0B}NDpTUgtfv@sN>%|`>85aolOlDjN_$IK6KT0wd3OI`yivZsYIJ@=G zpZ^`o2E1w%)N-)m7vt#%4h-Bgi>o(u4ci}s-9H#q${n&qImpxvNb9C|0{YaIF$&{sS3 zN1!c-{t)z44*fpb?hgGf@GBkqZP3XCzHnvR(JXQ3`#~>u=nbHUIrK>mjjwJU^iUAo zxH{0d1YHAqpu>+pikB)=v<$bq&PwsfaJy@+LnpY~EARzI$iF)@zRJIX#u*M5D#O)+ z<~%=wM$8EOd(aWijQ@K;he2~5?ztWg&u-8mhlhL4@vh802Lq9Y(1E+IyVHTYuGpdB zIw=U6^KjR7b8zmuB8Ps(p*hd9paa0UL612!AHV~k3myC}&|N`u{#(C?BBeDQ(_ z|Ku}4D(7Z3aAawxF>5ga< zuQ^{b(Z1@?P$)V>K^l(M4jO|HeK(`QXoW$K zI5d~}5NMhEpLcjre8C6kjc9-g{65gY1bwf#o#MG0*dbh8@p`leaq+%)pykVptSBJg` z?Ezfepe1PcJ2cNJzu@A=A$SFy;DRo|m!EO*4hzu!}t&pBv+aA-uk zYp+8i+FghXVbE-c<}#Z=e}{|H2zJ-E4vk<>xV~||Oh@~*L!XWoK_xm&LHngcPeMBb z7dQGev>}H+7405}J{c_!LauB)+Rq(&EZWZ;dJNj#f=;;7>Ot%hUtDP;(ekLpzqAqf z_!JixbRt@0EJ5pNKgPw47>brhHs=|F_B~wOy#vr9Vex-y{qQjb7w4%)`wlMNw-2ZJ z{NrC*Z+yJP3H*;aH17~`Xnv~)K6c>Zc%egc{yflc;BtfJxr5W(h#a)9sd(}sT%kE`c7f&V;~o^!c2WnObm_WuDbF0cv! diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam index 928de46229dfe8b2929154d4f9eb60142827861f..26e3dffa64b6bbbc308c3ccf56326a52b2c44863 100644 GIT binary patch delta 193 zcmV~$%`1Ze0KnnAj>>(9-L@M^cHS;lgja3Nh3vNZ6=iEgBOjyoi^J>2g$pMRnvW7? z2Zxnf4y4>U@-KLvD2&4E>7K0#CdrX!mN^!HMV45mLX8#HSZ9Mxw%K8ieGWL}m=jJp z;GjberyMbD#!<75 gIqsC91!tVMXvwk_tJYk#?ye1+YUI&P5cDVi0S0zijsO4v delta 193 zcmV~$K`Vm+0D$549d~ecaMMmC$(760WNcY>vFtXyQKk`@nwjF|;Ja~|3nwl#t2H$T z2c_gdYIlzOhCNT13RCy#NM(j3^JG{g#|o>gvCalXN^G&iE@k$qa6pYCPC4U(OX^&4 z%?-EQ@j!z|nzU&1M28oidF73FKKSH|Z@T>O%OC$n42(MHfc*|R?1-b|jyd6^ai>f; g?W}Vyn03j#1(z+lX~}K(ta|9NC!QNNf}k(f1LVP3Gynhq diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 3c950c0a2f53..c6d850833b02 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -467,7 +467,7 @@ A list of binaries. This datatype is useful to use together with -export([process_flag/3, process_info/1, processes/0, purge_module/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]). @@ -5471,6 +5471,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 #{ group => 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 @@ -5479,6 +5494,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. @@ -5491,14 +5511,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 """ @@ -5850,6 +5881,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 @@ -5896,7 +5932,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) -> diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index e0aaf5ee6af8..10e6204a041c 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -837,7 +837,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).