File 8442-pause_proc_timer-2-n-Pause-proc-timer-on-suspend-via.patch of Package erlang
From 8dda7ef6cba65696fb6e7d677758b2f1fd41b7c6 Mon Sep 17 00:00:00 2001
From: Daniel Gorin <danielgo@meta.com>
Date: Wed, 5 Mar 2025 18:03:02 +0000
Subject: [PATCH 2/2] [pause_proc_timer][2/n] Pause proc timer on suspend via
suspend_process/2
The suspend_process/2 BIF will now pause the process timer until the process
has been resumed via the resume_process/1 BIF.
We add testcase for this functionality
Co-authored-by: Rickard Green <rickard@erlang.org>
---
erts/emulator/beam/erl_hl_timer.c | 16 +++----
erts/emulator/beam/erl_proc_sig_queue.c | 30 +++++++------
erts/emulator/beam/erl_process.c | 8 ++++
erts/emulator/test/process_SUITE.erl | 57 +++++++++++++++++++++++++
4 files changed, 88 insertions(+), 23 deletions(-)
diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c
index c4a574055c..3cf6fe633f 100644
--- a/erts/emulator/beam/erl_hl_timer.c
+++ b/erts/emulator/beam/erl_hl_timer.c
@@ -209,7 +209,7 @@ typedef union {
typedef struct {
ErtsTmrHead head; /* NEED to be first! */
Sint64 time_left_in_msec;
- int count;
+ Sint count;
} ErtsPausedProcTimer;
typedef ErtsTimer *(*ErtsCreateTimerFunc)(ErtsSchedulerData *esdp,
@@ -2779,8 +2779,7 @@ erts_pause_proc_timer(Process *c_p)
{
ErtsPausedProcTimer *pptmr;
- ERTS_LC_ASSERT((ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_STATUS)
- & erts_proc_lc_my_proc_locks(c_p));
+ ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p));
pptmr = create_paused_proc_timer(c_p);
if (!pptmr) {
@@ -2798,19 +2797,18 @@ erts_resume_paused_proc_timer(Process *c_p)
erts_aint_t timer;
int resumed_timer = 0;
- ERTS_LC_ASSERT((ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_STATUS)
- & erts_proc_lc_my_proc_locks(c_p));
+ ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p));
- timer = erts_atomic_xchg_nob(&c_p->common.timer, ERTS_PTMR_NONE);
+ timer = erts_atomic_read_nob(&c_p->common.timer);
- ASSERT(timer != ERTS_PTMR_TIMEDOUT);
-
- if (timer != ERTS_PTMR_NONE) {
+ if (timer != ERTS_PTMR_NONE && timer != ERTS_PTMR_TIMEDOUT) {
UWord tmo = 0;
ErtsPausedProcTimer *pptmr = (ErtsPausedProcTimer *)timer;
ASSERT(pptmr->head.roflgs & ERTS_TMR_ROFLG_PAUSED);
+ erts_atomic_set_nob(&c_p->common.timer, ERTS_PTMR_NONE);
+
pptmr->count -= 1;
if (pptmr->count == 0) {
if (pptmr->time_left_in_msec > 0) {
diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
index cac0e55364..33157c5287 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.c
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -5381,6 +5381,18 @@ 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;
+
+ erts_pause_proc_timer(c_p);
+ 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)
{
@@ -5389,14 +5401,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 {
@@ -5602,12 +5608,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);
@@ -6536,6 +6537,7 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
&msp->state, ~ERTS_MSUSPEND_STATE_FLG_ACTIVE);
if (mstate & ERTS_MSUSPEND_STATE_FLG_ACTIVE) {
erts_resume(c_p, ERTS_PROC_LOCK_MAIN);
+ erts_resume_paused_proc_timer(c_p);
}
break;
}
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index cae1fdb663..0de90b896b 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -8981,6 +8981,13 @@ erts_internal_suspend_process_2(BIF_ALIST_2)
mstate = erts_atomic_inc_read_relb(&msp->state);
ASSERT(suspend || (mstate & ERTS_MSUSPEND_STATE_COUNTER_MASK) > 1);
+
+ if ((mstate & ERTS_MSUSPEND_STATE_COUNTER_MASK) == ERTS_AINT_T_MAX) {
+ ASSERT(!suspend);
+ erts_atomic_dec_nob(&msp->state);
+ BIF_RET(am_system_limit);
+ }
+
sync = !async & !suspend & !(mstate & ERTS_MSUSPEND_STATE_FLG_ACTIVE);
suspend = !!suspend; /* ensure 0|1 */
res = am_true;
@@ -9036,6 +9043,7 @@ erts_internal_suspend_process_2(BIF_ALIST_2)
else {
send_sig = !suspend_process(BIF_P, rp);
if (!send_sig) {
+ erts_pause_proc_timer(rp);
erts_monitor_list_insert(&ERTS_P_LT_MONITORS(rp), &mdp->u.target);
erts_atomic_read_bor_relb(&msp->state,
ERTS_MSUSPEND_STATE_FLG_ACTIVE);
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index becb2c983d..b8f22cc86b 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,7 @@
process_info_self_msgq_len/1,
process_info_self_msgq_len_messages/1,
process_info_self_msgq_len_more/1,
+ suspend_process_pausing_proc_timer/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, process_info_messages/1,
@@ -132,6 +134,7 @@ all() ->
dist_spawn_arg_list_mixup,
otp_6237,
{group, process_info_bif},
+ {group, suspend_process_bif},
{group, processes_bif},
{group, otp_7738}, garb_other_running,
{group, system_task},
@@ -187,6 +190,8 @@ groups() ->
process_info_self_msgq_len,
process_info_self_msgq_len_messages,
process_info_self_msgq_len_more]},
+ {suspend_process_bif, [],
+ [suspend_process_pausing_proc_timer]},
{otp_7738, [],
[otp_7738_waiting, otp_7738_suspended,
otp_7738_resume]},
@@ -1777,7 +1782,58 @@ pi_sig_spam_test(SpamFun, PITest, PICheckRes) ->
after
_ = erlang:system_flag(schedulers_online, SO)
end.
-
+
+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_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),
+ timer:sleep(5_000),
+ true = erlang:resume_process(Pid),
+ AfterResume(Pid),
+ timer:sleep(1_000),
+ Pid ! go,
+
+ WaitForSync(),
+ EnsureWaiting(),
+
+ BeforeSuspend(Pid),
+ true = erlang:suspend_process(Pid),
+ true = erlang:resume_process(Pid),
+ AfterResume(Pid),
+ WaitForSync(),
+ ok.
%% Tests erlang:bump_reductions/1.
bump_reductions(Config) when is_list(Config) ->
--
2.43.0