aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/emulator/beam/beam_emu.c4
-rw-r--r--erts/emulator/beam/erl_alloc.types1
-rw-r--r--erts/emulator/beam/erl_nif.c173
-rw-r--r--erts/emulator/beam/erl_process.c106
-rw-r--r--erts/emulator/beam/erl_process.h2
-rw-r--r--erts/emulator/beam/erl_thr_progress.c8
-rw-r--r--erts/emulator/beam/erl_trace.c14
-rw-r--r--erts/emulator/beam/global.h5
-rw-r--r--erts/emulator/test/dirty_nif_SUITE.erl42
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c27
10 files changed, 293 insertions, 89 deletions
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index ac8dd85b46..073b7e39b6 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -5382,7 +5382,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
ASSERT(!c_p->scheduler_data);
erts_pre_dirty_nif(esdp, &env, c_p,
- (struct erl_module_nif*)I[2], NULL);
+ (struct erl_module_nif*)I[2]);
#ifdef DEBUG
result =
@@ -5391,7 +5391,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
#endif
(*fp)(&env, arity, reg);
- erts_post_nif(&env);
+ erts_post_dirty_nif(&env);
ASSERT(!is_value(result));
ASSERT(c_p->freason == TRAP);
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 1b72a0cb59..971052b947 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -279,6 +279,7 @@ type TRACER_NIF LONG_LIVED SYSTEM tracer_nif
type TRACE_MSG_QUEUE SHORT_LIVED SYSTEM trace_message_queue
type SCHED_ASYNC_JOB SHORT_LIVED SYSTEM async_calls
type DIRTY_START STANDARD PROCESSES dirty_start
+type DIRTY_SL SHORT_LIVED SYSTEM dirty_short_lived
+if threads_no_smp
# Need thread safe allocs, but std_alloc and fix_alloc are not;
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index c6127a4967..ef2fb93106 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -120,8 +120,10 @@ execution_state(ErlNifEnv *env, Process **c_pp, int *schedp)
else {
Process *c_p = env->proc;
- if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC))
- ASSERT(is_scheduler() > 0);
+ if (!(c_p->static_flags & ERTS_STC_FLG_SHADOW_PROC)) {
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
+ }
else {
c_p = env->proc->next;
ASSERT(is_scheduler() < 0);
@@ -208,11 +210,16 @@ void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif,
#endif
}
+static void full_cache_env(ErlNifEnv *env);
+static void cache_env(ErlNifEnv* env);
+static void full_flush_env(ErlNifEnv *env);
+static void flush_env(ErlNifEnv* env);
+
+#ifdef ERTS_DIRTY_SCHEDULERS
void erts_pre_dirty_nif(ErtsSchedulerData *esdp,
- ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif,
- Process* tracee)
+ ErlNifEnv* env, Process* p,
+ struct erl_module_nif* mod_nif)
{
-#ifdef ERTS_DIRTY_SCHEDULERS
Process *sproc;
#ifdef DEBUG
erts_aint32_t state = erts_smp_atomic32_read_nob(&p->state);
@@ -223,7 +230,7 @@ void erts_pre_dirty_nif(ErtsSchedulerData *esdp,
ASSERT(esdp);
#endif
- erts_pre_nif(env, p, mod_nif, tracee);
+ erts_pre_nif(env, p, mod_nif, NULL);
sproc = esdp->dirty_shadow_process;
ASSERT(sproc);
@@ -235,22 +242,10 @@ void erts_pre_dirty_nif(ErtsSchedulerData *esdp,
sproc->next = p;
sproc->common.id = p->common.id;
- sproc->htop = p->htop;
- sproc->stop = p->stop;
- sproc->hend = p->hend;
- sproc->heap = p->heap;
- sproc->abandoned_heap = p->abandoned_heap;
- sproc->heap_sz = p->heap_sz;
- sproc->high_water = p->high_water;
- sproc->old_hend = p->old_hend;
- sproc->old_htop = p->old_htop;
- sproc->old_heap = p->old_heap;
- sproc->mbuf = NULL;
- sproc->mbuf_sz = 0;
- ERTS_INIT_OFF_HEAP(&sproc->off_heap);
env->proc = sproc;
-#endif
+ full_cache_env(env);
}
+#endif
/* Temporary object header, auto-deallocated when NIF returns
* or when independent environment is cleared.
@@ -274,32 +269,37 @@ static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env)
void erts_post_nif(ErlNifEnv* env)
{
erts_unblock_fpe(env->fpe_was_unmasked);
+ full_flush_env(env);
+ free_tmp_objs(env);
+ env->exiting = ERTS_PROC_IS_EXITING(env->proc);
+}
#ifdef ERTS_DIRTY_SCHEDULERS
- if (!(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+void erts_post_dirty_nif(ErlNifEnv* env)
+{
+ Process *c_p;
+ ASSERT(env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC);
+ ASSERT(env->proc->next);
+ erts_unblock_fpe(env->fpe_was_unmasked);
+ full_flush_env(env);
+ free_tmp_objs(env);
+ c_p = env->proc->next;
+ env->exiting = ERTS_PROC_IS_EXITING(c_p);
+ ERTS_VBUMP_ALL_REDS(c_p);
+}
#endif
- {
- ASSERT(is_scheduler() > 0);
- if (env->heap_frag == NULL) {
- ASSERT(env->hp_end == HEAP_LIMIT(env->proc));
- ASSERT(env->hp >= HEAP_TOP(env->proc));
- ASSERT(env->hp <= HEAP_LIMIT(env->proc));
- HEAP_TOP(env->proc) = env->hp;
- }
- else {
- ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
- ASSERT(env->hp_end - env->hp <= env->heap_frag->alloc_size);
- env->heap_frag->used_size = env->hp - env->heap_frag->mem;
- ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);
- }
- env->exiting = ERTS_PROC_IS_EXITING(env->proc);
- }
+
+static void full_flush_env(ErlNifEnv* env)
+{
#ifdef ERTS_DIRTY_SCHEDULERS
- else { /* Dirty nif call using shadow process struct */
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ /* Dirty nif call using shadow process struct */
Process *c_p = env->proc->next;
ASSERT(is_scheduler() < 0);
ASSERT(env->proc->common.id == c_p->common.id);
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
if (!env->heap_frag) {
ASSERT(env->hp_end == HEAP_LIMIT(c_p));
@@ -339,11 +339,48 @@ void erts_post_nif(ErlNifEnv* env)
}
c_p->off_heap.overhead += env->proc->off_heap.overhead;
- env->exiting = ERTS_PROC_IS_EXITING(c_p);
- BUMP_ALL_REDS(c_p);
+ return;
}
#endif
- free_tmp_objs(env);
+
+ flush_env(env);
+}
+
+static void full_cache_env(ErlNifEnv* env)
+{
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ /* Dirty nif call using shadow process struct */
+ Process *sproc = env->proc;
+ Process *c_p = sproc->next;
+ ASSERT(c_p);
+ ASSERT(is_scheduler() < 0);
+ ASSERT(env->proc->common.id == c_p->common.id);
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
+ & ERTS_PROC_LOCK_MAIN);
+
+ sproc->htop = c_p->htop;
+ sproc->stop = c_p->stop;
+ sproc->hend = c_p->hend;
+ sproc->heap = c_p->heap;
+ sproc->abandoned_heap = c_p->abandoned_heap;
+ sproc->heap_sz = c_p->heap_sz;
+ sproc->high_water = c_p->high_water;
+ sproc->old_hend = c_p->old_hend;
+ sproc->old_htop = c_p->old_htop;
+ sproc->old_heap = c_p->old_heap;
+ sproc->mbuf = NULL;
+ sproc->mbuf_sz = 0;
+ ERTS_INIT_OFF_HEAP(&sproc->off_heap);
+
+ env->hp_end = HEAP_LIMIT(c_p);
+ env->hp = HEAP_TOP(c_p);
+ env->heap_frag = NULL;
+ return;
+ }
+#endif
+
+ cache_env(env);
}
/* Flush out our cached heap pointers to allow an ordinary HAlloc
@@ -600,17 +637,32 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (scheduler > 0) { /* Normal scheduler */
rp = erts_proc_lookup(receiver);
- if (c_p == rp)
- rp_locks = ERTS_PROC_LOCK_MAIN;
+ if (!rp)
+ return 0;
}
else {
- if (c_p && ERTS_PROC_IS_EXITING(c_p))
- return 0;
- rp = erts_pid2proc_opt(c_p, 0, receiver, rp_locks,
+ if (c_p) {
+ ASSERT(scheduler < 0); /* Dirty scheduler */
+ if (ERTS_PROC_IS_EXITING(c_p))
+ return 0;
+
+ if (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC) {
+ erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
+ }
+ }
+
+ rp = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
+ receiver, rp_locks,
ERTS_P2P_FLG_INC_REFC);
+ if (!rp) {
+ if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+ return 0;
+ }
}
- if (rp == NULL)
- return 0;
+
+ if (c_p == rp)
+ rp_locks = ERTS_PROC_LOCK_MAIN;
if (menv) {
flush_env(msg_env);
@@ -631,9 +683,9 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
ErlOffHeap *ohp;
Eterm *hp;
if (env && !env->tracee) {
- flush_env(env);
+ full_flush_env(env);
mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
- cache_env(env);
+ full_cache_env(env);
}
else {
erts_aint_t state = erts_smp_atomic32_read_nob(&rp->state);
@@ -656,8 +708,11 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (!env || !env->tracee) {
- if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND))
+ if (c_p && IS_TRACED_FL(c_p, F_TRACE_SEND)) {
+ full_flush_env(env);
trace_send(c_p, receiver, msg);
+ full_cache_env(env);
+ }
}
#ifdef ERTS_SMP
else {
@@ -690,10 +745,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
erts_smp_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) {
if (!msgq) {
-#ifdef ERTS_SMP
- ErtsThrPrgrDelayHandle dhndl;
-#endif
-
msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE,
sizeof(ErlTraceMessageQueue));
msgq->receiver = receiver;
@@ -707,15 +758,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
erts_smp_proc_unlock(t_p, ERTS_PROC_LOCK_TRACE);
-#ifdef ERTS_SMP
- if (!scheduler)
- dhndl = erts_thr_progress_unmanaged_delay();
-#endif
- erts_schedule_flush_trace_messages(t_p->common.id);
-#ifdef ERTS_SMP
- if (!scheduler)
- erts_thr_progress_unmanaged_continue(dhndl);
-#endif
+ erts_schedule_flush_trace_messages(t_p, 0);
} else {
msgq->len++;
*msgq->last = mp;
@@ -740,6 +783,8 @@ done:
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
if (rp_locks & ~lc_locks)
erts_smp_proc_unlock(rp, rp_locks & ~lc_locks);
+ if (c_p && (env->proc->static_flags & ERTS_STC_FLG_SHADOW_PROC))
+ erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
#endif
if (scheduler <= 0)
erts_proc_dec_refc(rp);
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index c1699cb501..c2735b17e1 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -9538,15 +9538,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
p->reds += actual_reds;
-#ifdef ERTS_SMP
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_TRACE);
- if (p->trace_msg_q) {
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE);
- erts_schedule_flush_trace_messages(p->common.id);
- } else
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_TRACE);
-#endif
-
state = erts_smp_atomic32_read_nob(&p->state);
if (IS_TRACED(p)) {
@@ -9566,7 +9557,15 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
}
}
- erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+
+#ifdef ERTS_SMP
+ if (p->trace_msg_q) {
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ erts_schedule_flush_trace_messages(p, 1);
+ erts_smp_proc_lock(p, ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_TRACE);
+ }
+#endif
/* have to re-read state after taking lock */
state = erts_smp_atomic32_read_nob(&p->state);
@@ -9574,9 +9573,11 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
#ifdef ERTS_SMP
if (is_normal_sched && (state & ERTS_PSFLG_PENDING_EXIT))
erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_TRACE
| ERTS_PROC_LOCK_STATUS));
if (p->pending_suspenders)
handle_pending_suspend(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_TRACE
| ERTS_PROC_LOCK_STATUS));
#endif
@@ -9596,7 +9597,9 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
p->scheduler_data = NULL;
#endif
- erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS);
+ erts_smp_proc_unlock(p, (ERTS_PROC_LOCK_MAIN
+ | ERTS_PROC_LOCK_STATUS
+ | ERTS_PROC_LOCK_TRACE));
ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_OTHER);
@@ -10808,16 +10811,95 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type)
}
}
+
void
erts_schedule_complete_off_heap_message_queue_change(Eterm pid)
{
erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ);
}
+#ifdef ERTS_DIRTY_SCHEDULERS
+
+static void
+flush_dirty_trace_messages(void *vpid)
+{
+ Process *proc;
+ Eterm pid;
+#ifdef ARCH_64
+ pid = (Eterm) vpid;
+#else
+ pid = *((Eterm *) vpid);
+ erts_free(ERTS_ALC_T_DIRTY_SL, vpid);
+#endif
+
+ proc = erts_proc_lookup(pid);
+ if (proc)
+ (void) erts_flush_trace_messages(proc, 0);
+}
+
+#endif /* ERTS_DIRTY_SCHEDULERS */
+
void
-erts_schedule_flush_trace_messages(Eterm pid)
+erts_schedule_flush_trace_messages(Process *proc, int force_on_proc)
{
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl;
+#endif
+ Eterm pid = proc->common.id;
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ erts_aint32_t state;
+
+ if (!force_on_proc) {
+ state = erts_smp_atomic32_read_nob(&proc->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ goto sched_flush_dirty;
+ }
+ }
+#endif
+
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
+
erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ);
+
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+
+#ifdef ERTS_DIRTY_SCHEDULERS
+ if (!force_on_proc) {
+ state = erts_smp_atomic32_read_mb(&proc->state);
+ if (state & (ERTS_PSFLG_DIRTY_RUNNING
+ | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
+ void *vargp;
+
+ sched_flush_dirty:
+ /*
+ * We traced 'proc' from another thread than
+ * it is executing on, and it is executing
+ * on a dirty scheduler. It might take a
+ * significant amount of time before it is
+ * scheduled out (where it gets opportunity
+ * to flush messages). We therefore schedule
+ * the flush on the first ordinary scheduler.
+ */
+
+#ifdef ARCH_64
+ vargp = (void *) pid;
+#else
+ {
+ Eterm *argp = erts_alloc(ERTS_ALC_T_DIRTY_SL, sizeof(Eterm));
+ *argp = pid;
+ vargp = (void *) argp;
+ }
+#endif
+ erts_schedule_misc_aux_work(1, flush_dirty_trace_messages, vargp);
+ }
+ }
+#endif
}
static void
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 714e63e986..f1788c3f71 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1767,7 +1767,7 @@ void erts_schedule_thr_prgr_later_cleanup_op(void (*)(void *),
ErtsThrPrgrLaterOp *,
UWord);
void erts_schedule_complete_off_heap_message_queue_change(Eterm pid);
-void erts_schedule_flush_trace_messages(Eterm pid);
+void erts_schedule_flush_trace_messages(Process *proc, int force_on_proc);
int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks);
#if defined(ERTS_SMP) && defined(ERTS_ENABLE_LOCK_CHECK)
diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c
index 21938e7684..26d71f573f 100644
--- a/erts/emulator/beam/erl_thr_progress.c
+++ b/erts/emulator/beam/erl_thr_progress.c
@@ -969,8 +969,10 @@ erts_thr_progress_unmanaged_continue__(ErtsThrPrgrDelayHandle handle)
#ifdef ERTS_ENABLE_LOCK_CHECK
ErtsThrPrgrData *tpd = perhaps_thr_prgr_data(NULL);
ERTS_LC_ASSERT(tpd && tpd->is_delaying);
- tpd->is_delaying = 0;
- return_tmp_thr_prgr_data(tpd);
+ tpd->is_delaying--;
+ ASSERT(tpd->is_delaying >= 0);
+ if (!tpd->is_delaying)
+ return_tmp_thr_prgr_data(tpd);
#endif
ASSERT(!erts_thr_progress_is_managed_thread());
@@ -995,7 +997,7 @@ erts_thr_progress_unmanaged_delay__(void)
#ifdef ERTS_ENABLE_LOCK_CHECK
{
ErtsThrPrgrData *tpd = tmp_thr_prgr_data(NULL);
- tpd->is_delaying = 1;
+ tpd->is_delaying++;
}
#endif
return (ErtsThrPrgrDelayHandle) umrefc_ix;
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index c4396488f5..f4d92564c1 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -813,6 +813,9 @@ trace_send(Process *p, Eterm to, Eterm msg)
ErtsTracerNif *tnif = NULL;
ErtsTracingEvent* te;
Eterm pam_result;
+#ifdef ERTS_SMP
+ ErtsThrPrgrDelayHandle dhndl;
+#endif
ASSERT(ARE_TRACE_FLAGS_ON(p, F_TRACE_SEND));
@@ -837,6 +840,10 @@ trace_send(Process *p, Eterm to, Eterm msg)
} else
pam_result = am_true;
+#ifdef ERTS_SMP
+ dhndl = erts_thr_progress_unmanaged_delay();
+#endif
+
if (is_internal_pid(to)) {
if (!erts_proc_lookup(to))
goto send_to_non_existing_process;
@@ -852,6 +859,11 @@ trace_send(Process *p, Eterm to, Eterm msg)
send_to_tracer_nif(p, &p->common, p->common.id, tnif, TRACE_FUN_T_SEND,
operation, msg, to, pam_result);
}
+
+#ifdef ERTS_SMP
+ erts_thr_progress_unmanaged_continue(dhndl);
+#endif
+
erts_match_set_release_result_trace(p, pam_result);
}
@@ -1167,6 +1179,8 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec,
Eterm transformed_args[MAX_ARG];
ErtsTracer pre_ms_tracer = erts_tracer_nil;
+ ERTS_SMP_LC_ASSERT(erts_proc_lc_my_proc_locks(p) & ERTS_PROC_LOCK_MAIN);
+
ASSERT(tracer);
if (ERTS_TRACER_COMPARE(*tracer, erts_tracer_true)) {
/* Breakpoint trace enabled without specifying tracer =>
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 7a00cd8f6e..e6dc5303a8 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -61,9 +61,12 @@ struct enif_environment_t /* ErlNifEnv */
extern void erts_pre_nif(struct enif_environment_t*, Process*,
struct erl_module_nif*, Process* tracee);
extern void erts_post_nif(struct enif_environment_t* env);
+#ifdef ERTS_DIRTY_SCHEDULERS
extern void erts_pre_dirty_nif(ErtsSchedulerData *,
struct enif_environment_t*, Process*,
- struct erl_module_nif*, Process* tracee);
+ struct erl_module_nif*);
+extern void erts_post_dirty_nif(struct enif_environment_t* env);
+#endif
extern Eterm erts_nif_taints(Process* p);
extern void erts_print_nif_taints(int to, void* to_arg);
void erts_unload_nif(struct erl_module_nif* nif);
diff --git a/erts/emulator/test/dirty_nif_SUITE.erl b/erts/emulator/test/dirty_nif_SUITE.erl
index c2d2e58957..653b96557e 100644
--- a/erts/emulator/test/dirty_nif_SUITE.erl
+++ b/erts/emulator/test/dirty_nif_SUITE.erl
@@ -34,7 +34,7 @@
dirty_scheduler_exit/1, dirty_call_while_terminated/1,
dirty_heap_access/1, dirty_process_info/1,
dirty_process_register/1, dirty_process_trace/1,
- code_purge/1]).
+ code_purge/1, dirty_nif_send_traced/1]).
-define(nif_stub,nif_stub_error(?LINE)).
@@ -50,7 +50,8 @@ all() ->
dirty_process_info,
dirty_process_register,
dirty_process_trace,
- code_purge].
+ code_purge,
+ dirty_nif_send_traced].
init_per_suite(Config) ->
try erlang:system_info(dirty_cpu_schedulers) of
@@ -393,8 +394,8 @@ code_purge(Config) when is_list(Config) ->
end,
true = erlang:purge_module(dirty_code_test),
receive
- {'DOWN', Mon1, process, Pid1, Reason} ->
- killed = Reason
+ {'DOWN', Mon1, process, Pid1, Reason1} ->
+ killed = Reason1
end,
receive
{'DOWN', Mon2, process, Pid2, _} ->
@@ -411,8 +412,8 @@ code_purge(Config) when is_list(Config) ->
end,
true = erlang:purge_module(dirty_code_test),
receive
- {'DOWN', Mon2, process, Pid2, Reason} ->
- killed = Reason
+ {'DOWN', Mon2, process, Pid2, Reason2} ->
+ killed = Reason2
end,
End = erlang:monotonic_time(),
Time = erlang:convert_time_unit(End-Start, native, milli_seconds),
@@ -420,6 +421,34 @@ code_purge(Config) when is_list(Config) ->
true = Time =< 1000,
ok.
+dirty_nif_send_traced(Config) when is_list(Config) ->
+ Parent = self(),
+ Rcvr = spawn_link(fun() ->
+ Self = self(),
+ receive {ok, Self} -> ok end,
+ Parent ! {Self, received}
+ end),
+ Sndr = spawn_link(fun () ->
+ receive {Parent, go} -> ok end,
+ {ok, Rcvr} = send_wait_from_dirty_nif(Rcvr),
+ Parent ! {self(), sent}
+ end),
+ 1 = erlang:trace(Sndr, true, [send]),
+ Start = erlang:monotonic_time(),
+ Sndr ! {self(), go},
+ receive {trace, Sndr, send, {ok, Rcvr}, Rcvr} -> ok end,
+ receive {Rcvr, received} -> ok end,
+ End1 = erlang:monotonic_time(),
+ Time1 = erlang:convert_time_unit(End1-Start, native, 1000),
+ io:format("Time1: ~p milliseconds~n", [Time1]),
+ true = Time1 < 500,
+ receive {Sndr, sent} -> ok end,
+ End2 = erlang:monotonic_time(),
+ Time2 = erlang:convert_time_unit(End2-Start, native, 1000),
+ io:format("Time2: ~p milliseconds~n", [Time2]),
+ true = Time2 >= 1900,
+ ok.
+
%%
%% Internal...
%%
@@ -502,6 +531,7 @@ mcall(Node, Funs) ->
lib_loaded() -> false.
call_dirty_nif(_,_,_) -> ?nif_stub.
send_from_dirty_nif(_) -> ?nif_stub.
+send_wait_from_dirty_nif(_) -> ?nif_stub.
call_dirty_nif_exception(_) -> ?nif_stub.
call_dirty_nif_zero_args() -> ?nif_stub.
dirty_call_while_terminated_nif(_) -> ?nif_stub.
diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
index d92933a096..a0019e5d95 100644
--- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
+++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
@@ -100,6 +100,32 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_
return result;
}
+static ERL_NIF_TERM send_wait_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM result;
+ ErlNifPid pid;
+ ErlNifEnv* menv;
+ int res;
+
+ if (!enif_get_local_pid(env, argv[0], &pid))
+ return enif_make_badarg(env);
+ result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid));
+ menv = enif_alloc_env();
+ res = enif_send(env, &pid, menv, result);
+ enif_free_env(menv);
+
+#ifdef __WIN32__
+ Sleep(2000);
+#else
+ sleep(2);
+#endif
+
+ if (!res)
+ return enif_make_badarg(env);
+ else
+ return result;
+}
+
static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
switch (argc) {
@@ -237,6 +263,7 @@ static ErlNifFunc nif_funcs[] =
{"lib_loaded", 0, lib_loaded},
{"call_dirty_nif", 3, call_dirty_nif},
{"send_from_dirty_nif", 1, send_from_dirty_nif, ERL_NIF_DIRTY_JOB_CPU_BOUND},
+ {"send_wait_from_dirty_nif", 1, send_wait_from_dirty_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"dirty_sleeper", 0, dirty_sleeper, ERL_NIF_DIRTY_JOB_IO_BOUND},