File 1004-Fix-signal-handling-of-dirty-executing-process.patch of Package erlang

From 294a063233c27d966919bcf50c5a0ac53f78779b Mon Sep 17 00:00:00 2001
From: Rickard Green <rickard@erlang.org>
Date: Fri, 28 May 2021 20:10:26 +0200
Subject: [PATCH 2/2] Fix signal handling of dirty executing process

---
 erts/emulator/beam/erl_proc_sig_queue.c | 98 +++++++++++++++++++++++++
 erts/emulator/beam/erl_process.h        |  2 +
 2 files changed, 100 insertions(+)

diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
index ad5610d817..5f78dee6be 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.c
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -230,6 +230,9 @@ typedef struct {
     ErtsORefThing oref_thing;
 } ErtsProcSigRPC;
 
+static void wait_handle_signals(Process *c_p);
+static void wake_handle_signals(Process *proc);
+
 static int handle_msg_tracing(Process *c_p,
                               ErtsSigRecvTracing *tracing,
                               ErtsMessage ***next_nm_sig);
@@ -3754,6 +3757,12 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
     ErtsMessage *sig, ***next_nm_sig;
     ErtsSigRecvTracing tracing;
 
+    ASSERT(!(c_p->sig_qs.flags & FS_WAIT_HANDLE_SIGS));
+    if (c_p->sig_qs.flags & FS_HANDLING_SIGS)
+        wait_handle_signals(c_p);
+    else
+        c_p->sig_qs.flags |= FS_HANDLING_SIGS;
+
     ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p, 0);
     ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p));
 
@@ -3778,11 +3787,15 @@ erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
     if (!c_p->sig_qs.cont) {
         *statep = state;
         ASSERT(!deferred_fetch);
+        ASSERT(!(c_p->sig_qs.flags & FS_WAIT_HANDLE_SIGS));
+        c_p->sig_qs.flags &= ~FS_HANDLING_SIGS;
         return !0;
     }
 
     if (state & ERTS_PSFLG_EXITING) {
         *statep = state;
+        ASSERT(!(c_p->sig_qs.flags & FS_WAIT_HANDLE_SIGS));
+        c_p->sig_qs.flags &= ~FS_HANDLING_SIGS;
         return 0;
     }
 
@@ -4391,6 +4404,11 @@ stop: {
             *redsp = max_reds;
         }
 
+        if (c_p->sig_qs.flags & FS_WAIT_HANDLE_SIGS)
+            wake_handle_signals(c_p);
+        else
+            c_p->sig_qs.flags &= ~FS_HANDLING_SIGS;
+
         if (deferred_fetch)
             return 0;
         return res;
@@ -5292,6 +5310,8 @@ erts_internal_dirty_process_handle_signals_1(BIF_ALIST_1)
             res = am_noproc;
         else if (!dirty)
             res = am_normal; /* will handle signals itself... */
+        else if (rp->sig_qs.flags & FS_HANDLING_SIGS)
+            res = am_busy;
         else {
             reds = ERTS_BIF_REDS_LEFT(BIF_P);
             done = erts_proc_sig_handle_incoming(rp, &state, &reds,
@@ -5311,6 +5331,84 @@ erts_internal_dirty_process_handle_signals_1(BIF_ALIST_1)
     }
 }
 
+static void
+wait_handle_signals(Process *c_p)
+{
+    /*
+     * Process needs to wait on a dirty process signal
+     * handler before it can handle signals by itself...
+     *
+     * This should be a quite rare event. This only occurs
+     * when all of the following occurs:
+     * * The process is executing dirty and receives a
+     *   signal.
+     * * A dirty process signal handler starts handling
+     *   signals for the process and unlocks the main
+     *   lock while doing so. This can currently only
+     *   occur if handling an 'unlink' signal from a port.
+     * * While the dirty process signal handler is handling
+     *   signals for the process, the process stops executing
+     *   dirty, gets scheduled on a normal scheduler, and
+     *   then tries to handle signals itself.
+     *
+     * If the above happens, the normal sceduler executing
+     * the process will wait here until the dirty process
+     * signal handler is done with the process...
+     */
+    erts_tse_t *event;
+
+    ASSERT(c_p = erts_get_current_process());
+    ASSERT(c_p->scheduler_data);
+    ASSERT(c_p->scheduler_data->aux_work_data.ssi);
+    ASSERT(c_p->scheduler_data->aux_work_data.ssi->event);
+    ASSERT(c_p->sig_qs.flags & FS_HANDLING_SIGS);
+    ASSERT(!(c_p->sig_qs.flags & FS_WAIT_HANDLE_SIGS));
+
+    event = c_p->scheduler_data->aux_work_data.ssi->event;
+    c_p->sig_qs.flags |= FS_WAIT_HANDLE_SIGS;
+    
+    erts_tse_use(event);
+
+    do {
+        ASSERT(c_p->sig_qs.flags & FS_WAIT_HANDLE_SIGS);
+        erts_tse_reset(event);
+        erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+        erts_tse_wait(event);
+        erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+    } while (c_p->sig_qs.flags & FS_HANDLING_SIGS);
+
+    erts_tse_return(event);
+
+    c_p->sig_qs.flags &= ~FS_WAIT_HANDLE_SIGS;
+    c_p->sig_qs.flags |= FS_HANDLING_SIGS;
+}
+
+static void
+wake_handle_signals(Process *proc)
+{
+    /*
+     * Wake scheduler sleeping in wait_handle_signals()
+     * (above)...
+     *
+     * This function should only be called by a dirty process
+     * signal handler process...
+     */
+#ifdef DEBUG
+    Process *c_p = erts_get_current_process();
+    ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(proc));
+    ASSERT(c_p->sig_qs.flags & FS_WAIT_HANDLE_SIGS);
+    ERTS_ASSERT(c_p == erts_dirty_process_signal_handler_max
+                || c_p == erts_dirty_process_signal_handler_high
+                || erts_dirty_process_signal_handler);
+    ASSERT(proc->scheduler_data);
+    ASSERT(proc->scheduler_data->aux_work_data.ssi);
+    ASSERT(proc->scheduler_data->aux_work_data.ssi->event);
+#endif
+
+    proc->sig_qs.flags &= ~FS_HANDLING_SIGS;
+    erts_tse_set(proc->scheduler_data->aux_work_data.ssi->event);
+}
+
 static void
 debug_foreach_sig_heap_frags(ErlHeapFragment *hfrag,
                              void (*oh_func)(ErlOffHeap *, void *),
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 9e5ea73869..b9681b75ac 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1497,6 +1497,8 @@ extern int erts_system_profile_ts_type;
 #define FS_DELAYED_PSIGQS_LEN  (1 << 6) /* Delayed update of sig_qs.len */
 #define FS_HIPE_RECV_LOCKED    (1 << 7) /* HiPE message queue locked */
 #define FS_HIPE_RECV_YIELD     (1 << 8) /* HiPE receive yield */
+#define FS_HANDLING_SIGS       (1 << 9) /* Process is handling signals */
+#define FS_WAIT_HANDLE_SIGS    (1 << 10) /* Process is waiting to handle signals */
 
 /*
  * F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent
-- 
2.26.2

openSUSE Build Service is sponsored by