aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/beam/atom.names1
-rw-r--r--erts/emulator/beam/beam_bif_load.c62
-rw-r--r--erts/emulator/beam/beam_load.c15
-rw-r--r--erts/emulator/beam/erl_gc.c65
-rw-r--r--erts/emulator/beam/erl_nif.c18
-rw-r--r--erts/emulator/beam/module.c3
-rw-r--r--erts/emulator/beam/module.h1
-rw-r--r--erts/emulator/test/driver_SUITE.erl6
-rw-r--r--erts/emulator/test/driver_SUITE_data/timer_drv.c44
-rw-r--r--erts/emulator/test/nif_SUITE.erl190
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.erl10
11 files changed, 353 insertions, 62 deletions
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 9dae67cb2d..2b66bf6f4e 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -505,6 +505,7 @@ atom port_limit
atom port_op
atom positive
atom prepare
+atom prepare_on_load
atom print
atom priority
atom private
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index dddcfbb77d..5969197168 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -53,6 +53,7 @@ static struct {
ErlFunEntry *def_funs[10];
Uint fe_size;
Uint fe_ix;
+ struct erl_module_instance saved_old;
} purge_state;
Process *erts_code_purger = NULL;
@@ -105,6 +106,8 @@ init_purge_state(void)
purge_state.fe_size = sizeof(purge_state.def_funs);
purge_state.fe_size /= sizeof(purge_state.def_funs[0]);
purge_state.fe_ix = 0;
+
+ purge_state.saved_old.code_hdr = 0;
}
void
@@ -739,10 +742,13 @@ BIF_RETTYPE call_on_load_function_1(BIF_ALIST_1)
{
Module* modp = erts_get_module(BIF_ARG_1, erts_active_code_ix());
- if (modp && modp->old.code_hdr) {
- BIF_TRAP_CODE_PTR_0(BIF_P, modp->old.code_hdr->on_load_function_ptr);
+ if (!modp || !modp->on_load) {
+ BIF_ERROR(BIF_P, BADARG);
}
- else {
+ if (modp->on_load->code_hdr) {
+ BIF_TRAP_CODE_PTR_0(BIF_P,
+ modp->on_load->code_hdr->on_load_function_ptr);
+ } else {
BIF_ERROR(BIF_P, BADARG);
}
}
@@ -765,14 +771,14 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
code_ix = erts_active_code_ix();
modp = erts_get_module(BIF_ARG_1, code_ix);
- if (!modp || !modp->old.code_hdr) {
+ if (!modp || !modp->on_load || !modp->on_load->code_hdr) {
error:
erts_smp_thr_progress_unblock();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_release_code_write_permission();
BIF_ERROR(BIF_P, BADARG);
}
- if (modp->old.code_hdr->on_load_function_ptr == NULL) {
+ if (modp->on_load->code_hdr->on_load_function_ptr == NULL) {
goto error;
}
if (BIF_ARG_2 != am_false && BIF_ARG_2 != am_true) {
@@ -781,14 +787,17 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
if (BIF_ARG_2 == am_true) {
int i;
- struct erl_module_instance t;
/*
- * Swap old and new code.
+ * Make the code with the on_load function current.
*/
- t = modp->curr;
- modp->curr = modp->old;
- modp->old = t;
+
+ if (modp->curr.code_hdr) {
+ modp->old = modp->curr;
+ }
+ modp->curr = *modp->on_load;
+ erts_free(ERTS_ALC_T_PREPARED_CODE, modp->on_load);
+ modp->on_load = 0;
/*
* The on_load function succeded. Fix up export entries.
@@ -1731,7 +1740,8 @@ BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
switch (BIF_ARG_2) {
- case am_prepare: {
+ case am_prepare:
+ case am_prepare_on_load: {
/*
* Prepare for purge by marking all fun
* entries referring to the code to purge
@@ -1756,7 +1766,22 @@ BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
/*
* Any code to purge?
*/
- erts_rlock_old_code(code_ix);
+
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ erts_rwlock_old_code(code_ix);
+ } else {
+ erts_rlock_old_code(code_ix);
+ }
+
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ ASSERT(modp->on_load);
+ ASSERT(modp->on_load->code_hdr);
+ purge_state.saved_old = modp->old;
+ modp->old = *modp->on_load;
+ erts_free(ERTS_ALC_T_PREPARED_CODE, (void *) modp->on_load);
+ modp->on_load = 0;
+ }
+
if (!modp->old.code_hdr)
res = am_false;
else {
@@ -1774,7 +1799,12 @@ BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
ERTS_SET_COPY_LITERAL_AREA(modp->old.code_hdr->literal_area);
#endif
}
- erts_runlock_old_code(code_ix);
+
+ if (BIF_ARG_2 == am_prepare_on_load) {
+ erts_rwunlock_old_code(code_ix);
+ } else {
+ erts_runlock_old_code(code_ix);
+ }
}
#ifndef ERTS_SMP
@@ -1911,8 +1941,14 @@ BIF_RETTYPE erts_internal_purge_module_2(BIF_ALIST_2)
modp->old.code_length = 0;
modp->old.catches = BEAM_CATCHES_NIL;
erts_remove_from_ranges(code);
+
ERTS_BIF_PREP_RET(ret, am_true);
}
+
+ if (purge_state.saved_old.code_hdr) {
+ modp->old = purge_state.saved_old;
+ purge_state.saved_old.code_hdr = 0;
+ }
erts_rwunlock_old_code(code_ix);
}
if (is_blocking) {
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index f63addb309..0afdedf6c2 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -833,11 +833,20 @@ erts_finish_loading(Binary* magic, Process* c_p,
size = stp->loaded_size;
erts_total_code_size += size;
- if (stp->on_load) {
- inst_p = &mod_tab_p->old;
- } else {
+
+ if (!stp->on_load) {
inst_p = &mod_tab_p->curr;
+ } else {
+ mod_tab_p->on_load =
+ (struct erl_module_instance *)
+ erts_alloc(ERTS_ALC_T_PREPARED_CODE,
+ sizeof(struct erl_module_instance));
+ inst_p = mod_tab_p->on_load;
+ inst_p->nif = 0;
+ inst_p->num_breakpoints = 0;
+ inst_p->num_traced_exports = 0;
}
+
inst_p->code_hdr = stp->hdr;
inst_p->code_length = size;
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 75f8ebefbd..6f641a1ea7 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -150,6 +150,7 @@ static void move_msgq_to_heap(Process *p);
static int reached_max_heap_size(Process *p, Uint total_heap_size,
Uint extra_heap_size, Uint extra_old_heap_size);
static void init_gc_info(ErtsGCInfo *gcip);
+static Uint64 next_vheap_size(Process* p, Uint64 vheap, Uint64 vheap_sz);
#ifdef HARDDEBUG
static void disallow_heap_frag_ref_in_heap(Process* p);
@@ -387,6 +388,11 @@ erts_gc_after_bif_call_lhf(Process* p, ErlHeapFragment *live_hf_end,
return result;
}
+ if (!p->mbuf) {
+ /* Must have GC:d in BIF call... invalidate live_hf_end */
+ live_hf_end = ERTS_INVALID_HFRAG_PTR;
+ }
+
if (is_non_value(result)) {
if (p->freason == TRAP) {
#if HIPE
@@ -535,9 +541,19 @@ young_gen_usage(Process *p)
if (p->flags & F_ON_HEAP_MSGQ) {
ErtsMessage *mp;
- for (mp = p->msg.first; mp; mp = mp->next)
+ for (mp = p->msg.first; mp; mp = mp->next) {
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding. However, we use their estimated
+ * size when calculating need, and by this making
+ * it more likely that they will fit on the heap
+ * when actually decoded.
+ */
if (mp->data.attached)
hsz += erts_msg_attached_data_size(mp);
+ }
}
aheap = p->abandoned_heap;
@@ -747,6 +763,9 @@ do_major_collection:
p->last_old_htop = p->old_htop;
#endif
+ ASSERT(!p->mbuf);
+ ASSERT(!ERTS_IS_GC_DESIRED(p));
+
return reds;
}
@@ -2251,44 +2270,31 @@ copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap,
static void
move_msgq_to_heap(Process *p)
{
+
ErtsMessage **mpp = &p->msg.first;
+ Uint64 pre_oh = MSO(p).overhead;
while (*mpp) {
ErtsMessage *mp = *mpp;
if (mp->data.attached) {
ErlHeapFragment *bp;
- ErtsHeapFactory factory;
-
- erts_factory_proc_prealloc_init(&factory, p,
- erts_msg_attached_data_size(mp));
-
- if (is_non_value(ERL_MESSAGE_TERM(mp))) {
- if (mp->data.dist_ext) {
- ASSERT(mp->data.dist_ext->heap_size >= 0);
- if (is_not_nil(ERL_MESSAGE_TOKEN(mp))) {
- bp = erts_dist_ext_trailer(mp->data.dist_ext);
- ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp),
- bp->used_size,
- &factory.hp,
- factory.off_heap);
- erts_cleanup_offheap(&bp->off_heap);
- }
- ERL_MESSAGE_TERM(mp) = erts_decode_dist_ext(&factory,
- mp->data.dist_ext);
- erts_free_dist_ext_copy(mp->data.dist_ext);
- mp->data.dist_ext = NULL;
- }
- }
- else {
+
+ /*
+ * We leave not yet decoded distribution messages
+ * as they are in the queue since it is not
+ * possible to determine a maximum size until
+ * actual decoding...
+ */
+ if (is_value(ERL_MESSAGE_TERM(mp))) {
bp = erts_message_to_heap_frag(mp);
if (bp->next)
- erts_move_multi_frags(&factory.hp, factory.off_heap, bp,
+ erts_move_multi_frags(&p->htop, &p->off_heap, bp,
mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0);
else
- copy_one_frag(&factory.hp, factory.off_heap, bp,
+ copy_one_frag(&p->htop, &p->off_heap, bp,
mp->m, ERL_MESSAGE_REF_ARRAY_SZ);
if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) {
@@ -2305,12 +2311,15 @@ move_msgq_to_heap(Process *p)
mp = new_mp;
}
}
-
- erts_factory_close(&factory);
}
mpp = &(*mpp)->next;
}
+
+ if (pre_oh != MSO(p).overhead) {
+ /* Got new binaries; update vheap size... */
+ BIN_VHEAP_SZ(p) = next_vheap_size(p, MSO(p).overhead, BIN_VHEAP_SZ(p));
+ }
}
static Uint
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 559b4017e7..3a547982da 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -3203,18 +3203,20 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
if (init_func != NULL)
handle = init_func;
+ this_mi = &module_p->curr;
+ prev_mi = &module_p->old;
if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) {
- if (module_p->old.code_hdr->on_load_function_ptr) {
- this_mi = &module_p->old;
+ ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
+ "module '%T' not allowed", mod_atom);
+ goto error;
+ } else if (module_p->on_load) {
+ ASSERT(module_p->on_load->code_hdr->on_load_function_ptr);
+ if (module_p->curr.code_hdr) {
prev_mi = &module_p->curr;
} else {
- ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
- "module '%T' not allowed", mod_atom);
- goto error;
+ prev_mi = &module_p->old;
}
- } else {
- this_mi = &module_p->curr;
- prev_mi = &module_p->old;
+ this_mi = module_p->on_load;
}
if (init_func == NULL &&
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index 3eb11f1693..4f36377450 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -85,6 +85,7 @@ static Module* module_alloc(Module* tmpl)
obj->old.num_breakpoints = 0;
obj->curr.num_traced_exports = 0;
obj->old.num_traced_exports = 0;
+ obj->on_load = 0;
return obj;
}
@@ -201,6 +202,7 @@ void module_start_staging(void)
dst_mod->curr = src_mod->curr;
dst_mod->old = src_mod->old;
+ dst_mod->on_load = src_mod->on_load;
}
/*
@@ -214,6 +216,7 @@ void module_start_staging(void)
dst_mod->curr = src_mod->curr;
dst_mod->old = src_mod->old;
+ dst_mod->on_load = src_mod->on_load;
}
newsz = index_table_sz(dst);
erts_smp_atomic_add_nob(&tot_module_bytes, (newsz - oldsz));
diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h
index 5a60bc90d9..1c1afc8461 100644
--- a/erts/emulator/beam/module.h
+++ b/erts/emulator/beam/module.h
@@ -39,6 +39,7 @@ typedef struct erl_module {
struct erl_module_instance curr;
struct erl_module_instance old; /* protected by "old_code" rwlock */
+ struct erl_module_instance* on_load;
} Module;
Module* erts_get_module(Eterm mod, ErtsCodeIndex code_ix);
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index 1df72193a6..2fbf6eae61 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -452,11 +452,7 @@ timer_delay(Config) when is_list(Config) ->
TimeBefore = erlang:monotonic_time(),
Timeout0 = 350,
erlang:port_command(Port, <<?DELAY_START_TIMER,Timeout0:32>>),
- Timeout = Timeout0 +
- case os:type() of
- {win32,_} -> 0; %Driver doesn't sleep on Windows.
- _ -> 1000
- end,
+ Timeout = Timeout0 + 1000,
receive
{Port,{data,[?TIMER]}} ->
Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
diff --git a/erts/emulator/test/driver_SUITE_data/timer_drv.c b/erts/emulator/test/driver_SUITE_data/timer_drv.c
index 57538e0d57..c3ce3b6e49 100644
--- a/erts/emulator/test/driver_SUITE_data/timer_drv.c
+++ b/erts/emulator/test/driver_SUITE_data/timer_drv.c
@@ -1,5 +1,13 @@
#include <stdio.h>
#include "erl_driver.h"
+#ifdef __WIN32__
+# include <windows.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/select.h>
+# include <unistd.h>
+#endif
#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
(((unsigned char*) (s))[1] << 16) | \
@@ -17,6 +25,7 @@ static ErlDrvData timer_start(ErlDrvPort, char*);
static void timer_stop(ErlDrvData);
static void timer_read(ErlDrvData, char*, ErlDrvSizeT);
static void timer(ErlDrvData);
+static void ms_sleep(int ms);
static ErlDrvEntry timer_driver_entry =
{
@@ -75,9 +84,7 @@ static void timer_read(ErlDrvData p, char *buf, ErlDrvSizeT len)
reply[0] = CANCELLED;
driver_output(port, reply, 1);
} else if (buf[0] == DELAY_START_TIMER) {
-#ifndef __WIN32__
- sleep(1);
-#endif
+ ms_sleep(1000);
driver_set_timer(port, get_int32(buf + 1));
}
}
@@ -95,3 +102,34 @@ static void timer(ErlDrvData port)
reply[0] = TIMER;
driver_output((ErlDrvPort)port, reply, 1);
}
+
+static void
+ms_sleep(int ms)
+{
+ /* Important that we do not return too early... */
+ ErlDrvTime time, timeout_time;
+
+ time = erl_drv_monotonic_time(ERL_DRV_USEC);
+
+ timeout_time = time + ((ErlDrvTime) ms)*1000;
+
+ while (time < timeout_time) {
+ ErlDrvTime timeout = timeout_time - time;
+
+#ifdef __WIN32__
+ Sleep((DWORD) (timeout / 1000));
+#else
+ {
+ struct timeval tv;
+
+ tv.tv_sec = (long) timeout / (1000*1000);
+ tv.tv_usec = (long) timeout % (1000*1000);
+
+ select(0, NULL, NULL, NULL, &tv);
+ }
+#endif
+
+ time = erl_drv_monotonic_time(ERL_DRV_USEC);
+ }
+
+}
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 8df2733fac..9c1694fa8a 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -29,6 +29,7 @@
-export([all/0, suite/0,
init_per_testcase/2, end_per_testcase/2,
basic/1, reload/1, upgrade/1, heap_frag/1,
+ t_on_load/1,
types/1, many_args/1, binaries/1, get_string/1, get_atom/1,
maps/1,
api_macros/1,
@@ -68,6 +69,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, reload, upgrade, heap_frag, types, many_args,
+ t_on_load,
binaries, get_string, get_atom, maps, api_macros, from_array,
iolist_as_binary, resource, resource_binary,
resource_takeover, threading, send, send2, send3,
@@ -83,10 +85,19 @@ all() ->
nif_port_command,
nif_snprintf].
+init_per_testcase(t_on_load, Config) ->
+ ets:new(nif_SUITE, [named_table]),
+ Config;
init_per_testcase(_Case, Config) ->
Config.
+end_per_testcase(t_on_load, _Config) ->
+ ets:delete(nif_SUITE),
+ testcase_cleanup();
end_per_testcase(_Func, _Config) ->
+ testcase_cleanup().
+
+testcase_cleanup() ->
P1 = code:purge(nif_mod),
Del = code:delete(nif_mod),
P2 = code:purge(nif_mod),
@@ -174,11 +185,33 @@ upgrade(Config) when is_list(Config) ->
true = erlang:delete_module(nif_mod),
[] = nif_mod_call_history(),
+ %% Repeat upgrade again but from old (deleted) instance
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,9,109}] = nif_mod_call_history(),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+ 1 = nif_mod:lib_version(),
+ [{upgrade,1,10,110},{lib_version,1,11,111}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,1,12,112}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,13,113}] = nif_mod_call_history(),
+
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
+
+
Pid ! die,
{'DOWN', MRef, process, Pid, normal} = receive_any(),
false = check_process_code(Pid, nif_mod),
true = erlang:purge_module(nif_mod),
- [{unload,1,9,109}] = nif_mod_call_history(),
+ [{unload,1,14,114}] = nif_mod_call_history(),
%% Module upgrade with different lib version
{module,nif_mod} = erlang:load_module(nif_mod,Bin),
@@ -215,17 +248,170 @@ upgrade(Config) when is_list(Config) ->
true = erlang:delete_module(nif_mod),
[] = nif_mod_call_history(),
+
+ %% Reverse upgrade but from old (deleted) instance
+ {module,nif_mod} = erlang:load_module(nif_mod,Bin),
+ undefined = nif_mod:lib_version(),
+ [] = nif_mod_call_history(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,4,204}] = nif_mod_call_history(),
+
+ ok = nif_mod:load_nif_lib(Config, 1),
+ 1 = nif_mod:lib_version(),
+ [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
+
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,5,205}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = erlang:purge_module(nif_mod),
+ [{unload,2,6,206}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ true = erlang:delete_module(nif_mod),
+ [] = nif_mod_call_history(),
+
+
Pid2 ! die,
{'DOWN', MRef2, process, Pid2, normal} = receive_any(),
false= check_process_code(Pid2, nif_mod),
true = erlang:purge_module(nif_mod),
- [{unload,2,4,204}] = nif_mod_call_history(),
+ [{unload,1,4,104}] = nif_mod_call_history(),
true = lists:member(?MODULE, erlang:system_info(taints)),
true = lists:member(nif_mod, erlang:system_info(taints)),
verify_tmpmem(TmpMem),
ok.
+%% Test loading/upgrade in on_load
+t_on_load(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
+ ensure_lib_loaded(Config),
+
+ Data = proplists:get_value(data_dir, Config),
+ File = filename:join(Data, "nif_mod"),
+ {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors,
+ {d,'USE_ON_LOAD'}]),
+
+ %% Use ETS to tell nif_mod:on_load what to do
+ ets:insert(nif_SUITE, {data_dir, Data}),
+ ets:insert(nif_SUITE, {lib_version, 1}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+
+ {Pid,MRef} = nif_mod:start(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ %% Module upgrade with same lib-version
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ 1 = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{upgrade,1,4,104},{lib_version,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,7,107}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,8,108}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+ %% Repeat upgrade again but from old (deleted) instance
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,1,9,109}] = nif_mod_call_history(),
+ 1 = nif_mod:lib_version(),
+ 1 = call(Pid,lib_version),
+ [{lib_version,1,10,110},{lib_version,1,11,111}] = nif_mod_call_history(),
+
+ upgraded = call(Pid,upgrade),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,12,112}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,13,113}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+
+ Pid ! die,
+ {'DOWN', MRef, process, Pid, normal} = receive_any(),
+ false = check_process_code(Pid, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,14,114}] = nif_mod_call_history(),
+
+ %% Module upgrade with different lib version
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ {Pid2,MRef2} = nif_mod:start(),
+ 1 = call(Pid2,lib_version),
+ [{lib_version,1,3,103},{lib_version,1,4,104}] = nif_mod_call_history(),
+
+ true = ets:insert(nif_SUITE,{lib_version,2}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,2,1,201}] = nif_mod_call_history(),
+
+ 2 = nif_mod:lib_version(),
+ 1 = call(Pid2,lib_version),
+ [{lib_version,2,2,202},{lib_version,1,5,105}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,6,106}] = nif_mod_call_history(),
+
+ 2 = nif_mod:lib_version(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,2,3,203},{lib_version,2,4,204}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+ %% Reverse upgrade but from old (deleted) instance
+ ets:insert(nif_SUITE,{lib_version,1}),
+ {module,nif_mod} = code:load_binary(nif_mod,File,Bin),
+ [{upgrade,1,1,101}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ 2 = call(Pid2,lib_version),
+ [{lib_version,1,2,102},{lib_version,2,5,205}] = nif_mod_call_history(),
+
+ upgraded = call(Pid2,upgrade),
+ false = check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,2,6,206}] = nif_mod_call_history(),
+
+ 1 = nif_mod:lib_version(),
+ [{lib_version,1,3,103}] = nif_mod_call_history(),
+
+ true = code:delete(nif_mod),
+ [] = nif_mod_call_history(),
+
+
+ Pid2 ! die,
+ {'DOWN', MRef2, process, Pid2, normal} = receive_any(),
+ false= check_process_code(Pid2, nif_mod),
+ true = code:soft_purge(nif_mod),
+ [{unload,1,4,104}] = nif_mod_call_history(),
+
+ true = lists:member(?MODULE, erlang:system_info(taints)),
+ true = lists:member(nif_mod, erlang:system_info(taints)),
+ verify_tmpmem(TmpMem),
+ ok.
+
+
%% Test NIF building heap fragments
heap_frag(Config) when is_list(Config) ->
TmpMem = tmpmem(),
diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl
index eec1bb8858..1fcc33faa4 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl
+++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl
@@ -29,6 +29,16 @@
-define(nif_stub,nif_stub_error(?LINE)).
+-ifdef(USE_ON_LOAD).
+-on_load(on_load/0).
+
+on_load() ->
+ [{data_dir, Path}] = ets:lookup(nif_SUITE, data_dir),
+ [{lib_version, Ver}] = ets:lookup(nif_SUITE, lib_version),
+ erlang:load_nif(filename:join(Path,libname(Ver)), []).
+
+-endif.
+
load_nif_lib(Config, Ver) ->
load_nif_lib(Config, Ver, []).