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

openSUSE Build Service is sponsored by