summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.c98
-rw-r--r--erts/emulator/beam/erl_process.h2
2 files changed, 100 insertions, 0 deletions
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,
@@ -5312,6 +5332,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 *),
void *arg)
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