aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
authorSverker Eriksson <[email protected]>2010-02-11 13:30:32 +0000
committerErlang/OTP <[email protected]>2010-02-11 13:30:32 +0000
commit2a96208cb00220f963e723ae0530492c5c70df27 (patch)
tree1a0150d8a404a19fa69da48bb49fad95e701454c /erts/emulator
parent94a5a2832200fa5061d31e64c0eb8315c3215e0a (diff)
downloadotp-2a96208cb00220f963e723ae0530492c5c70df27.tar.gz
otp-2a96208cb00220f963e723ae0530492c5c70df27.tar.bz2
otp-2a96208cb00220f963e723ae0530492c5c70df27.zip
OTP-8335 Even more NIF features
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/Makefile.in2
-rw-r--r--erts/emulator/beam/beam_bif_load.c31
-rw-r--r--erts/emulator/beam/beam_emu.c4
-rw-r--r--erts/emulator/beam/binary.c18
-rw-r--r--erts/emulator/beam/erl_binary.h21
-rw-r--r--erts/emulator/beam/erl_driver.h33
-rw-r--r--erts/emulator/beam/erl_drv_nif.h48
-rw-r--r--erts/emulator/beam/erl_init.c1
-rw-r--r--erts/emulator/beam/erl_nif.c805
-rw-r--r--erts/emulator/beam/erl_nif.h42
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h151
-rw-r--r--erts/emulator/beam/external.c9
-rw-r--r--erts/emulator/beam/global.h25
-rw-r--r--erts/emulator/beam/io.c12
-rw-r--r--erts/emulator/beam/module.c18
-rw-r--r--erts/emulator/beam/module.h19
-rw-r--r--erts/emulator/beam/utils.c20
-rw-r--r--erts/emulator/test/nif_SUITE.erl608
-rw-r--r--erts/emulator/test/nif_SUITE_data/Makefile.src13
-rw-r--r--erts/emulator/test/nif_SUITE_data/head.txt5
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c388
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.c203
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.erl10
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.h5
-rw-r--r--erts/emulator/test/nif_SUITE_data/tail.txt5
-rw-r--r--erts/emulator/test/nif_SUITE_data/testcase_driver.h59
-rw-r--r--erts/emulator/test/nif_SUITE_data/tester.c73
-rw-r--r--erts/emulator/test/nif_SUITE_data/tester.erl13
28 files changed, 2336 insertions, 305 deletions
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 2fe12be43e..a81ab28847 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -419,7 +419,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
RELSYSDIR = $(RELEASE_PATH)/erts-$(VSN)
-RELEASE_INCLUDES = beam/erl_driver.h sys/$(ERLANG_OSTYPE)/driver_int.h beam/erl_nif.h beam/erl_nif_api_funcs.h
+RELEASE_INCLUDES = beam/erl_driver.h sys/$(ERLANG_OSTYPE)/driver_int.h beam/erl_nif.h beam/erl_nif_api_funcs.h beam/erl_drv_nif.h
ifeq ($(TARGET),win32)
RELEASE_INCLUDES += sys/$(ERLANG_OSTYPE)/erl_win_dyn_driver.h
endif
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index d3a1ed4e7d..b1feec7074 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1999-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1999-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
@@ -642,21 +642,9 @@ purge_module(int module)
/*
* Unload any NIF library
*/
- if (modp->old_nif.handle != NULL) {
- if (modp->old_nif.entry->unload != NULL) {
- ErlNifEnv env;
- env.nif_data = modp->old_nif.data;
- env.proc = NULL; /* BUGBUG: unlink can not access calling process */
- env.hp = NULL;
- env.hp_end = NULL;
- env.heap_frag_sz = 0;
- env.fpe_was_unmasked = erts_block_fpe();
- modp->old_nif.entry->unload(NULL, modp->old_nif.data);
- erts_unblock_fpe(env.fpe_was_unmasked);
- }
- erts_sys_ddll_close(modp->old_nif.handle);
- modp->old_nif.handle = NULL;
- modp->old_nif.entry = NULL;
+ if (modp->old_nif != NULL) {
+ erts_unload_nif(modp->old_nif);
+ modp->old_nif = NULL;
}
/*
@@ -732,8 +720,7 @@ delete_code(Process *c_p, ErtsProcLocks c_p_locks, Module* modp)
modp->code = NULL;
modp->code_length = 0;
modp->catches = BEAM_CATCHES_NIL;
- modp->nif.handle = NULL;
- modp->nif.entry = NULL;
+ modp->nif = NULL;
}
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 13757b7d1c..2f7f48193d 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -2973,7 +2973,7 @@ void process_main(void)
* I[-1]: Arity
* I[0]: &&call_nif
* I[1]: Function pointer to NIF function
- * I[2]: priv_data pointer
+ * I[2]: Pointer to erl_module_nif
*/
BifFunction vbf;
@@ -2989,7 +2989,7 @@ void process_main(void)
typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]);
NifF* fp = vbf = (NifF*) I[1];
struct enif_environment_t env;
- erts_pre_nif(&env, c_p, (void*)I[2]);
+ erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2]);
reg[0] = r(0);
tmp_arg1 = (*fp)(&env, tmp_arg2, reg);
erts_post_nif(&env);
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index 49bc0d6457..08c64610a2 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1996-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
@@ -180,7 +180,7 @@ erts_realloc_binary(Eterm bin, size_t size)
}
byte*
-erts_get_aligned_binary_bytes(Eterm bin, byte** base_ptr)
+erts_get_aligned_binary_bytes_extra(Eterm bin, byte** base_ptr, unsigned extra)
{
byte* bytes;
Eterm* real_bin;
@@ -208,10 +208,10 @@ erts_get_aligned_binary_bytes(Eterm bin, byte** base_ptr)
bytes = (byte *)(&(((ErlHeapBin *) real_bin)->data)) + offs;
}
if (bit_offs) {
- byte* buf = (byte *) erts_alloc(ERTS_ALC_T_TMP, byte_size);
-
- erts_copy_bits(bytes, bit_offs, 1, buf, 0, 1, byte_size*8);
+ byte* buf = (byte *) erts_alloc(ERTS_ALC_T_TMP, byte_size + extra);
*base_ptr = buf;
+ buf += extra;
+ erts_copy_bits(bytes, bit_offs, 1, buf, 0, 1, byte_size*8);
bytes = buf;
}
return bytes;
diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h
index f8ecdde997..21d4e3fdfd 100644
--- a/erts/emulator/beam/erl_binary.h
+++ b/erts/emulator/beam/erl_binary.h
@@ -150,7 +150,7 @@ do { \
void erts_init_binary(void);
-byte* erts_get_aligned_binary_bytes(Eterm, byte**);
+byte* erts_get_aligned_binary_bytes_extra(Eterm, byte**, unsigned extra);
#if defined(__i386__) || !defined(__GNUC__)
/*
@@ -166,6 +166,7 @@ byte* erts_get_aligned_binary_bytes(Eterm, byte**);
#define ERTS_CHK_BIN_ALIGNMENT(B) \
do { ASSERT(!(B) || (((Uint) &((Binary *)(B))->orig_bytes[0]) & ERTS_BIN_ALIGNMENT_MASK) == ((Uint) 0)) } while(0)
+ERTS_GLB_INLINE byte* erts_get_aligned_binary_bytes(Eterm bin, byte** base_ptr);
ERTS_GLB_INLINE void erts_free_aligned_binary_bytes(byte* buf);
ERTS_GLB_INLINE Binary *erts_bin_drv_alloc_fnf(Uint size);
ERTS_GLB_INLINE Binary *erts_bin_drv_alloc(Uint size);
@@ -178,6 +179,14 @@ ERTS_GLB_INLINE Binary *erts_create_magic_binary(Uint size,
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+#include <stddef.h> /* offsetof */
+
+ERTS_GLB_INLINE byte*
+erts_get_aligned_binary_bytes(Eterm bin, byte** base_ptr)
+{
+ return erts_get_aligned_binary_bytes_extra(bin, base_ptr, 0);
+}
+
ERTS_GLB_INLINE void
erts_free_aligned_binary_bytes(byte* buf)
{
@@ -189,7 +198,7 @@ erts_free_aligned_binary_bytes(byte* buf)
ERTS_GLB_INLINE Binary *
erts_bin_drv_alloc_fnf(Uint size)
{
- Uint bsize = sizeof(Binary) - 1 + size;
+ Uint bsize = ERTS_SIZEOF_Binary(size);
void *res;
res = erts_alloc_fnf(ERTS_ALC_T_DRV_BINARY, bsize);
ERTS_CHK_BIN_ALIGNMENT(res);
@@ -199,7 +208,7 @@ erts_bin_drv_alloc_fnf(Uint size)
ERTS_GLB_INLINE Binary *
erts_bin_drv_alloc(Uint size)
{
- Uint bsize = sizeof(Binary) - 1 + size;
+ Uint bsize = ERTS_SIZEOF_Binary(size);
void *res;
res = erts_alloc(ERTS_ALC_T_DRV_BINARY, bsize);
ERTS_CHK_BIN_ALIGNMENT(res);
@@ -210,7 +219,7 @@ erts_bin_drv_alloc(Uint size)
ERTS_GLB_INLINE Binary *
erts_bin_nrml_alloc(Uint size)
{
- Uint bsize = sizeof(Binary) - 1 + size;
+ Uint bsize = ERTS_SIZEOF_Binary(size);
void *res;
res = erts_alloc(ERTS_ALC_T_BINARY, bsize);
ERTS_CHK_BIN_ALIGNMENT(res);
@@ -221,7 +230,7 @@ ERTS_GLB_INLINE Binary *
erts_bin_realloc_fnf(Binary *bp, Uint size)
{
Binary *nbp;
- Uint bsize = sizeof(Binary) - 1 + size;
+ Uint bsize = ERTS_SIZEOF_Binary(size);
ASSERT((bp->flags & BIN_FLAG_MAGIC) == 0);
if (bp->flags & BIN_FLAG_DRV)
nbp = erts_realloc_fnf(ERTS_ALC_T_DRV_BINARY, (void *) bp, bsize);
@@ -235,7 +244,7 @@ ERTS_GLB_INLINE Binary *
erts_bin_realloc(Binary *bp, Uint size)
{
Binary *nbp;
- Uint bsize = sizeof(Binary) - 1 + size;
+ Uint bsize = ERTS_SIZEOF_Binary(size);
ASSERT((bp->flags & BIN_FLAG_MAGIC) == 0);
if (bp->flags & BIN_FLAG_DRV)
nbp = erts_realloc_fnf(ERTS_ALC_T_DRV_BINARY, (void *) bp, bsize);
diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h
index cdb584b282..489e74d960 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1999-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1999-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
@@ -65,6 +65,8 @@
# error SIZEOF_LONG_LONG mismatch
#endif
+#include "erl_drv_nif.h"
+
#include <stdlib.h>
#if defined(VXWORKS)
@@ -116,7 +118,7 @@ typedef struct {
#define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed)
#define ERL_DRV_EXTENDED_MAJOR_VERSION 1
-#define ERL_DRV_EXTENDED_MINOR_VERSION 4
+#define ERL_DRV_EXTENDED_MINOR_VERSION 5
/*
* The emulator will refuse to load a driver with different major
@@ -195,22 +197,6 @@ typedef struct {
unsigned char data[sizeof(void *)*4];
} ErlDrvMonitor;
-
-/*
- * System info
- */
-
-typedef struct {
- int driver_major_version;
- int driver_minor_version;
- char *erts_version;
- char *otp_release;
- int thread_support;
- int smp_support;
- int async_threads;
- int scheduler_threads;
-} ErlDrvSysInfo;
-
typedef struct {
unsigned long megasecs;
unsigned long secs;
@@ -255,9 +241,6 @@ typedef struct ErlDrvCond_ ErlDrvCond;
typedef struct ErlDrvRWLock_ ErlDrvRWLock;
typedef int ErlDrvTSDKey;
-typedef struct {
- int suggested_stack_size;
-} ErlDrvThreadOpts;
/*
*
diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h
new file mode 100644
index 0000000000..ea013a49a3
--- /dev/null
+++ b/erts/emulator/beam/erl_drv_nif.h
@@ -0,0 +1,48 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Common structures for both erl_driver.h and erl_nif.h
+ */
+
+#ifndef __ERL_DRV_NIF_H__
+#define __ERL_DRV_NIF_H__
+
+typedef struct {
+ int driver_major_version;
+ int driver_minor_version;
+ char *erts_version;
+ char *otp_release;
+ int thread_support;
+ int smp_support;
+ int async_threads;
+ int scheduler_threads;
+ int nif_major_version;
+ int nif_minor_version;
+} ErlDrvSysInfo;
+
+typedef struct {
+ int suggested_stack_size;
+} ErlDrvThreadOpts;
+
+#endif /* __ERL_DRV_NIF_H__ */
+
+
+
+
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 17cf3b9597..e97ab328cd 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -296,6 +296,7 @@ erl_init(void)
erl_sys_init_final();
#endif
packet_parser_init();
+ erl_nif_init();
}
static void
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 2cb93112ae..585a6c1fdf 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2009-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
/* Erlang Native InterFace
@@ -34,9 +34,26 @@
#include "beam_bp.h"
#include <limits.h>
+#include <stddef.h> /* offsetof */
+
+
+/* Information about a loaded nif library.
+ * Each successful call to erlang:load_nif will allocate an instance of
+ * erl_module_nif. Two calls opening the same library will thus have the same
+ * 'handle'.
+ */
+struct erl_module_nif {
+ void* priv_data;
+ void* handle; /* "dlopen" */
+ struct enif_entry_t* entry;
+ erts_refc_t rt_cnt; /* number of resource types */
+ erts_refc_t rt_dtor_cnt; /* number of resource types with destructors */
+ int is_orphan; /* if erlang module has been purged */
+};
+
#define MIN_HEAP_FRAG_SZ 200
-static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need);
+static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp);
static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, unsigned need)
{
@@ -45,43 +62,88 @@ static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, unsigned need)
if (env->hp <= env->hp_end) {
return hp;
}
- env->hp = hp;
- return alloc_heap_heavy(env,need);
+ return alloc_heap_heavy(env, need, hp);
}
-static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need)
-{
- Eterm* hp;
-
- if (env->heap_frag_sz == 0) {
+static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp)
+{
+ unsigned frag_sz;
+ env->hp = hp;
+ if (env->heap_frag == NULL) {
ASSERT(HEAP_LIMIT(env->proc) == env->hp_end);
HEAP_TOP(env->proc) = env->hp;
- env->heap_frag_sz = need + MIN_HEAP_FRAG_SZ;
}
else {
HRelease(env->proc, env->hp_end, env->hp);
- env->heap_frag_sz *= 2;
}
- hp = erts_heap_alloc(env->proc, env->heap_frag_sz);
+ frag_sz = need + MIN_HEAP_FRAG_SZ;
+ hp = erts_heap_alloc(env->proc, frag_sz);
env->hp = hp + need;
- env->hp_end = hp + env->heap_frag_sz;
+ env->hp_end = hp + frag_sz;
+ env->heap_frag = MBUF(env->proc);
return hp;
}
-void erts_pre_nif(ErlNifEnv* env, Process* p, void* nif_data)
+void erts_pre_nif(ErlNifEnv* env, Process* p, struct erl_module_nif* mod_nif)
{
- env->nif_data = nif_data;
+ env->mod_nif = mod_nif;
env->proc = p;
env->hp = HEAP_TOP(p);
env->hp_end = HEAP_LIMIT(p);
- env->heap_frag_sz = 0;
+ env->heap_frag = NULL;
+ env->fpe_was_unmasked = erts_block_fpe();
+ env->tmp_obj_list = NULL;
+}
+
+static void pre_nif_noproc(ErlNifEnv* env, struct erl_module_nif* mod_nif)
+{
+ env->mod_nif = mod_nif;
+ env->proc = NULL;
+ env->hp = NULL;
+ env->hp_end = NULL;
+ env->heap_frag = NULL;
env->fpe_was_unmasked = erts_block_fpe();
+ env->tmp_obj_list = NULL;
+}
+
+static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env)
+{
+ while (env->tmp_obj_list != NULL) {
+ struct enif_tmp_obj_t* free_me = env->tmp_obj_list;
+ env->tmp_obj_list = free_me->next;
+ free_me->dtor(free_me);
+ }
}
void erts_post_nif(ErlNifEnv* env)
{
erts_unblock_fpe(env->fpe_was_unmasked);
- if (env->heap_frag_sz == 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->size);
+ HRelease(env->proc, env->hp_end, env->hp);
+ }
+ free_tmp_objs(env);
+}
+
+static void post_nif_noproc(ErlNifEnv* env)
+{
+ erts_unblock_fpe(env->fpe_was_unmasked);
+ free_tmp_objs(env);
+}
+
+
+/* Flush out our cached heap pointers to allow an ordinary HAlloc
+*/
+static void enable_halloc(ErlNifEnv* env)
+{
+ 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));
@@ -89,14 +151,35 @@ void erts_post_nif(ErlNifEnv* env)
}
else {
ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
- ASSERT(env->hp_end - env->hp <= env->heap_frag_sz);
+ ASSERT(env->hp_end - env->hp <= env->heap_frag->size);
HRelease(env->proc, env->hp_end, env->hp);
}
}
-void* enif_get_data(ErlNifEnv* env)
+/* Restore cached heap pointers
+*/
+static void disable_halloc(ErlNifEnv* env)
{
- return env->nif_data;
+ 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));
+ env->hp = HEAP_TOP(env->proc);
+ }
+ else {
+ ASSERT(env->hp_end != HEAP_LIMIT(env->proc));
+ ASSERT(env->hp_end - env->hp <= env->heap_frag->size);
+ env->heap_frag = MBUF(env->proc);
+ ASSERT(env->heap_frag != NULL);
+ env->hp = env->heap_frag->mem + env->heap_frag->used_size;
+ env->hp_end = env->heap_frag->mem + env->heap_frag->size;
+ }
+}
+
+
+void* enif_priv_data(ErlNifEnv* env)
+{
+ return env->mod_nif->priv_data;
}
void* enif_alloc(ErlNifEnv* env, size_t size)
@@ -104,6 +187,11 @@ void* enif_alloc(ErlNifEnv* env, size_t size)
return erts_alloc_fnf(ERTS_ALC_T_NIF, (Uint) size);
}
+void* enif_realloc(ErlNifEnv* env, void* ptr, size_t size)
+{
+ return erts_realloc_fnf(ERTS_ALC_T_NIF, ptr, size);
+}
+
void enif_free(ErlNifEnv* env, void* ptr)
{
erts_free(ERTS_ALC_T_NIF, ptr);
@@ -119,25 +207,94 @@ int enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)
return is_binary(term) && (binary_bitsize(term) % 8 == 0);
}
+int enif_is_empty_list(ErlNifEnv* env, ERL_NIF_TERM term)
+{
+ return is_nil(term);
+}
+
+int enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM term)
+{
+ return is_fun(term);
+}
+
+int enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)
+{
+ return is_pid(term);
+}
+
+int enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)
+{
+ return is_port(term);
+}
+
int enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)
{
return is_ref(term);
}
+static void aligned_binary_dtor(struct enif_tmp_obj_t* obj)
+{
+ erts_free_aligned_binary_bytes((byte*)obj);
+}
int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin)
{
- bin->tmp_alloc = NULL;
- bin->data = erts_get_aligned_binary_bytes(bin_term, &bin->tmp_alloc);
+ union {
+ struct enif_tmp_obj_t* tmp;
+ byte* raw_ptr;
+ }u;
+ u.tmp = NULL;
+ bin->data = erts_get_aligned_binary_bytes_extra(bin_term, &u.raw_ptr,
+ sizeof(struct enif_tmp_obj_t));
if (bin->data == NULL) {
return 0;
}
+ if (u.tmp != NULL) {
+ u.tmp->next = env->tmp_obj_list;
+ u.tmp->dtor = &aligned_binary_dtor;
+ env->tmp_obj_list = u.tmp;
+ }
bin->bin_term = bin_term;
bin->size = binary_size(bin_term);
bin->ref_bin = NULL;
return 1;
}
+static void tmp_alloc_dtor(struct enif_tmp_obj_t* obj)
+{
+ erts_free(ERTS_ALC_T_TMP, obj);
+}
+
+int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
+{
+ struct enif_tmp_obj_t* tobj;
+ int sz;
+ if (is_binary(term)) {
+ return enif_inspect_binary(env,term,bin);
+ }
+ if (is_nil(term)) {
+ bin->data = (unsigned char*) &bin->data; /* dummy non-NULL */
+ bin->size = 0;
+ bin->bin_term = THE_NON_VALUE;
+ bin->ref_bin = NULL;
+ return 1;
+ }
+ if ((sz = io_list_len(term)) < 0) {
+ return 0;
+ }
+
+ tobj = erts_alloc(ERTS_ALC_T_TMP, sz + sizeof(struct enif_tmp_obj_t));
+ tobj->next = env->tmp_obj_list;
+ tobj->dtor = &tmp_alloc_dtor;
+ env->tmp_obj_list = tobj;
+
+ bin->data = (unsigned char*) &tobj[1];
+ bin->size = sz;
+ bin->bin_term = THE_NON_VALUE;
+ bin->ref_bin = NULL;
+ io_list_to_buf(term, (char*) bin->data, sz);
+ return 1;
+}
int enif_alloc_binary(ErlNifEnv* env, unsigned size, ErlNifBinary* bin)
{
@@ -154,46 +311,48 @@ int enif_alloc_binary(ErlNifEnv* env, unsigned size, ErlNifBinary* bin)
bin->size = size;
bin->data = (unsigned char*) refbin->orig_bytes;
bin->bin_term = THE_NON_VALUE;
- bin->tmp_alloc = NULL;
bin->ref_bin = refbin;
return 1;
}
int enif_realloc_binary(ErlNifEnv* env, ErlNifBinary* bin, unsigned size)
{
- Binary* oldbin;
- Binary* newbin;
- ASSERT(bin->ref_bin != NULL);
-
- oldbin = (Binary*) bin->ref_bin;
- newbin = (Binary *) erts_bin_realloc_fnf(oldbin, size);
- if (!newbin) {
- return 0;
- }
- newbin->orig_size = size;
- bin->ref_bin = newbin;
- bin->data = (unsigned char*) newbin->orig_bytes;
- bin->size = size;
+ if (bin->ref_bin != NULL) {
+ Binary* oldbin;
+ Binary* newbin;
+
+ oldbin = (Binary*) bin->ref_bin;
+ newbin = (Binary *) erts_bin_realloc_fnf(oldbin, size);
+ if (!newbin) {
+ return 0;
+ }
+ newbin->orig_size = size;
+ bin->ref_bin = newbin;
+ bin->data = (unsigned char*) newbin->orig_bytes;
+ bin->size = size;
+ }
+ else {
+ unsigned char* old_data = bin->data;
+ unsigned cpy_sz = (size < bin->size ? size : bin->size);
+ enif_alloc_binary(env, size, bin);
+ sys_memcpy(bin->data, old_data, cpy_sz);
+ }
return 1;
}
void enif_release_binary(ErlNifEnv* env, ErlNifBinary* bin)
{
- if (bin->ref_bin == NULL) {
- erts_free_aligned_binary_bytes(bin->tmp_alloc);
- }
- else {
+ if (bin->ref_bin != NULL) {
Binary* refbin = bin->ref_bin;
- ASSERT(bin->tmp_alloc == NULL);
ASSERT(bin->bin_term == THE_NON_VALUE);
if (erts_refc_dectest(&refbin->refc, 0) == 0) {
erts_bin_free(refbin);
}
}
#ifdef DEBUG
+ bin->data = NULL;
bin->bin_term = THE_NON_VALUE;
- bin->tmp_alloc = NULL;
bin->ref_bin = NULL;
#endif
}
@@ -208,7 +367,7 @@ int enif_compare(ErlNifEnv* env, Eterm lhs, Eterm rhs)
return cmp(lhs,rhs);
}
-int enif_get_tuple(ErlNifEnv* env, Eterm tpl, int* arity, Eterm** array)
+int enif_get_tuple(ErlNifEnv* env, Eterm tpl, int* arity, const Eterm** array)
{
Eterm* ptr;
if (is_not_tuple(tpl)) {
@@ -220,16 +379,47 @@ int enif_get_tuple(ErlNifEnv* env, Eterm tpl, int* arity, Eterm** array)
return 1;
}
+int enif_get_string(ErlNifEnv *env, ERL_NIF_TERM list, char* buf, unsigned len,
+ ErlNifCharEncoding encoding)
+{
+ Eterm* listptr;
+ int n = 0;
+
+ ASSERT(encoding == ERL_NIF_LATIN1);
+ if (len < 1) {
+ return 0;
+ }
+ while (is_not_nil(list)) {
+ if (is_not_list(list)) {
+ buf[n] = '\0';
+ return 0;
+ }
+ listptr = list_val(list);
+
+ if (!is_byte(*listptr)) {
+ buf[n] = '\0';
+ return 0;
+ }
+ buf[n++] = unsigned_val(*listptr);
+ if (n >= len) {
+ buf[n-1] = '\0'; /* truncate */
+ return -len;
+ }
+ list = CDR(listptr);
+ }
+ buf[n] = '\0';
+ return n + 1;
+}
+
Eterm enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)
{
- if (bin->ref_bin == NULL) {
- erts_free_aligned_binary_bytes(bin->tmp_alloc);
+ if (bin->bin_term != THE_NON_VALUE) {
return bin->bin_term;
}
- else {
+ else if (bin->ref_bin != NULL) {
Binary* bptr = bin->ref_bin;
ProcBin* pb;
- ASSERT(bin->tmp_alloc == NULL);
+ Eterm bin_term;
/* !! Copy-paste from new_binary() !! */
pb = (ProcBin *) alloc_heap(env, PROC_BIN_SIZE);
@@ -242,15 +432,67 @@ Eterm enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)
pb->flags = 0;
MSO(env->proc).overhead += pb->size / sizeof(Eterm);
- return make_binary(pb);
+ bin_term = make_binary(pb);
+ if (erts_refc_read(&bptr->refc, 1) == 1) {
+ /* Total ownership transfer */
+ bin->ref_bin = NULL;
+ bin->bin_term = bin_term;
+ }
+ return bin_term;
+ }
+ else {
+ enable_halloc(env);
+ bin->bin_term = new_binary(env->proc, bin->data, bin->size);
+ disable_halloc(env);
+ return bin->bin_term;
}
}
+Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term,
+ unsigned pos, unsigned size)
+{
+ ErlSubBin* sb;
+ Eterm orig;
+ Uint offset, bit_offset, bit_size;
+ unsigned src_size;
+
+ ASSERT(is_binary(bin_term));
+ src_size = binary_size(bin_term);
+ ASSERT(pos <= src_size);
+ ASSERT(size <= src_size);
+ ASSERT(pos + size <= src_size);
+ sb = (ErlSubBin*) alloc_heap(env, ERL_SUB_BIN_SIZE);
+ ERTS_GET_REAL_BIN(bin_term, orig, offset, bit_offset, bit_size);
+ sb->thing_word = HEADER_SUB_BIN;
+ sb->size = size;
+ sb->offs = offset + pos;
+ sb->orig = orig;
+ sb->bitoffs = bit_offset;
+ sb->bitsize = 0;
+ sb->is_writable = 0;
+ return make_binary(sb);
+}
+
+
Eterm enif_make_badarg(ErlNifEnv* env)
{
BIF_ERROR(env->proc, BADARG);
}
+int enif_get_atom(ErlNifEnv* env, Eterm atom, char* buf, unsigned len)
+{
+ Atom* ap;
+ if (is_not_atom(atom)) {
+ return 0;
+ }
+ ap = atom_tab(atom_val(atom));
+ if (ap->len+1 > len) {
+ return 0;
+ }
+ sys_memcpy(buf, ap->name, ap->len);
+ buf[ap->len] = '\0';
+ return ap->len + 1;
+}
int enif_get_int(ErlNifEnv* env, Eterm term, int* ip)
{
@@ -268,6 +510,29 @@ int enif_get_int(ErlNifEnv* env, Eterm term, int* ip)
#endif
}
+int enif_get_uint(ErlNifEnv* env, Eterm term, unsigned* ip)
+{
+#if SIZEOF_INT == SIZEOF_VOID_P
+ return term_to_Uint(term, (Uint*)ip);
+#elif SIZEOF_LONG == SIZEOF_VOID_P
+ Uint i;
+ if (!term_to_Uint(term, &i) || i > UINT_MAX) {
+ return 0;
+ }
+ *ip = (unsigned) i;
+ return 1;
+#endif
+}
+
+int enif_get_long(ErlNifEnv* env, Eterm term, long* ip)
+{
+#if SIZEOF_LONG == SIZEOF_VOID_P
+ return term_to_Sint(term, ip);
+#else
+# error Unknown long word size
+#endif
+}
+
int enif_get_ulong(ErlNifEnv* env, Eterm term, unsigned long* ip)
{
#if SIZEOF_LONG == SIZEOF_VOID_P
@@ -307,18 +572,26 @@ ERL_NIF_TERM enif_make_int(ErlNifEnv* env, int i)
#endif
}
-ERL_NIF_TERM enif_make_ulong(ErlNifEnv* env, unsigned long i)
+ERL_NIF_TERM enif_make_uint(ErlNifEnv* env, unsigned i)
{
-#if SIZEOF_LONG == SIZEOF_VOID_P
- Eterm* hp;
- Uint sz = 0;
- erts_bld_uint(NULL, &sz, i);
- hp = alloc_heap(env,sz);
- return erts_bld_uint(&hp, NULL, i);
-#else
+#if SIZEOF_INT == SIZEOF_VOID_P
+ return IS_USMALL(0,i) ? make_small(i) : uint_to_big(i,alloc_heap(env,2));
+#elif SIZEOF_LONG == SIZEOF_VOID_P
+ return make_small(i);
+#endif
+}
+
+ERL_NIF_TERM enif_make_long(ErlNifEnv* env, long i)
+{
+#if SIZEOF_LONG != SIZEOF_VOID_P
# error Unknown long word size
#endif
+ return IS_SSMALL(i) ? make_small(i) : small_to_big(i, alloc_heap(env,2));
+}
+ERL_NIF_TERM enif_make_ulong(ErlNifEnv* env, unsigned long i)
+{
+ return IS_USMALL(0,i) ? make_small(i) : uint_to_big(i,alloc_heap(env,2));
}
ERL_NIF_TERM enif_make_double(ErlNifEnv* env, double d)
@@ -355,6 +628,19 @@ ERL_NIF_TERM enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)
return ret;
}
+ERL_NIF_TERM enif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)
+{
+ Eterm* hp = alloc_heap(env,cnt+1);
+ Eterm ret = make_tuple(hp);
+ const Eterm* src = arr;
+
+ *hp++ = make_arityval(cnt);
+ while (cnt--) {
+ *hp++ = *src++;
+ }
+ return ret;
+}
+
ERL_NIF_TERM enif_make_list_cell(ErlNifEnv* env, Eterm car, Eterm cdr)
{
Eterm* hp = alloc_heap(env,2);
@@ -384,10 +670,29 @@ ERL_NIF_TERM enif_make_list(ErlNifEnv* env, unsigned cnt, ...)
return ret;
}
-ERL_NIF_TERM enif_make_string(ErlNifEnv* env, const char* string)
+ERL_NIF_TERM enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)
+{
+ Eterm* hp = alloc_heap(env,cnt*2);
+ Eterm ret = make_list(hp);
+ Eterm* last = &ret;
+ const Eterm* src = arr;
+
+ while (cnt--) {
+ *last = make_list(hp);
+ *hp = *src++;
+ last = ++hp;
+ ++hp;
+ }
+ *last = NIL;
+ return ret;
+}
+
+ERL_NIF_TERM enif_make_string(ErlNifEnv* env, const char* string,
+ ErlNifCharEncoding encoding)
{
Sint n = sys_strlen(string);
Eterm* hp = alloc_heap(env,n*2);
+ ASSERT(encoding == ERL_NIF_LATIN1);
return erts_bld_string_n(&hp,NULL,string,n);
}
@@ -397,32 +702,265 @@ ERL_NIF_TERM enif_make_ref(ErlNifEnv* env)
return erts_make_ref_in_buffer(hp);
}
-#if 0 /* To be continued... */
-typedef struct enif_handle_type_t
+void enif_system_info(ErlNifSysInfo *sip, size_t si_size)
{
- struct enif_handle_type_t* next;
- struct enif_handle_type_t* prev;
- const char* name;
- void (*dtor)(void* obj);
- erts_smp_atomic_t ref_cnt; /* num of handles of this type */
-}ErlNifHandleType;
+ driver_system_info(sip, si_size);
+}
-ErlNifHandleType*
-enif_create_handle_type(ErlNifEnv* env, const char* type_name,
- void (*dtor)(void *))
+
+ErlNifMutex* enif_mutex_create(char *name) { return erl_drv_mutex_create(name); }
+void enif_mutex_destroy(ErlNifMutex *mtx) { erl_drv_mutex_destroy(mtx); }
+int enif_mutex_trylock(ErlNifMutex *mtx) { return erl_drv_mutex_trylock(mtx); }
+void enif_mutex_lock(ErlNifMutex *mtx) { erl_drv_mutex_lock(mtx); }
+void enif_mutex_unlock(ErlNifMutex *mtx) { erl_drv_mutex_unlock(mtx); }
+ErlNifCond* enif_cond_create(char *name) { return erl_drv_cond_create(name); }
+void enif_cond_destroy(ErlNifCond *cnd) { erl_drv_cond_destroy(cnd); }
+void enif_cond_signal(ErlNifCond *cnd) { erl_drv_cond_signal(cnd); }
+void enif_cond_broadcast(ErlNifCond *cnd) { erl_drv_cond_broadcast(cnd); }
+void enif_cond_wait(ErlNifCond *cnd, ErlNifMutex *mtx) { erl_drv_cond_wait(cnd,mtx); }
+ErlNifRWLock* enif_rwlock_create(char *name) { return erl_drv_rwlock_create(name); }
+void enif_rwlock_destroy(ErlNifRWLock *rwlck) { erl_drv_rwlock_destroy(rwlck); }
+int enif_rwlock_tryrlock(ErlNifRWLock *rwlck) { return erl_drv_rwlock_tryrlock(rwlck); }
+void enif_rwlock_rlock(ErlNifRWLock *rwlck) { erl_drv_rwlock_rlock(rwlck); }
+void enif_rwlock_runlock(ErlNifRWLock *rwlck) { erl_drv_rwlock_runlock(rwlck); }
+int enif_rwlock_tryrwlock(ErlNifRWLock *rwlck) { return erl_drv_rwlock_tryrwlock(rwlck); }
+void enif_rwlock_rwlock(ErlNifRWLock *rwlck) { erl_drv_rwlock_rwlock(rwlck); }
+void enif_rwlock_rwunlock(ErlNifRWLock *rwlck) { erl_drv_rwlock_rwunlock(rwlck); }
+int enif_tsd_key_create(char *name, ErlNifTSDKey *key) { return erl_drv_tsd_key_create(name,key); }
+void enif_tsd_key_destroy(ErlNifTSDKey key) { erl_drv_tsd_key_destroy(key); }
+void enif_tsd_set(ErlNifTSDKey key, void *data) { erl_drv_tsd_set(key,data); }
+void* enif_tsd_get(ErlNifTSDKey key) { return erl_drv_tsd_get(key); }
+ErlNifThreadOpts* enif_thread_opts_create(char *name) { return (ErlNifThreadOpts*) erl_drv_thread_opts_create(name); }
+void enif_thread_opts_destroy(ErlNifThreadOpts *opts) { erl_drv_thread_opts_destroy((ErlDrvThreadOpts*)opts); }
+int enif_thread_create(char *name, ErlNifTid *tid, void* (*func)(void *),
+ void *args, ErlNifThreadOpts *opts) {
+ return erl_drv_thread_create(name,tid,func,args,(ErlDrvThreadOpts*)opts);
+}
+ErlNifTid enif_thread_self(void) { return erl_drv_thread_self(); }
+int enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2) { return erl_drv_equal_tids(tid1,tid2); }
+void enif_thread_exit(void *resp) { erl_drv_thread_exit(resp); }
+int enif_thread_join(ErlNifTid tid, void **respp) { return erl_drv_thread_join(tid,respp); }
+
+int enif_fprintf(void* filep, const char* format, ...)
+{
+ int ret;
+ va_list arglist;
+ va_start(arglist, format);
+ ret = erts_vfprintf((FILE*)filep, format, arglist);
+ va_end(arglist);
+ return ret;
+}
+
+/***********************************************************
+ ** Memory managed (GC'ed) "resource" objects **
+ ***********************************************************/
+
+
+struct enif_resource_type_t
{
+ struct enif_resource_type_t* next; /* list of all resource types */
+ struct enif_resource_type_t* prev;
+ struct erl_module_nif* owner; /* that created this type and thus implements the destructor*/
+ ErlNifResourceDtor* dtor; /* user destructor function */
+ erts_refc_t refc; /* num of resources of this type (HOTSPOT warning)
+ +1 for active erl_module_nif */
+ char name[1];
+};
+
+/* dummy node in circular list */
+struct enif_resource_type_t resource_type_list;
+
+typedef struct enif_resource_t
+{
+ struct enif_resource_type_t* type;
+#ifdef DEBUG
+ erts_refc_t nif_refc;
+#endif
+ char data[1];
+}ErlNifResource;
+
+#define SIZEOF_ErlNifResource(SIZE) (offsetof(ErlNifResource,data) + (SIZE))
+#define DATA_TO_RESOURCE(PTR) ((ErlNifResource*)((char*)(PTR) - offsetof(ErlNifResource,data)))
+static ErlNifResourceType* find_resource_type(const char* name)
+{
+ ErlNifResourceType* type;
+ for (type = resource_type_list.next;
+ type != &resource_type_list;
+ type = type->next) {
+
+ if (sys_strcmp(type->name, name) == 0) {
+ return type;
+ }
+ }
+ return NULL;
+}
+
+#define in_area(ptr,start,nbytes) \
+ ((unsigned long)((char*)(ptr) - (char*)(start)) < (nbytes))
+
+
+static void close_lib(struct erl_module_nif* lib)
+{
+ ASSERT(lib != NULL);
+ ASSERT(lib->handle != NULL);
+ ASSERT(erts_refc_read(&lib->rt_dtor_cnt,0) == 0);
+
+ if (lib->entry != NULL && lib->entry->unload != NULL) {
+ ErlNifEnv env;
+ pre_nif_noproc(&env, lib);
+ lib->entry->unload(&env, lib->priv_data);
+ post_nif_noproc(&env);
+ }
+ erts_sys_ddll_close(lib->handle);
+ lib->handle = NULL;
}
-ERL_NIF_TERM enif_make_handle(ErlNifEnv* env, ErlNifHandleType* htype, void* obj)
+static void steal_resource_type(ErlNifResourceType* type)
{
+ struct erl_module_nif* lib = type->owner;
+
+ if (type->dtor != NULL
+ && erts_refc_dectest(&lib->rt_dtor_cnt, 0) == 0
+ && lib->is_orphan) {
+ /* last type with destructor gone, close orphan lib */
+ close_lib(lib);
+ }
+ if (erts_refc_dectest(&lib->rt_cnt, 0) == 0
+ && lib->is_orphan) {
+ erts_free(ERTS_ALC_T_NIF, lib);
+ }
}
-int enif_get_handle(ErlNifEnv* env, ERL_NIF_TERM term, void** objp)
+ErlNifResourceType*
+enif_open_resource_type(ErlNifEnv* env, const char* type_name,
+ ErlNifResourceDtor* dtor,
+ enum ErlNifResourceFlags flags,
+ enum ErlNifResourceFlags* tried)
{
+ ErlNifResourceType* type = find_resource_type(type_name);
+ enum ErlNifResourceFlags op = flags;
+ ASSERT(erts_smp_is_system_blocked(0));
+ if (type == NULL) {
+ if (flags & ERL_NIF_RT_CREATE) {
+ type = erts_alloc(ERTS_ALC_T_NIF,
+ sizeof(struct enif_resource_type_t)
+ + sys_strlen(type_name));
+ type->dtor = dtor;
+ sys_strcpy(type->name, type_name);
+ erts_refc_init(&type->refc, 1);
+ type->owner = env->mod_nif;
+ type->prev = &resource_type_list;
+ type->next = resource_type_list.next;
+ type->next->prev = type;
+ type->prev->next = type;
+ op = ERL_NIF_RT_CREATE;
+ }
+ }
+ else {
+ if (flags & ERL_NIF_RT_TAKEOVER) {
+ steal_resource_type(type);
+ op = ERL_NIF_RT_TAKEOVER;
+ }
+ else {
+ type = NULL;
+ }
+ }
+ if (type != NULL) {
+ type->owner = env->mod_nif;
+ type->dtor = dtor;
+ if (type->dtor != NULL) {
+ erts_refc_inc(&type->owner->rt_dtor_cnt, 1);
+ }
+ erts_refc_inc(&type->owner->rt_cnt, 1);
+ }
+ if (tried != NULL) {
+ *tried = op;
+ }
+ return type;
+}
+
+static void nif_resource_dtor(Binary* bin)
+{
+ ErlNifResource* resource = (ErlNifResource*) ERTS_MAGIC_BIN_DATA(bin);
+ ErlNifResourceType* type = resource->type;
+ ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor);
+
+ if (type->dtor != NULL) {
+ ErlNifEnv env;
+ pre_nif_noproc(&env, type->owner);
+ type->dtor(&env,resource->data);
+ post_nif_noproc(&env);
+ }
+ if (erts_refc_dectest(&type->refc, 0) == 0) {
+ ASSERT(type->next == NULL);
+ ASSERT(type->owner != NULL);
+ ASSERT(type->owner->is_orphan);
+ steal_resource_type(type);
+ erts_free(ERTS_ALC_T_NIF, type);
+ }
}
+
+void* enif_alloc_resource(ErlNifEnv* env, ErlNifResourceType* type, unsigned size)
+{
+ Binary* bin = erts_create_magic_binary(SIZEOF_ErlNifResource(size), &nif_resource_dtor);
+ ErlNifResource* resource = ERTS_MAGIC_BIN_DATA(bin);
+ resource->type = type;
+ erts_refc_inc(&bin->refc, 1);
+#ifdef DEBUG
+ erts_refc_init(&resource->nif_refc, 1);
+#endif
+ erts_refc_inc(&resource->type->refc, 2);
+ return resource->data;
+}
+
+void enif_release_resource(ErlNifEnv* env, void* obj)
+{
+ ErlNifResource* resource = DATA_TO_RESOURCE(obj);
+ ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_DATA(resource);
+
+ ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor);
+#ifdef DEBUG
+ erts_refc_dec(&resource->nif_refc, 0);
#endif
+ if (erts_refc_dectest(&bin->binary.refc, 0) == 0) {
+ erts_bin_free(&bin->binary);
+ }
+}
+
+ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj)
+{
+ ErlNifResource* resource = DATA_TO_RESOURCE(obj);
+ ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_DATA(resource);
+ Eterm* hp = alloc_heap(env,PROC_BIN_SIZE);
+ return erts_mk_magic_binary_term(&hp, &MSO(env->proc), &bin->binary);
+}
+
+int enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type,
+ void** objp)
+{
+ Binary* mbin;
+ ErlNifResource* resource;
+ if (!ERTS_TERM_IS_MAGIC_BINARY(term)) {
+ return 0;
+ }
+ mbin = ((ProcBin*) binary_val(term))->val;
+ resource = (ErlNifResource*) ERTS_MAGIC_BIN_DATA(mbin);
+ if (ERTS_MAGIC_BIN_DESTRUCTOR(mbin) != &nif_resource_dtor
+ || resource->type != type) {
+ return 0;
+ }
+ *objp = resource->data;
+ return 1;
+}
+
+unsigned enif_sizeof_resource(ErlNifEnv* env, void* obj)
+{
+ ErlNifResource* resource = DATA_TO_RESOURCE(obj);
+ Binary* bin = &ERTS_MAGIC_BIN_FROM_DATA(resource)->binary;
+ return ERTS_MAGIC_BIN_DATA_SIZE(bin) - offsetof(ErlNifResource,data);
+}
/***************************************************************************
** load_nif/2 **
@@ -445,10 +983,7 @@ static Uint** get_func_pp(Eterm* mod_code, Eterm f_atom, unsigned arity)
return NULL;
}
-#define in_area(ptr,start,nbytes) \
- ((unsigned long)((char*)(ptr) - (char*)(start)) < (nbytes))
-
-static void refresh_cached_nif_data(Eterm* mod_code,
+/*static void refresh_cached_nif_data(Eterm* mod_code,
struct erl_module_nif* mod_nif)
{
int i;
@@ -459,9 +994,9 @@ static void refresh_cached_nif_data(Eterm* mod_code,
erts_atom_get(func->name, sys_strlen(func->name), &f_atom);
code_ptr = *get_func_pp(mod_code, f_atom, func->arity);
- code_ptr[5+2] = (Uint) mod_nif->data;
+ code_ptr[5+2] = (Uint) mod_nif->priv_data;
}
-}
+}*/
static Eterm mkatom(const char *str)
{
@@ -567,6 +1102,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT;
Eterm ret = am_ok;
int veto;
+ struct erl_module_nif* lib = NULL;
len = intlist_to_buf(BIF_ARG_1, lib_name, sizeof(lib_name)-1);
if (len < 1) {
@@ -652,16 +1188,27 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
/* Call load, reload or upgrade:
*/
- if (mod->nif.handle != NULL) { /* Reload */
+
+
+ lib = erts_alloc(ERTS_ALC_T_NIF, sizeof(struct erl_module_nif));
+ lib->handle = handle;
+ lib->entry = entry;
+ erts_refc_init(&lib->rt_cnt, 0);
+ erts_refc_init(&lib->rt_dtor_cnt, 0);
+ lib->is_orphan = 0;
+ env.mod_nif = lib;
+ if (mod->nif != NULL) { /* Reload */
int k;
- ASSERT(mod->nif.entry != NULL);
+ lib->priv_data = mod->nif->priv_data;
+
+ ASSERT(mod->nif->entry != NULL);
if (entry->reload == NULL) {
ret = load_nif_error(BIF_P,reload,"Reload not supported by this NIF library.");
goto error;
}
/* Check that no NIF is removed */
- for (k=0; k < mod->nif.entry->num_of_funcs; k++) {
- ErlNifFunc* old_func = &mod->nif.entry->funcs[k];
+ for (k=0; k < mod->nif->entry->num_of_funcs; k++) {
+ ErlNifFunc* old_func = &mod->nif->entry->funcs[k];
for (i=0; i < entry->num_of_funcs; i++) {
if (old_func->arity == entry->funcs[i].arity
&& sys_strcmp(old_func->name, entry->funcs[i].name) == 0) {
@@ -675,37 +1222,39 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
goto error;
}
}
- erts_pre_nif(&env, BIF_P, mod->nif.data);
- veto = entry->reload(&env, &env.nif_data, BIF_ARG_2);
+ erts_pre_nif(&env, BIF_P, lib);
+ veto = entry->reload(&env, &lib->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
ret = load_nif_error(BIF_P, reload, "Library reload-call unsuccessful.");
}
else {
- erts_sys_ddll_close(mod->nif.handle);
+ mod->nif->entry = NULL; /* to prevent 'unload' callback */
+ erts_unload_nif(mod->nif);
}
}
else {
- if (mod->old_nif.handle != NULL) { /* Upgrade */
- void* prev_old_data = mod->old_nif.data;
+ lib->priv_data = NULL;
+ if (mod->old_nif != NULL) { /* Upgrade */
+ void* prev_old_data = mod->old_nif->priv_data;
if (entry->upgrade == NULL) {
ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library.");
goto error;
}
- erts_pre_nif(&env, BIF_P, NULL);
- veto = entry->upgrade(&env, &env.nif_data, &mod->old_nif.data, BIF_ARG_2);
+ erts_pre_nif(&env, BIF_P, lib);
+ veto = entry->upgrade(&env, &lib->priv_data, &mod->old_nif->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
- mod->old_nif.data = prev_old_data;
+ mod->old_nif->priv_data = prev_old_data;
ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful.");
}
- else if (mod->old_nif.data != prev_old_data) {
- refresh_cached_nif_data(mod->old_code, &mod->old_nif);
- }
+ /*else if (mod->old_nif->priv_data != prev_old_data) {
+ refresh_cached_nif_data(mod->old_code, mod->old_nif);
+ }*/
}
else if (entry->load != NULL) { /* Initial load */
- erts_pre_nif(&env, BIF_P, NULL);
- veto = entry->load(&env, &env.nif_data, BIF_ARG_2);
+ erts_pre_nif(&env, BIF_P, lib);
+ veto = entry->load(&env, &lib->priv_data, BIF_ARG_2);
erts_post_nif(&env);
if (veto) {
ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful.");
@@ -716,9 +1265,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
/*
** Everything ok, patch the beam code with op_call_nif
*/
- mod->nif.data = env.nif_data;
- mod->nif.handle = handle;
- mod->nif.entry = entry;
+ mod->nif = lib;
for (i=0; i < entry->num_of_funcs; i++)
{
Uint* code_ptr;
@@ -727,17 +1274,21 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
if (code_ptr[1] == 0) {
code_ptr[5+0] = (Uint) BeamOp(op_call_nif);
- } else { /* Function traced, patch the original instruction word */
+ }
+ else { /* Function traced, patch the original instruction word */
BpData* bp = (BpData*) code_ptr[1];
bp->orig_instr = (Uint) BeamOp(op_call_nif);
}
code_ptr[5+1] = (Uint) entry->funcs[i].fptr;
- code_ptr[5+2] = (Uint) mod->nif.data;
+ code_ptr[5+2] = (Uint) lib;
}
}
else {
error:
ASSERT(ret != am_ok);
+ if (lib != NULL) {
+ erts_free(ERTS_ALC_T_NIF, lib);
+ }
if (handle != NULL) {
erts_sys_ddll_close(handle);
}
@@ -749,3 +1300,53 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
BIF_RET(ret);
}
+
+void
+erts_unload_nif(struct erl_module_nif* lib)
+{
+ ErlNifResourceType* rt;
+ ErlNifResourceType* next;
+ ASSERT(erts_smp_is_system_blocked(0));
+ ASSERT(lib != NULL);
+ ASSERT(!lib->is_orphan);
+ for (rt = resource_type_list.next;
+ rt != &resource_type_list;
+ rt = next) {
+
+ next = rt->next;
+ if (rt->owner == lib) {
+ rt->next->prev = rt->prev;
+ rt->prev->next = rt->next;
+ rt->next = NULL;
+ rt->prev = NULL;
+ if (erts_refc_dectest(&rt->refc, 0) == 0) {
+ if (rt->dtor != NULL) {
+ erts_refc_dec(&lib->rt_dtor_cnt, 0);
+ }
+ erts_refc_dec(&lib->rt_cnt, 0);
+ erts_free(ERTS_ALC_T_NIF, rt);
+ }
+ }
+ }
+ if (erts_refc_read(&lib->rt_dtor_cnt, 0) == 0) {
+ close_lib(lib);
+ if (erts_refc_read(&lib->rt_cnt, 0) == 0) {
+ erts_free(ERTS_ALC_T_NIF, lib);
+ return;
+ }
+ }
+ else {
+ ASSERT(erts_refc_read(&lib->rt_cnt, 1) > 0);
+ }
+ lib->is_orphan = 1;
+}
+
+void erl_nif_init()
+{
+ resource_type_list.next = &resource_type_list;
+ resource_type_list.prev = &resource_type_list;
+ resource_type_list.dtor = NULL;
+ resource_type_list.owner = NULL;
+ resource_type_list.name[0] = '\0';
+}
+
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index e5e6d65c0e..1ccf00293e 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -1,25 +1,30 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2009-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
/* Include file for writers of Native Implemented Functions.
*/
+#ifndef __ERL_NIF_H__
+#define __ERL_NIF_H__
+
+#include "erl_drv_nif.h"
+
/* Version history:
** 0.1: R13B03
** 1.0: R13B04
@@ -63,11 +68,32 @@ typedef struct
/* Internals (avert your eyes) */
ERL_NIF_TERM bin_term;
- unsigned char* tmp_alloc;
void* ref_bin;
-
}ErlNifBinary;
+typedef struct enif_resource_type_t ErlNifResourceType;
+typedef void ErlNifResourceDtor(ErlNifEnv*, void*);
+enum ErlNifResourceFlags
+{
+ ERL_NIF_RT_CREATE = 1,
+ ERL_NIF_RT_TAKEOVER = 2
+};
+
+typedef enum
+{
+ ERL_NIF_LATIN1 = 1
+}ErlNifCharEncoding;
+
+typedef ErlDrvSysInfo ErlNifSysInfo;
+
+typedef struct ErlDrvTid_ *ErlNifTid;
+typedef struct ErlDrvMutex_ ErlNifMutex;
+typedef struct ErlDrvCond_ ErlNifCond;
+typedef struct ErlDrvRWLock_ ErlNifRWLock;
+typedef int ErlNifTSDKey;
+
+typedef ErlDrvThreadOpts ErlNifThreadOpts;
+
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
# define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS
typedef struct {
@@ -124,3 +150,5 @@ ERL_NIF_INIT_DECL(NAME) \
return &entry; \
}
+#endif /* __ERL_NIF_H__ */
+
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index 7556806ce4..ec07a976b2 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2009-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
@@ -22,7 +22,7 @@
#endif
#ifdef ERL_NIF_API_FUNC_DECL
-ERL_NIF_API_FUNC_DECL(void*,enif_get_data,(ErlNifEnv*));
+ERL_NIF_API_FUNC_DECL(void*,enif_priv_data,(ErlNifEnv*));
ERL_NIF_API_FUNC_DECL(void*,enif_alloc,(ErlNifEnv*, size_t size));
ERL_NIF_API_FUNC_DECL(void,enif_free,(ErlNifEnv*, void* ptr));
ERL_NIF_API_FUNC_DECL(int,enif_is_atom,(ErlNifEnv*, ERL_NIF_TERM term));
@@ -36,7 +36,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_get_int,(ErlNifEnv*, ERL_NIF_TERM term, int* ip))
ERL_NIF_API_FUNC_DECL(int,enif_get_ulong,(ErlNifEnv*, ERL_NIF_TERM term, unsigned long* ip));
ERL_NIF_API_FUNC_DECL(int,enif_get_double,(ErlNifEnv*, ERL_NIF_TERM term, double* dp));
ERL_NIF_API_FUNC_DECL(int,enif_get_list_cell,(ErlNifEnv* env, ERL_NIF_TERM term, ERL_NIF_TERM* head, ERL_NIF_TERM* tail));
-ERL_NIF_API_FUNC_DECL(int,enif_get_tuple,(ErlNifEnv* env, ERL_NIF_TERM tpl, int* arity, ERL_NIF_TERM** array));
+ERL_NIF_API_FUNC_DECL(int,enif_get_tuple,(ErlNifEnv* env, ERL_NIF_TERM tpl, int* arity, const ERL_NIF_TERM** array));
ERL_NIF_API_FUNC_DECL(int,enif_is_identical,(ErlNifEnv* env, ERL_NIF_TERM lhs, ERL_NIF_TERM rhs));
ERL_NIF_API_FUNC_DECL(int,enif_compare,(ErlNifEnv* env, ERL_NIF_TERM lhs, ERL_NIF_TERM rhs));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_binary,(ErlNifEnv* env, ErlNifBinary* bin));
@@ -49,15 +49,69 @@ ERL_NIF_API_FUNC_DECL(int,enif_make_existing_atom,(ErlNifEnv* env, const char* n
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_tuple,(ErlNifEnv* env, unsigned cnt, ...));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_list,(ErlNifEnv* env, unsigned cnt, ...));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_list_cell,(ErlNifEnv* env, ERL_NIF_TERM car, ERL_NIF_TERM cdr));
-ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_string,(ErlNifEnv* env, const char* string));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_string,(ErlNifEnv* env, const char* string, ErlNifCharEncoding));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_ref,(ErlNifEnv* env));
+
+ERL_NIF_API_FUNC_DECL(ErlNifMutex*,enif_mutex_create,(char *name));
+ERL_NIF_API_FUNC_DECL(void,enif_mutex_destroy,(ErlNifMutex *mtx));
+ERL_NIF_API_FUNC_DECL(int,enif_mutex_trylock,(ErlNifMutex *mtx));
+ERL_NIF_API_FUNC_DECL(void,enif_mutex_lock,(ErlNifMutex *mtx));
+ERL_NIF_API_FUNC_DECL(void,enif_mutex_unlock,(ErlNifMutex *mtx));
+ERL_NIF_API_FUNC_DECL(ErlNifCond*,enif_cond_create,(char *name));
+ERL_NIF_API_FUNC_DECL(void,enif_cond_destroy,(ErlNifCond *cnd));
+ERL_NIF_API_FUNC_DECL(void,enif_cond_signal,(ErlNifCond *cnd));
+ERL_NIF_API_FUNC_DECL(void,enif_cond_broadcast,(ErlNifCond *cnd));
+ERL_NIF_API_FUNC_DECL(void,enif_cond_wait,(ErlNifCond *cnd, ErlNifMutex *mtx));
+ERL_NIF_API_FUNC_DECL(ErlNifRWLock*,enif_rwlock_create,(char *name));
+ERL_NIF_API_FUNC_DECL(void,enif_rwlock_destroy,(ErlNifRWLock *rwlck));
+ERL_NIF_API_FUNC_DECL(int,enif_rwlock_tryrlock,(ErlNifRWLock *rwlck));
+ERL_NIF_API_FUNC_DECL(void,enif_rwlock_rlock,(ErlNifRWLock *rwlck));
+ERL_NIF_API_FUNC_DECL(void,enif_rwlock_runlock,(ErlNifRWLock *rwlck));
+ERL_NIF_API_FUNC_DECL(int,enif_rwlock_tryrwlock,(ErlNifRWLock *rwlck));
+ERL_NIF_API_FUNC_DECL(void,enif_rwlock_rwlock,(ErlNifRWLock *rwlck));
+ERL_NIF_API_FUNC_DECL(void,enif_rwlock_rwunlock,(ErlNifRWLock *rwlck));
+ERL_NIF_API_FUNC_DECL(int,enif_tsd_key_create,(char *name, ErlNifTSDKey *key));
+ERL_NIF_API_FUNC_DECL(void,enif_tsd_key_destroy,(ErlNifTSDKey key));
+ERL_NIF_API_FUNC_DECL(void,enif_tsd_set,(ErlNifTSDKey key, void *data));
+ERL_NIF_API_FUNC_DECL(void*,enif_tsd_get,(ErlNifTSDKey key));
+ERL_NIF_API_FUNC_DECL(ErlNifThreadOpts*,enif_thread_opts_create,(char *name));
+ERL_NIF_API_FUNC_DECL(void,enif_thread_opts_destroy,(ErlNifThreadOpts *opts));
+ERL_NIF_API_FUNC_DECL(int,enif_thread_create,(char *name,ErlNifTid *tid,void * (*func)(void *),void *args,ErlNifThreadOpts *opts));
+ERL_NIF_API_FUNC_DECL(ErlNifTid,enif_thread_self,(void));
+ERL_NIF_API_FUNC_DECL(int,enif_equal_tids,(ErlNifTid tid1, ErlNifTid tid2));
+ERL_NIF_API_FUNC_DECL(void,enif_thread_exit,(void *resp));
+ERL_NIF_API_FUNC_DECL(int,enif_thread_join,(ErlNifTid, void **respp));
+
+ERL_NIF_API_FUNC_DECL(void*,enif_realloc,(ErlNifEnv*, void* ptr, size_t size));
+ERL_NIF_API_FUNC_DECL(void,enif_system_info,(ErlNifSysInfo *sip, size_t si_size));
+ERL_NIF_API_FUNC_DECL(int,enif_fprintf,(void/* FILE* */ *filep, const char *format, ...));
+ERL_NIF_API_FUNC_DECL(int,enif_inspect_iolist_as_binary,(ErlNifEnv*, ERL_NIF_TERM term, ErlNifBinary* bin));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_sub_binary,(ErlNifEnv*, ERL_NIF_TERM bin_term, unsigned pos, unsigned size));
+ERL_NIF_API_FUNC_DECL(int,enif_get_string,(ErlNifEnv*, ERL_NIF_TERM list, char* buf, unsigned len, ErlNifCharEncoding));
+ERL_NIF_API_FUNC_DECL(int,enif_get_atom,(ErlNifEnv*, ERL_NIF_TERM atom, char* buf, unsigned len));
+ERL_NIF_API_FUNC_DECL(int,enif_is_fun,(ErlNifEnv*, ERL_NIF_TERM term));
+ERL_NIF_API_FUNC_DECL(int,enif_is_pid,(ErlNifEnv*, ERL_NIF_TERM term));
+ERL_NIF_API_FUNC_DECL(int,enif_is_port,(ErlNifEnv*, ERL_NIF_TERM term));
+ERL_NIF_API_FUNC_DECL(int,enif_get_uint,(ErlNifEnv*, ERL_NIF_TERM term, unsigned* ip));
+ERL_NIF_API_FUNC_DECL(int,enif_get_long,(ErlNifEnv*, ERL_NIF_TERM term, long* ip));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_uint,(ErlNifEnv*, unsigned i));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_long,(ErlNifEnv*, long i));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_tuple_from_array,(ErlNifEnv*, const ERL_NIF_TERM arr[], unsigned cnt));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_list_from_array,(ErlNifEnv*, const ERL_NIF_TERM arr[], unsigned cnt));
+ERL_NIF_API_FUNC_DECL(int,enif_is_empty_list,(ErlNifEnv*, ERL_NIF_TERM term));
+ERL_NIF_API_FUNC_DECL(ErlNifResourceType*,enif_open_resource_type,(ErlNifEnv*, const char* type_name, void (*dtor)(ErlNifEnv*,void *), enum ErlNifResourceFlags flags, enum ErlNifResourceFlags* tried));
+ERL_NIF_API_FUNC_DECL(void*,enif_alloc_resource,(ErlNifEnv*, ErlNifResourceType* type, unsigned size));
+ERL_NIF_API_FUNC_DECL(void,enif_release_resource,(ErlNifEnv*, void* obj));
+ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_resource,(ErlNifEnv*, void* obj));
+ERL_NIF_API_FUNC_DECL(int,enif_get_resource,(ErlNifEnv*, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp));
+ERL_NIF_API_FUNC_DECL(unsigned,enif_sizeof_resource,(ErlNifEnv*, void* obj));
/*
** Add last to keep compatibility on Windows!!!
*/
#endif
#ifdef ERL_NIF_API_FUNC_MACRO
-# define enif_get_data ERL_NIF_API_FUNC_MACRO(enif_get_data)
+# define enif_priv_data ERL_NIF_API_FUNC_MACRO(enif_priv_data)
# define enif_alloc ERL_NIF_API_FUNC_MACRO(enif_alloc)
# define enif_free ERL_NIF_API_FUNC_MACRO(enif_free)
# define enif_is_atom ERL_NIF_API_FUNC_MACRO(enif_is_atom)
@@ -88,5 +142,84 @@ ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_ref,(ErlNifEnv* env));
# define enif_make_string ERL_NIF_API_FUNC_MACRO(enif_make_string)
# define enif_make_ref ERL_NIF_API_FUNC_MACRO(enif_make_ref)
+# define enif_mutex_create ERL_NIF_API_FUNC_MACRO(enif_mutex_create)
+# define enif_mutex_destroy ERL_NIF_API_FUNC_MACRO(enif_mutex_destroy)
+# define enif_mutex_trylock ERL_NIF_API_FUNC_MACRO(enif_mutex_trylock)
+# define enif_mutex_lock ERL_NIF_API_FUNC_MACRO(enif_mutex_lock)
+# define enif_mutex_unlock ERL_NIF_API_FUNC_MACRO(enif_mutex_unlock)
+# define enif_cond_create ERL_NIF_API_FUNC_MACRO(enif_cond_create)
+# define enif_cond_destroy ERL_NIF_API_FUNC_MACRO(enif_cond_destroy)
+# define enif_cond_signal ERL_NIF_API_FUNC_MACRO(enif_cond_signal)
+# define enif_cond_broadcast ERL_NIF_API_FUNC_MACRO(enif_cond_broadcast)
+# define enif_cond_wait ERL_NIF_API_FUNC_MACRO(enif_cond_wait)
+# define enif_rwlock_create ERL_NIF_API_FUNC_MACRO(enif_rwlock_create)
+# define enif_rwlock_destroy ERL_NIF_API_FUNC_MACRO(enif_rwlock_destroy)
+# define enif_rwlock_tryrlock ERL_NIF_API_FUNC_MACRO(enif_rwlock_tryrlock)
+# define enif_rwlock_rlock ERL_NIF_API_FUNC_MACRO(enif_rwlock_rlock)
+# define enif_rwlock_runlock ERL_NIF_API_FUNC_MACRO(enif_rwlock_runlock)
+# define enif_rwlock_tryrwlock ERL_NIF_API_FUNC_MACRO(enif_rwlock_tryrwlock)
+# define enif_rwlock_rwlock ERL_NIF_API_FUNC_MACRO(enif_rwlock_rwlock)
+# define enif_rwlock_rwunlock ERL_NIF_API_FUNC_MACRO(enif_rwlock_rwunlock)
+# define enif_tsd_key_create ERL_NIF_API_FUNC_MACRO(enif_tsd_key_create)
+# define enif_tsd_key_destroy ERL_NIF_API_FUNC_MACRO(enif_tsd_key_destroy)
+# define enif_tsd_set ERL_NIF_API_FUNC_MACRO(enif_tsd_set)
+# define enif_tsd_get ERL_NIF_API_FUNC_MACRO(enif_tsd_get)
+# define enif_thread_opts_create ERL_NIF_API_FUNC_MACRO(enif_thread_opts_create)
+# define enif_thread_opts_destroy ERL_NIF_API_FUNC_MACRO(enif_thread_opts_destroy)
+# define enif_thread_create ERL_NIF_API_FUNC_MACRO(enif_thread_create)
+# define enif_thread_self ERL_NIF_API_FUNC_MACRO(enif_thread_self)
+# define enif_equal_tids ERL_NIF_API_FUNC_MACRO(enif_equal_tids)
+# define enif_thread_exit ERL_NIF_API_FUNC_MACRO(enif_thread_exit)
+# define enif_thread_join ERL_NIF_API_FUNC_MACRO(enif_thread_join)
+
+# define enif_realloc ERL_NIF_API_FUNC_MACRO(enif_realloc)
+# define enif_system_info ERL_NIF_API_FUNC_MACRO(enif_system_info)
+# define enif_fprintf ERL_NIF_API_FUNC_MACRO(enif_fprintf)
+# define enif_inspect_iolist_as_binary ERL_NIF_API_FUNC_MACRO(enif_inspect_iolist_as_binary)
+# define enif_make_sub_binary ERL_NIF_API_FUNC_MACRO(enif_make_sub_binary)
+# define enif_get_string ERL_NIF_API_FUNC_MACRO(enif_get_string)
+# define enif_get_atom ERL_NIF_API_FUNC_MACRO(enif_get_atom)
+# define enif_is_fun ERL_NIF_API_FUNC_MACRO(enif_is_fun)
+# define enif_is_pid ERL_NIF_API_FUNC_MACRO(enif_is_pid)
+# define enif_is_port ERL_NIF_API_FUNC_MACRO(enif_is_port)
+# define enif_get_uint ERL_NIF_API_FUNC_MACRO(enif_get_uint)
+# define enif_get_long ERL_NIF_API_FUNC_MACRO(enif_get_long)
+# define enif_make_uint ERL_NIF_API_FUNC_MACRO(enif_make_uint)
+# define enif_make_long ERL_NIF_API_FUNC_MACRO(enif_make_long)
+# define enif_make_tuple_from_array ERL_NIF_API_FUNC_MACRO(enif_make_tuple_from_array)
+# define enif_make_list_from_array ERL_NIF_API_FUNC_MACRO(enif_make_list_from_array)
+# define enif_is_empty_list ERL_NIF_API_FUNC_MACRO(enif_is_empty_list)
+# define enif_open_resource_type ERL_NIF_API_FUNC_MACRO(enif_open_resource_type)
+# define enif_alloc_resource ERL_NIF_API_FUNC_MACRO(enif_alloc_resource)
+# define enif_release_resource ERL_NIF_API_FUNC_MACRO(enif_release_resource)
+# define enif_make_resource ERL_NIF_API_FUNC_MACRO(enif_make_resource)
+# define enif_get_resource ERL_NIF_API_FUNC_MACRO(enif_get_resource)
+# define enif_sizeof_resource ERL_NIF_API_FUNC_MACRO(enif_sizeof_resource)
+
+#endif
+
+#ifndef enif_make_list1
+# define enif_make_list1(ENV,E1) enif_make_list(ENV,1,E1)
+# define enif_make_list2(ENV,E1,E2) enif_make_list(ENV,2,E1,E2)
+# define enif_make_list3(ENV,E1,E2,E3) enif_make_list(ENV,3,E1,E2,E3)
+# define enif_make_list4(ENV,E1,E2,E3,E4) enif_make_list(ENV,4,E1,E2,E3,E4)
+# define enif_make_list5(ENV,E1,E2,E3,E4,E5) enif_make_list(ENV,5,E1,E2,E3,E4,E5)
+# define enif_make_list6(ENV,E1,E2,E3,E4,E5,E6) enif_make_list(ENV,6,E1,E2,E3,E4,E5,E6)
+# define enif_make_list7(ENV,E1,E2,E3,E4,E5,E6,E7) enif_make_list(ENV,7,E1,E2,E3,E4,E5,E6,E7)
+# define enif_make_list8(ENV,E1,E2,E3,E4,E5,E6,E7,E8) enif_make_list(ENV,8,E1,E2,E3,E4,E5,E6,E7,E8)
+# define enif_make_list9(ENV,E1,E2,E3,E4,E5,E6,E7,E8,E9) enif_make_list(ENV,9,E1,E2,E3,E4,E5,E6,E7,E8,E9)
+# define enif_make_tuple1(ENV,E1) enif_make_tuple(ENV,1,E1)
+# define enif_make_tuple2(ENV,E1,E2) enif_make_tuple(ENV,2,E1,E2)
+# define enif_make_tuple3(ENV,E1,E2,E3) enif_make_tuple(ENV,3,E1,E2,E3)
+# define enif_make_tuple4(ENV,E1,E2,E3,E4) enif_make_tuple(ENV,4,E1,E2,E3,E4)
+# define enif_make_tuple5(ENV,E1,E2,E3,E4,E5) enif_make_tuple(ENV,5,E1,E2,E3,E4,E5)
+# define enif_make_tuple6(ENV,E1,E2,E3,E4,E5,E6) enif_make_tuple(ENV,6,E1,E2,E3,E4,E5,E6)
+# define enif_make_tuple7(ENV,E1,E2,E3,E4,E5,E6,E7) enif_make_tuple(ENV,7,E1,E2,E3,E4,E5,E6,E7)
+# define enif_make_tuple8(ENV,E1,E2,E3,E4,E5,E6,E7,E8) enif_make_tuple(ENV,8,E1,E2,E3,E4,E5,E6,E7,E8)
+# define enif_make_tuple9(ENV,E1,E2,E3,E4,E5,E6,E7,E8,E9) enif_make_tuple(ENV,9,E1,E2,E3,E4,E5,E6,E7,E8,E9)
+#endif
+
+#ifndef enif_get_data
+# define enif_get_data enif_priv_data /* deprecated */
#endif
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index b011d4c0de..099eddd195 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -1027,11 +1027,12 @@ static uLongf binary2term_uncomp_size(byte* data, Sint size)
err = inflateInit(&stream);
if (err == Z_OK) {
- while ((err = inflate(&stream, Z_NO_FLUSH)) == Z_OK) {
- uncomp_size += chunk_size - stream.avail_out;
+ do {
stream.next_out = tmp_buf;
- stream.avail_out = chunk_size;
- }
+ stream.avail_out = chunk_size;
+ err = inflate(&stream, Z_NO_FLUSH);
+ uncomp_size += chunk_size - stream.avail_out;
+ }while (err == Z_OK);
inflateEnd(&stream);
}
erts_free(ERTS_ALC_T_TMP, tmp_buf);
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 8def007c63..cab249a53f 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -76,19 +76,29 @@ typedef struct line_buf { /* Buffer used in line oriented I/O */
The rest is the overflow buffer. */
} LineBuf;
+/* Temporary object header, auto-deallocated when NIF returns. */
+struct enif_tmp_obj_t {
+ struct enif_tmp_obj_t* next;
+ void (*dtor)(struct enif_tmp_obj_t*);
+ /*char data[];*/
+};
struct enif_environment_t /* ErlNifEnv */
{
- void* nif_data;
+ struct erl_module_nif* mod_nif;
Process* proc;
Eterm* hp;
Eterm* hp_end;
- unsigned heap_frag_sz;
+ ErlHeapFragment* heap_frag;
int fpe_was_unmasked;
+ struct enif_tmp_obj_t* tmp_obj_list;
};
-extern void erts_pre_nif(struct enif_environment_t*, Process*, void* nif_data);
+extern void erts_pre_nif(struct enif_environment_t*, Process*,
+ struct erl_module_nif*);
extern void erts_post_nif(struct enif_environment_t* env);
extern Eterm erts_nif_taints(Process* p);
-extern void erts_print_nif_taints(int to, void* to_arg);
+extern void erts_print_nif_taints(int to, void* to_arg);
+void erts_unload_nif(struct erl_module_nif* nif);
+extern void erl_nif_init(void);
/*
* Port Specific Data.
@@ -403,6 +413,9 @@ typedef struct binary {
char orig_bytes[1]; /* to be continued */
} Binary;
+#define ERTS_SIZEOF_Binary(Sz) \
+ (offsetof(Binary,orig_bytes) + (Sz))
+
typedef struct {
ERTS_INTERNAL_BINARY_FIELDS
long orig_size;
@@ -435,7 +448,9 @@ typedef union {
#define ERTS_MAGIC_BIN_ORIG_SIZE(Sz) \
(sizeof(void (*)(Binary *)) + (Sz))
#define ERTS_MAGIC_BIN_SIZE(Sz) \
- (sizeof(ErtsMagicBinary) - 1 + (Sz))
+ (offsetof(ErtsMagicBinary,magic_bin_data) + (Sz))
+#define ERTS_MAGIC_BIN_FROM_DATA(DATA) \
+ ((ErtsBinary*)((char*)(DATA) - offsetof(ErtsMagicBinary,magic_bin_data)))
#define Binary2ErlDrvBinary(B) (&((ErtsBinary *) (B))->driver.binary)
#define ErlDrvBinary2Binary(D) ((Binary *) \
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index c5de180cb2..ad0b909b2a 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -31,6 +31,7 @@
/* must be included BEFORE global.h (since it includes erl_driver.h) */
#include "erl_sys_driver.h"
+#include "erl_nif.h"
#include "erl_vm.h"
#include "global.h"
@@ -1078,7 +1079,7 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list)
}
cbin = driver_alloc_binary(csize);
if (!cbin)
- erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY, sizeof(Binary) + csize);
+ erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY, ERTS_SIZEOF_Binary(csize));
/* Element 0 is for driver usage to add header block */
ivp[0].iov_base = NULL;
@@ -4473,7 +4474,14 @@ driver_system_info(ErlDrvSysInfo *sip, size_t si_size)
sip->async_threads = erts_async_max_threads;
sip->scheduler_threads = erts_no_schedulers;
}
-
+ /*
+ * 'nif_minor_version' is the last field in the third version
+ * (driver version 1.5, NIF version 1.0)
+ */
+ if (si_size >= ERL_DRV_SYS_INFO_SIZE(nif_minor_version)) {
+ sip->nif_major_version = ERL_NIF_MAJOR_VERSION;
+ sip->nif_minor_version = ERL_NIF_MINOR_VERSION;
+ }
}
diff --git a/erts/emulator/beam/module.c b/erts/emulator/beam/module.c
index 57a43c89f4..91e4ccce70 100644
--- a/erts/emulator/beam/module.c
+++ b/erts/emulator/beam/module.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1996-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
@@ -66,12 +66,8 @@ static Module* module_alloc(Module* tmpl)
obj->code_length = 0;
obj->old_code_length = 0;
obj->slot.index = -1;
- obj->nif.handle = NULL;
- obj->old_nif.handle = NULL;
- obj->nif.entry = NULL;
- obj->old_nif.entry = NULL;
- obj->nif.data = NULL;
- obj->old_nif.data = NULL;
+ obj->nif = NULL;
+ obj->old_nif = NULL;
return obj;
}
diff --git a/erts/emulator/beam/module.h b/erts/emulator/beam/module.h
index 314be8e2ee..87d13b3607 100644
--- a/erts/emulator/beam/module.h
+++ b/erts/emulator/beam/module.h
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1996-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
@@ -24,11 +24,6 @@
#include "index.h"
#endif
-struct erl_module_nif {
- void* handle;
- struct enif_entry_t* entry;
- void* data;
-};
typedef struct erl_module {
IndexSlot slot; /* Must be located at top of struct! */
@@ -39,8 +34,8 @@ typedef struct erl_module {
int code_length; /* Length of loaded code in bytes. */
int old_code_length; /* Length of old loaded code in bytes */
unsigned catches, old_catches;
- struct erl_module_nif nif;
- struct erl_module_nif old_nif;
+ struct erl_module_nif* nif;
+ struct erl_module_nif* old_nif;
} Module;
Module* erts_get_module(Eterm mod);
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 687e6fa67b..31efddc0f2 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -124,26 +124,28 @@ erts_heap_alloc(Process* p, Uint need)
#endif /* FORCE_HEAP_FRAGS */
n = need;
+ bp = MBUF(p);
+ if (bp != NULL && need <= (bp->size - bp->used_size)) {
+ Eterm* ret = bp->mem + bp->used_size;
+ bp->used_size += need;
+ return ret;
+ }
#ifdef DEBUG
n++;
#endif
bp = (ErlHeapFragment*)
ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP_FRAG, ERTS_HEAP_FRAG_SIZE(n));
-#ifdef DEBUG
- n--;
-#endif
-
-#if defined(DEBUG)
- for (i = 0; i <= n; i++) {
- bp->mem[i] = ERTS_HOLE_MARKER;
- }
-#elif defined(CHECK_FOR_HOLES)
+#if defined(DEBUG) || defined(CHECK_FOR_HOLES)
for (i = 0; i < n; i++) {
bp->mem[i] = ERTS_HOLE_MARKER;
}
#endif
+#ifdef DEBUG
+ n--;
+#endif
+
/*
* When we have created a heap fragment, we are no longer allowed
* to store anything more on the heap.
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index e47161fcbc..522caec8f1 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -1,38 +1,50 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(nif_SUITE).
%%-define(line_trace,true).
+%%-define(CHECK(Exp,Got), ?line check(Exp,Got,?LINE)).
+-define(CHECK(Exp,Got), ?line Exp = Got).
-include("test_server.hrl").
-export([all/1, fin_per_testcase/2, basic/1, reload/1, upgrade/1, heap_frag/1,
- types/1, many_args/1, neg/1]).
+ types/1, many_args/1, binaries/1, get_string/1, get_atom/1, api_macros/1,
+ from_array/1, iolist_as_binary/1, resource/1, resource_takeover/1,
+ threading/1, neg/1]).
-export([many_args_100/100]).
-define(nif_stub,nif_stub_error(?LINE)).
all(suite) ->
- [basic, reload, upgrade, heap_frag, types, many_args, neg].
+ [basic, reload, upgrade, heap_frag, types, many_args, binaries, get_string,
+ get_atom, api_macros, from_array, iolist_as_binary, resource,
+ resource_takeover, threading, neg].
+
+%%init_per_testcase(_Case, Config) ->
+%% ?line Dog = ?t:timetrap(?t:seconds(60*60*24)),
+%% [{watchdog, Dog}|Config].
fin_per_testcase(_Func, _Config) ->
+ %%Dog = ?config(watchdog, Config),
+ %%?t:timetrap_cancel(Dog),
P1 = code:purge(nif_mod),
Del = code:delete(nif_mod),
P2 = code:purge(nif_mod),
@@ -51,6 +63,7 @@ basic(Config) when is_list(Config) ->
reload(doc) -> ["Test reload callback in nif lib"];
reload(suite) -> [];
reload(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
ensure_lib_loaded(Config),
?line Data = ?config(data_dir, Config),
@@ -58,16 +71,16 @@ reload(Config) when is_list(Config) ->
?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
- ?line nif_mod:load_nif_lib(Config, 1),
+ ?line ok = nif_mod:load_nif_lib(Config, 1),
?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
- ?line nif_mod:load_nif_lib(Config, 2),
+ ?line ok = nif_mod:load_nif_lib(Config, 2),
?line 2 = nif_mod:lib_version(),
?line [{reload,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(),
- ?line nif_mod:load_nif_lib(Config, 1),
+ ?line ok = nif_mod:load_nif_lib(Config, 1),
?line 1 = nif_mod:lib_version(),
?line [{reload,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
@@ -79,11 +92,13 @@ reload(Config) when is_list(Config) ->
?line [{unload,1,3,103}] = nif_mod_call_history(),
?line [?MODULE, nif_mod] = erlang:system_info(taints),
+ ?line verify_tmpmem(TmpMem),
ok.
upgrade(doc) -> ["Test upgrade callback in nif lib"];
upgrade(suite) -> [];
upgrade(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
ensure_lib_loaded(Config),
?line Data = ?config(data_dir, Config),
@@ -91,7 +106,7 @@ upgrade(Config) when is_list(Config) ->
?line {ok,nif_mod,Bin} = compile:file(File, [binary,return_errors]),
?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
- ?line nif_mod:load_nif_lib(Config, 1),
+ ?line ok = nif_mod:load_nif_lib(Config, 1),
?line {Pid,MRef} = nif_mod:start(),
?line 1 = call(Pid,lib_version),
@@ -104,7 +119,7 @@ upgrade(Config) when is_list(Config) ->
?line 1 = call(Pid,lib_version),
?line [{lib_version,1,4,104}] = nif_mod_call_history(),
- ?line nif_mod:load_nif_lib(Config, 1),
+ ?line ok = nif_mod:load_nif_lib(Config, 1),
?line 1 = nif_mod:lib_version(),
?line [{upgrade,1,5,105},{lib_version,1,6,106}] = nif_mod_call_history(),
@@ -131,7 +146,7 @@ upgrade(Config) when is_list(Config) ->
?line {Pid2,MRef2} = nif_mod:start(),
?line undefined = call(Pid2,lib_version),
- ?line nif_mod:load_nif_lib(Config, 1),
+ ?line ok = nif_mod:load_nif_lib(Config, 1),
?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
?line 1 = call(Pid2,lib_version),
?line [{load,1,1,101},{get_priv_data_ptr,1,2,102},{lib_version,1,3,103}] = nif_mod_call_history(),
@@ -142,7 +157,7 @@ upgrade(Config) when is_list(Config) ->
?line 1 = call(Pid2,lib_version),
?line [{lib_version,1,4,104}] = nif_mod_call_history(),
- ?line nif_mod:load_nif_lib(Config, 2),
+ ?line ok = nif_mod:load_nif_lib(Config, 2),
?line 2 = nif_mod:lib_version(),
?line [{upgrade,2,1,201},{lib_version,2,2,202}] = nif_mod_call_history(),
@@ -167,14 +182,17 @@ upgrade(Config) when is_list(Config) ->
?line [{unload,2,4,204}] = nif_mod_call_history(),
?line [?MODULE, nif_mod] = erlang:system_info(taints),
+ ?line verify_tmpmem(TmpMem),
ok.
heap_frag(doc) -> ["Test NIF building heap fragments"];
heap_frag(suite) -> [];
heap_frag(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
ensure_lib_loaded(Config),
heap_frag_do(1,1000000),
+ ?line verify_tmpmem(TmpMem),
ok.
heap_frag_do(N, Max) when N > Max ->
@@ -188,6 +206,7 @@ heap_frag_do(N, Max) ->
types(doc) -> ["Type tests"];
types(suite) -> [];
types(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
ensure_lib_loaded(Config),
?line ok = type_test(),
lists:foreach(fun(Tpl) ->
@@ -197,6 +216,7 @@ types(Config) when is_list(Config) ->
[{},{ok},{{}},{[],{}},{1,2,3,4,5}]),
Stuff = [[],{},0,0.0,(1 bsl 100),(fun()-> ok end),make_ref(),self()],
[eq_cmp(A,clone(B)) || A<-Stuff, B<-Stuff],
+ ?line verify_tmpmem(TmpMem),
ok.
clone(X) ->
@@ -227,17 +247,506 @@ eq_cmp_do(A,B) ->
many_args(doc) -> ["Test NIF with many arguments"];
many_args(suite) -> [];
many_args(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
?line ensure_lib_loaded(Config ,1),
?line ok = apply(?MODULE,many_args_100,lists:seq(1,100)),
?line ok = many_args_100(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100),
+ ?line verify_tmpmem(TmpMem),
ok.
-
+binaries(doc) -> ["Test NIF binary handling."];
+binaries(suite) -> [];
+binaries(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
+ ?line ensure_lib_loaded(Config, 1),
+ ?line RefcBin = list_to_binary(lists:seq(1,255)),
+ ?line RefcBin = clone_bin(RefcBin),
+ ?line HeapBin = list_to_binary(lists:seq(1,20)),
+ ?line HeapBin = clone_bin(HeapBin),
+ ?line <<_:8,Sub1:6/binary,_/binary>> = RefcBin,
+ ?line <<_:8,Sub2:6/binary,_/binary>> = HeapBin,
+ ?line Sub1 = Sub2,
+ ?line Sub1 = clone_bin(Sub1),
+ ?line Sub2 = clone_bin(Sub2),
+ ?line <<_:9,Sub3:6/binary,_/bitstring>> = RefcBin,
+ ?line <<_:9,Sub4:6/binary,_/bitstring>> = HeapBin,
+ ?line Sub3 = Sub4,
+ ?line Sub3 = clone_bin(Sub3),
+ ?line Sub4 = clone_bin(Sub4),
+ %% When NIFs get bitstring support
+ %%?line <<_:8,Sub5:27/bitstring,_/bitstring>> = RefcBin,
+ %%?line <<_:8,Sub6:27/bitstring,_/bitstring>> = HeapBin,
+ %%?line Sub5 = Sub6,
+ %%?line Sub5 = clone_bin(Sub5),
+ %%?line Sub6 = clone_bin(Sub6),
+ %%?line <<_:9,Sub7:27/bitstring,_/bitstring>> = RefcBin,
+ %%?line <<_:9,Sub8:27/bitstring,_/bitstring>> = HeapBin,
+ %%?line Sub7 = Sub8,
+ %%?line Sub7 = clone_bin(Sub7),
+ %%?line Sub8 = clone_bin(Sub8),
+ %%?line <<>> = clone_bin(<<>>),
+
+ <<_:8,SubBinA:200/binary,_/binary>> = RefcBin,
+ <<_:9,SubBinB:200/binary,_/bitstring>> = RefcBin,
+ <<_:8,SubBinC:17/binary,_/binary>> = HeapBin,
+ <<_:9,SubBinD:17/binary,_/bitstring>> = HeapBin,
+ test_make_sub_bin(RefcBin),
+ test_make_sub_bin(HeapBin),
+ test_make_sub_bin(SubBinA),
+ test_make_sub_bin(SubBinB),
+ test_make_sub_bin(SubBinC),
+ test_make_sub_bin(SubBinD),
+
+ ?line verify_tmpmem(TmpMem),
+ ok.
+
+test_make_sub_bin(Bin) ->
+ Size = byte_size(Bin),
+ Rest10 = Size - 10,
+ Rest1 = Size - 1,
+ ?line Bin = make_sub_bin(Bin, 0, Size),
+ <<_:10/binary,Sub0:Rest10/binary>> = Bin,
+ ?line Sub0 = make_sub_bin(Bin, 10, Rest10),
+ <<Sub1:10/binary,_/binary>> = Bin,
+ ?line Sub1 = make_sub_bin(Bin, 0, 10),
+ <<_:7/binary,Sub2:10/binary,_/binary>> = Bin,
+ ?line Sub2 = make_sub_bin(Bin, 7, 10),
+ ?line <<>> = make_sub_bin(Bin, 0, 0),
+ ?line <<>> = make_sub_bin(Bin, 10, 0),
+ ?line <<>> = make_sub_bin(Bin, Rest1, 0),
+ ?line <<>> = make_sub_bin(Bin, Size, 0),
+ ok.
+
+get_string(doc) -> ["Test enif_get_string"];
+get_string(suite) -> [];
+get_string(Config) when is_list(Config) ->
+ ?line ensure_lib_loaded(Config, 1),
+ ?line {7, <<"hejsan",0,_:3/binary>>} = string_to_bin("hejsan",10),
+ ?line {7, <<"hejsan",0,_>>} = string_to_bin("hejsan",8),
+ ?line {7, <<"hejsan",0>>} = string_to_bin("hejsan",7),
+ ?line {-6, <<"hejsa",0>>} = string_to_bin("hejsan",6),
+ ?line {-5, <<"hejs",0>>} = string_to_bin("hejsan",5),
+ ?line {-1, <<0>>} = string_to_bin("hejsan",1),
+ ?line {0, <<>>} = string_to_bin("hejsan",0),
+ ?line {1, <<0>>} = string_to_bin("",1),
+ ?line {0, <<>>} = string_to_bin("",0),
+ ok.
+
+get_atom(doc) -> ["Test enif_get_atom"];
+get_atom(suite) -> [];
+get_atom(Config) when is_list(Config) ->
+ ?line ensure_lib_loaded(Config, 1),
+ ?line {7, <<"hejsan",0,_:3/binary>>} = atom_to_bin(hejsan,10),
+ ?line {7, <<"hejsan",0,_>>} = atom_to_bin(hejsan,8),
+ ?line {7, <<"hejsan",0>>} = atom_to_bin(hejsan,7),
+ ?line {0, <<_:6/binary>>} = atom_to_bin(hejsan,6),
+ ?line {0, <<>>} = atom_to_bin(hejsan,0),
+ ?line {1, <<0>>} = atom_to_bin('',1),
+ ?line {0, <<>>} = atom_to_bin('',0),
+ ok.
+
+api_macros(doc) -> ["Test macros enif_make_list<N> and enif_make_tuple<N>"];
+api_macros(suite) -> [];
+api_macros(Config) when is_list(Config) ->
+ ?line ensure_lib_loaded(Config, 1),
+ Expected = {[lists:seq(1,N) || N <- lists:seq(1,9)],
+ [list_to_tuple(lists:seq(1,N)) || N <- lists:seq(1,9)]
+ },
+ ?line Expected = macros(list_to_tuple(lists:seq(1,9))),
+ ok.
+
+from_array(doc) -> ["enif_make_[tuple|list]_from_array"];
+from_array(suite) -> [];
+from_array(Config) when is_list(Config) ->
+ ?line ensure_lib_loaded(Config, 1),
+ lists:foreach(fun(Tpl) ->
+ Lst = tuple_to_list(Tpl),
+ ?line {Lst,Tpl} = tuple_2_list_and_tuple(Tpl)
+ end,
+ [{}, {1,2,3}, {[4,5],[],{},{6,7}}, {{}}, {[]}]),
+ ok.
+
+iolist_as_binary(doc) -> ["enif_inspect_iolist_as_binary"];
+iolist_as_binary(suite) -> [];
+iolist_as_binary(Config) when is_list(Config) ->
+ ?line ensure_lib_loaded(Config, 1),
+ TmpMem = tmpmem(),
+ List = [<<"hejsan">>, <<>>, [], [17], [<<>>],
+ [127,128,255,0],
+ [1, 2, 3, <<"abc">>, [<<"def">>,4], 5, <<"ghi">>],
+ [1, 2, 3, <<"abc">>, [<<"def">>,4], 5 | <<"ghi">>]],
+
+ lists:foreach(fun(IoL) ->
+ B1 = erlang:iolist_to_binary(IoL),
+ ?line B2 = iolist_2_bin(IoL),
+ ?line B1 = B2
+ end,
+ List),
+ ?line verify_tmpmem(TmpMem),
+ ok.
+
+resource(doc) -> ["Test memory managed objects, aka 'resources'"];
+resource(suite) -> [];
+resource(Config) when is_list(Config) ->
+ ?line ensure_lib_loaded(Config, 1),
+ ?line Type = get_resource_type(0),
+ resource_hugo(Type),
+ resource_otto(Type),
+ resource_new(Type),
+ resource_neg(Type),
+ ok.
+
+resource_hugo(Type) ->
+ DtorCall = resource_hugo_do(Type),
+ erlang:garbage_collect(),
+ ?line DtorCall = last_resource_dtor_call(),
+ ok.
+
+resource_hugo_do(Type) ->
+ HugoBin = <<"Hugo Hacker">>,
+ ?line HugoPtr = alloc_resource(Type, HugoBin),
+ ?line Hugo = make_resource(HugoPtr),
+ ?line <<>> = Hugo,
+ release_resource(HugoPtr),
+ erlang:garbage_collect(),
+ ?line {HugoPtr,HugoBin} = get_resource(Type,Hugo),
+ Pid = spawn_link(fun() ->
+ receive {Pid, Type, Resource, Ptr, Bin} ->
+ Pid ! {self(), got_it},
+ receive {Pid, check_it} ->
+ ?line {Ptr,Bin} = get_resource(Type,Resource),
+ Pid ! {self(), ok}
+ end
+ end
+ end),
+ Pid ! {self(), Type, Hugo, HugoPtr, HugoBin},
+ ?line {Pid, got_it} = receive_any(),
+ erlang:garbage_collect(), % just to make our ProcBin move in memory
+ Pid ! {self(), check_it},
+ ?line {Pid, ok} = receive_any(),
+ ?line [] = last_resource_dtor_call(),
+ ?line {HugoPtr,HugoBin} = get_resource(Type,Hugo),
+ {HugoPtr, HugoBin, 1}.
+
+resource_otto(Type) ->
+ {OttoPtr, OttoBin} = resource_otto_do(Type),
+ erlang:garbage_collect(),
+ ?line [] = last_resource_dtor_call(),
+ release_resource(OttoPtr),
+ ?line {OttoPtr,OttoBin,1} = last_resource_dtor_call(),
+ ok.
+
+resource_otto_do(Type) ->
+ OttoBin = <<"Otto Ordonnans">>,
+ ?line OttoPtr = alloc_resource(Type, OttoBin),
+ ?line Otto = make_resource(OttoPtr),
+ ?line <<>> = Otto,
+ %% forget resource term but keep referenced by NIF
+ {OttoPtr, OttoBin}.
+
+resource_new(Type) ->
+ ?line {PtrB,BinB} = resource_new_do1(Type),
+ erlang:garbage_collect(),
+ ?line {PtrB,BinB,1} = last_resource_dtor_call(),
+ ok.
+
+resource_new_do1(Type) ->
+ ?line {{PtrA,BinA}, {ResB,PtrB,BinB}} = resource_new_do2(Type),
+ erlang:garbage_collect(),
+ ?line {PtrA,BinA,1} = last_resource_dtor_call(),
+ ?line {PtrB,BinB} = get_resource(Type, ResB),
+ %% forget ResB and make it garbage
+ {PtrB,BinB}.
+
+resource_new_do2(Type) ->
+ BinA = <<"NewA">>,
+ BinB = <<"NewB">>,
+ ?line ResA = make_new_resource(Type, BinA),
+ ?line ResB = make_new_resource(Type, BinB),
+ ?line <<>> = ResA,
+ ?line <<>> = ResB,
+ ?line {PtrA,BinA} = get_resource(Type, ResA),
+ ?line {PtrB,BinB} = get_resource(Type, ResB),
+ ?line true = (PtrA =/= PtrB),
+ ?line [] = last_resource_dtor_call(),
+ %% forget ResA and make it garbage
+ {{PtrA,BinA}, {ResB,PtrB,BinB}}.
+
+resource_neg(TypeA) ->
+ TypeB = get_resource_type(1),
+ Aptr = alloc_resource(TypeA, <<"Arnold">>),
+ Bptr = alloc_resource(TypeB, <<"Bobo">>),
+ ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeA, Bptr)),
+ ?line {'EXIT',{badarg,_}} = (catch get_resource(TypeB, Aptr)),
+ ok.
+-define(RT_CREATE,1).
+-define(RT_TAKEOVER,2).
+
+resource_takeover(doc) -> ["Test resource takeover by module reload and upgrade"];
+resource_takeover(suite) -> [];
+resource_takeover(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
+ ensure_lib_loaded(Config),
+
+ ?line Data = ?config(data_dir, Config),
+ ?line File = filename:join(Data, "nif_mod"),
+ ?line {ok,nif_mod,ModBin} = compile:file(File, [binary,return_errors]),
+ ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
+
+ ?line ok = nif_mod:load_nif_lib(Config, 1,
+ [{resource_type, 0, ?RT_CREATE, "resource_type_A",resource_dtor_A,
+ ?RT_CREATE},
+ {resource_type, 1, ?RT_CREATE, "resource_type_null_A",null,
+ ?RT_CREATE},
+ {resource_type, 2, ?RT_CREATE bor ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A,
+ ?RT_CREATE},
+ {resource_type, 3, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B,
+ ?RT_CREATE},
+ {resource_type, 4, ?RT_CREATE, "resource_type_null_goneX",null,
+ ?RT_CREATE},
+ {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A,
+ ?RT_TAKEOVER}
+ ]),
+
+ ?line hold_nif_mod_priv_data(nif_mod:get_priv_data_ptr()),
+ ?line [{load,1,1,101},{get_priv_data_ptr,1,2,102}] = nif_mod_call_history(),
+
+ ?line {Holder, _MRef} = spawn_opt(fun resource_holder/0, [link, monitor]),
+
+ {A1,BinA1} = make_resource(0,Holder,"A1"),
+ {A2,BinA2} = make_resource(0,Holder,"A2"),
+ {A3,BinA3} = make_resource(0,Holder,"A3"),
+
+ {NA1,_BinNA1} = make_resource(1,Holder,"NA1"),
+ {NA2,BinNA2} = make_resource(1,Holder,"NA2"),
+ {NA3,_BinNA3} = make_resource(1,Holder,"NA3"),
+
+ {AN1,BinAN1} = make_resource(2,Holder,"AN1"),
+ {AN2,_BinAN2} = make_resource(2,Holder,"AN2"),
+ {AN3,BinAN3} = make_resource(2,Holder,"AN3"),
+
+ {BGX1,BinBGX1} = make_resource(3,Holder,"BGX1"),
+ {BGX2,BinBGX2} = make_resource(3,Holder,"BGX2"),
+
+ {NGX1,_BinNGX1} = make_resource(4,Holder,"NGX1"),
+ {NGX2,_BinNGX2} = make_resource(4,Holder,"NGX2"),
+
+ ?line [] = nif_mod_call_history(),
+
+ ?line ok = forget_resource(A1),
+ ?line [{{resource_dtor_A_v1,BinA1},1,3,103}] = nif_mod_call_history(),
+
+ ?line ok = forget_resource(NA1),
+ ?line [] = nif_mod_call_history(), % no dtor
+
+ ?line ok = forget_resource(AN1),
+ ?CHECK([{{resource_dtor_A_v1,BinAN1},1,4,104}] , nif_mod_call_history()),
+
+ ?line ok = forget_resource(BGX1),
+ ?CHECK([{{resource_dtor_B_v1,BinBGX1},1,5,105}], nif_mod_call_history()),
+
+ ?line ok = forget_resource(NGX1),
+ ?CHECK([], nif_mod_call_history()), % no dtor
+
+ ?line ok = nif_mod:load_nif_lib(Config, 2,
+ [{resource_type, 0, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, 1, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, 2, ?RT_TAKEOVER, "resource_type_A_null",null,
+ ?RT_TAKEOVER},
+ {resource_type, null, ?RT_TAKEOVER, "Pink unicorn", resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, null, ?RT_CREATE, "resource_type_B_goneX",resource_dtor_B,
+ ?RT_CREATE},
+ {resource_type, null, ?RT_CREATE, "resource_type_null_goneX",null,
+ ?RT_CREATE},
+ {resource_type, 3, ?RT_CREATE, "resource_type_B_goneY",resource_dtor_B,
+ ?RT_CREATE},
+ {resource_type, 4, ?RT_CREATE, "resource_type_null_goneY",null,
+ ?RT_CREATE}
+ ]),
+ ?CHECK([{reload,2,1,201}], nif_mod_call_history()),
+
+ ?line BinA2 = read_resource(0,A2),
+ ?line ok = forget_resource(A2),
+ ?CHECK([{{resource_dtor_A_v2,BinA2},2,2,202}], nif_mod_call_history()),
+
+ ?line ok = forget_resource(NA2),
+ ?CHECK([{{resource_dtor_A_v2,BinNA2},2,3,203}], nif_mod_call_history()),
+
+ ?line ok = forget_resource(AN2),
+ ?CHECK([], nif_mod_call_history()), % no dtor
+
+ ?line ok = forget_resource(BGX2), % calling dtor in orphan library v1 still loaded
+ ?CHECK([{{resource_dtor_B_v1,BinBGX2},1,6,106}], nif_mod_call_history()),
+ % How to test that lib v1 is closed here?
+
+ ?line ok = forget_resource(NGX2),
+ ?CHECK([], nif_mod_call_history()), % no dtor
+
+ {BGY1,BinBGY1} = make_resource(3,Holder,"BGY1"),
+ {NGY1,_BinNGY1} = make_resource(4,Holder,"NGY1"),
+
+ %% Module upgrade with same lib-version
+ ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
+ ?line undefined = nif_mod:lib_version(),
+ ?line ok = nif_mod:load_nif_lib(Config, 2,
+ [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_B,
+ ?RT_TAKEOVER},
+ {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",null,
+ ?RT_TAKEOVER},
+ {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, null, ?RT_TAKEOVER, "Pink elephant", resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, 3, ?RT_CREATE, "resource_type_B_goneZ",resource_dtor_B,
+ ?RT_CREATE},
+ {resource_type, 4, ?RT_CREATE, "resource_type_null_goneZ",null,
+ ?RT_CREATE}
+ ]),
+
+ ?line 2 = nif_mod:lib_version(),
+ ?CHECK([{upgrade,2,4,204},{lib_version,2,5,205}], nif_mod_call_history()),
+
+ ?line ok = forget_resource(A3),
+ ?CHECK([{{resource_dtor_B_v2,BinA3},2,6,206}], nif_mod_call_history()),
+
+ ?line ok = forget_resource(NA3),
+ ?CHECK([], nif_mod_call_history()),
+
+ ?line ok = forget_resource(AN3),
+ ?CHECK([{{resource_dtor_A_v2,BinAN3},2,7,207}], nif_mod_call_history()),
+
+ {A4,BinA4} = make_resource(2,Holder, "A4"),
+ {NA4,BinNA4} = make_resource(0,Holder, "NA4"),
+ {AN4,_BinAN4} = make_resource(1,Holder, "AN4"),
+
+ {BGZ1,BinBGZ1} = make_resource(3,Holder,"BGZ1"),
+ {NGZ1,_BinNGZ1} = make_resource(4,Holder,"NGZ1"),
+
+ ?line false = code:purge(nif_mod),
+ ?line [] = nif_mod_call_history(),
+
+ ?line ok = forget_resource(NGY1),
+ ?line [] = nif_mod_call_history(),
+
+ ?line ok = forget_resource(BGY1), % calling dtor in orphan library v2 still loaded
+ ?line [{{resource_dtor_B_v2,BinBGY1},2,8,208},{unload,2,9,209}] = nif_mod_call_history(),
+
+ %% Module upgrade with other lib-version
+ ?line {module,nif_mod} = erlang:load_module(nif_mod,ModBin),
+ ?line undefined = nif_mod:lib_version(),
+ ?line ok = nif_mod:load_nif_lib(Config, 1,
+ [{resource_type, 2, ?RT_TAKEOVER, "resource_type_A",resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, 0, ?RT_TAKEOVER bor ?RT_CREATE, "resource_type_null_A",resource_dtor_A,
+ ?RT_TAKEOVER},
+ {resource_type, 1, ?RT_TAKEOVER, "resource_type_A_null",null,
+ ?RT_TAKEOVER},
+ {resource_type, null, ?RT_TAKEOVER, "Mr Pink", resource_dtor_A,
+ ?RT_TAKEOVER}
+ ]),
+
+ ?line 1 = nif_mod:lib_version(),
+ ?line [{upgrade,1,1,101},{lib_version,1,2,102}] = nif_mod_call_history(),
+
+ %%?line false= check_process_code(Pid, nif_mod),
+ ?line false = code:purge(nif_mod),
+ %% no unload here as we still have instances with destructors
+ ?line [] = nif_mod_call_history(),
+
+ ?line ok = forget_resource(BGZ1), % calling dtor in orphan library v2 still loaded
+ ?line [{{resource_dtor_B_v2,BinBGZ1},2,10,210},{unload,2,11,211}] = nif_mod_call_history(),
+
+ ?line ok = forget_resource(NGZ1),
+ ?line [] = nif_mod_call_history(),
+
+ ?line ok = forget_resource(A4),
+ ?line [{{resource_dtor_A_v1,BinA4},1,3,103}] = nif_mod_call_history(),
+
+ ?line ok = forget_resource(NA4),
+ ?line [{{resource_dtor_A_v1,BinNA4},1,4,104}] = nif_mod_call_history(),
+ ?line ok = forget_resource(AN4),
+ ?line [] = nif_mod_call_history(),
+
+ ?line [?MODULE, nif_mod] = erlang:system_info(taints),
+ ?line verify_tmpmem(TmpMem),
+ ok.
+
+make_resource(Type,Holder,Str) when is_list(Str) ->
+ Bin = list_to_binary(Str),
+ A1 = make_resource_do(Type,Holder,Bin),
+ ?line Bin = read_resource(Type,A1),
+ {A1,Bin}.
+
+make_resource_do(Type, Holder, Bin) ->
+ Holder ! {self(), make, Type, Bin},
+ {Holder, make_ok, Id} = receive_any(),
+ {Holder,Id}.
+
+read_resource(Type, {Holder,Id}) ->
+ Holder ! {self(), get, Type, Id},
+ {Holder, get_ok, Bin} = receive_any(),
+ Bin.
+
+forget_resource({Holder,Id}) ->
+ Holder ! {self(), forget, Id},
+ {Holder, forget_ok, Id} = receive_any(),
+ ok.
+
+
+resource_holder() ->
+ resource_holder([]).
+resource_holder(List) ->
+ %%io:format("resource_holder waiting for msg\n", []),
+ Msg = receive_any(),
+ %%io:format("resource_holder got ~p with list = ~p\n", [Msg,List]),
+ case Msg of
+ {Pid, make, Type, Bin} ->
+ ?line Resource = nif_mod:make_new_resource(Type, Bin),
+ Id = {make_ref(),Bin},
+ Pid ! {self(), make_ok, Id},
+ resource_holder([{Id,Resource} | List]);
+ {Pid, get, Type, Id} ->
+ {Id,Resource} = lists:keyfind(Id, 1, List),
+ Pid ! {self(), get_ok, nif_mod:get_resource(Type, Resource)},
+ resource_holder(List);
+
+ {Pid, forget, Id} ->
+ NewList = lists:keydelete(Id, 1, List),
+ %%io:format("resource_holder forget: NewList = ~p\n", [NewList]),
+ resource_holder(Pid, {self(),forget_ok,Id}, NewList)
+ end.
+
+resource_holder(Pid,Reply,List) ->
+ erlang:garbage_collect(),
+ %%io:format("resource_holder GC'ed, now send ~p to ~p\n", [Reply,Pid]),
+ Pid ! Reply,
+ resource_holder(List).
+
+
+threading(doc) -> ["Test the threading API functions (reuse tests from driver API)"];
+threading(Config) when is_list(Config) ->
+ ?line Data = ?config(data_dir, Config),
+ ?line File = filename:join(Data, "tester"),
+ ?line {ok,tester,ModBin} = compile:file(File, [binary,return_errors]),
+ ?line {module,tester} = erlang:load_module(tester,ModBin),
+
+ ?line ok = tester:load_nif_lib(Config, "basic"),
+ ?line ok = tester:run(),
+
+ ?line ok = tester:load_nif_lib(Config, "rwlock"),
+ ?line ok = tester:run(),
+
+ ?line ok = tester:load_nif_lib(Config, "tsd"),
+ ?line ok = tester:run().
+
neg(doc) -> ["Negative testing of load_nif"];
-neg(suite) -> [];
neg(Config) when is_list(Config) ->
+ TmpMem = tmpmem(),
?line {'EXIT',{badarg,_}} = (catch erlang:load_nif(badarg, 0)),
?line {error,{load_failed,_}} = erlang:load_nif("pink_unicorn", 0),
@@ -247,23 +756,61 @@ neg(Config) when is_list(Config) ->
?line {module,nif_mod} = erlang:load_module(nif_mod,Bin),
?line {error,{bad_lib,_}} = nif_mod:load_nif_lib(Config, no_init),
+ ?line verify_tmpmem(TmpMem),
?line ok.
ensure_lib_loaded(Config) ->
ensure_lib_loaded(Config, 1).
-
ensure_lib_loaded(Config, Ver) ->
?line case lib_version() of
undefined ->
?line Path = ?config(data_dir, Config),
?line Lib = "nif_SUITE." ++ integer_to_list(Ver),
- ?line ok = erlang:load_nif(filename:join(Path,Lib), 0);
+ ?line ok = erlang:load_nif(filename:join(Path,Lib), []);
Ver when is_integer(Ver) ->
ok
end.
+tmpmem() ->
+ case erlang:system_info({allocator,temp_alloc}) of
+ false -> undefined;
+ MemInfo ->
+ MSBCS = lists:foldl(
+ fun ({instance, _, L}, Acc) ->
+ {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L),
+ {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L),
+ [MBCS,SBCS | Acc]
+ end,
+ [],
+ MemInfo),
+ lists:foldl(
+ fun(L, {Bl0,BlSz0}) ->
+ {value,{_,Bl,_,_}} = lists:keysearch(blocks, 1, L),
+ {value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L),
+ {Bl0+Bl,BlSz0+BlSz}
+ end, {0,0}, MSBCS)
+ end.
+
+verify_tmpmem(MemInfo) ->
+ %%wait_for_test_procs(),
+ case tmpmem() of
+ MemInfo ->
+ io:format("Tmp mem info: ~p", [MemInfo]),
+ case MemInfo of
+ {notsup,undefined} ->
+ %% Use 'erl +Mea max' to do more complete memory leak testing.
+ {comment,"Incomplete or no mem leak testing"};
+ _ ->
+ ok
+ end;
+ Other ->
+ io:format("Expected: ~p", [MemInfo]),
+ io:format("Actual: ~p", [Other]),
+ ?t:fail()
+ end.
+
call(Pid,Cmd) ->
%%io:format("~p calling ~p with ~p\n",[self(), Pid, Cmd]),
Pid ! {self(), Cmd},
@@ -274,6 +821,15 @@ call(Pid,Cmd) ->
receive_any() ->
receive M -> M end.
+%% check(Exp,Got,Line) ->
+%% case Got of
+%% Exp -> Exp;
+%% _ ->
+%% io:format("CHECK at ~p: Expected ~p but got ~p\n",[Line,Exp,Got]),
+%% Got
+%% end.
+
+
%% The NIFs:
lib_version() -> undefined.
call_history() -> ?nif_stub.
@@ -285,6 +841,20 @@ tuple_2_list(_) -> ?nif_stub.
is_identical(_,_) -> ?nif_stub.
compare(_,_) -> ?nif_stub.
many_args_100(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub.
-
+clone_bin(_) -> ?nif_stub.
+make_sub_bin(_,_,_) -> ?nif_stub.
+string_to_bin(_,_) -> ?nif_stub.
+atom_to_bin(_,_) -> ?nif_stub.
+macros(_) -> ?nif_stub.
+tuple_2_list_and_tuple(_) -> ?nif_stub.
+iolist_2_bin(_) -> ?nif_stub.
+get_resource_type(_) -> ?nif_stub.
+alloc_resource(_,_) -> ?nif_stub.
+make_resource(_) -> ?nif_stub.
+get_resource(_,_) -> ?nif_stub.
+release_resource(_) -> ?nif_stub.
+last_resource_dtor_call() -> ?nif_stub.
+make_new_resource(_,_) -> ?nif_stub.
+
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/nif_SUITE_data/Makefile.src b/erts/emulator/test/nif_SUITE_data/Makefile.src
index 6a8b4f1245..ab4ff77add 100644
--- a/erts/emulator/test/nif_SUITE_data/Makefile.src
+++ b/erts/emulator/test/nif_SUITE_data/Makefile.src
@@ -4,11 +4,22 @@ NIF_LIBS = nif_SUITE.1@dll@ \
nif_mod.2@dll@ \
nif_mod.3@dll@
-all: $(NIF_LIBS)
+all: $(NIF_LIBS) basic@dll@ rwlock@dll@ tsd@dll@
@SHLIB_RULES@
$(NIF_LIBS): nif_SUITE.c nif_mod.c nif_mod.h
+basic@dll@: tester.c testcase_driver.h
+rwlock@dll@: tester.c testcase_driver.h
+
+tsd@dll@: tester.c testcase_driver.h
+
+DRIVER_DIR = ../erl_drv_thread_SUITE_data
+
+basic.c rwlock.c tsd.c: $(DRIVER_DIR)/$@
+ cat head.txt > $@
+ cat $(DRIVER_DIR)/$@ | sed -e 's/erl_drv_/enif_/g' -e 's/driver_/enif_/g' -e 's/ErlDrv/ErlNif/g' >> $@
+ cat tail.txt >> $@
diff --git a/erts/emulator/test/nif_SUITE_data/head.txt b/erts/emulator/test/nif_SUITE_data/head.txt
new file mode 100644
index 0000000000..4b9b44276f
--- /dev/null
+++ b/erts/emulator/test/nif_SUITE_data/head.txt
@@ -0,0 +1,5 @@
+/* Do NOT edit this file!!!
+**
+** This is a NIF'ified COPY of the original in ../erl_drv_thread_SUITE_data/
+*/
+
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 4532062dce..7d05a9a880 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -15,6 +15,7 @@ typedef struct
int ref_cnt;
CallInfo* call_history;
NifModPrivData* nif_mod;
+ union { ErlNifResourceType* t; long l; } rt_arr[2];
}PrivData;
void add_call(ErlNifEnv* env, PrivData* data, const char* func_name)
@@ -26,12 +27,29 @@ void add_call(ErlNifEnv* env, PrivData* data, const char* func_name)
call->static_cntA = ++static_cntA;
call->static_cntB = ++static_cntB;
data->call_history = call;
+ call->arg = NULL;
+ call->arg_sz = 0;
}
#define ADD_CALL(FUNC_NAME) add_call(env, enif_get_data(env),FUNC_NAME)
+static void* resource_dtor_last = NULL;
+static unsigned resource_dtor_last_sz = 0;
+static char resource_dtor_last_data[20];
+static int resource_dtor_cnt = 0;
+
+static void resource_dtor(ErlNifEnv* env, void* obj)
+{
+ resource_dtor_last = obj;
+ resource_dtor_cnt++;
+ resource_dtor_last_sz = enif_sizeof_resource(env, obj);
+ assert(resource_dtor_last_sz <= sizeof(resource_dtor_last_data));
+ memcpy(resource_dtor_last_data, obj, resource_dtor_last_sz);
+}
+
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
+ /*ERL_NIF_TERM head, tail;*/
PrivData* data = enif_alloc(env, sizeof(PrivData));
assert(data != NULL);
data->ref_cnt = 1;
@@ -39,7 +57,26 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
data->nif_mod = NULL;
add_call(env, data, "load");
-
+
+ /*
+ head = load_info;
+ data->rt_cnt = 0;
+ for (head=load_info; enif_get_list_cell(env,load_info,&head,&tail);
+ head=tail) {
+ char buf[20];
+ int n = enif_get_string(env,head,buf,sizeof(buf));
+ assert(n > 0);
+ assert(i < sizeof(data->rt_arr)/sizeof(*data->rt_arr));
+ data->rt_arr[data->rt_cnt++].t = enif_create_resource_type(env,buf,resource_dtor,
+ ERL_NIF_RT_CREATE,NULL);
+ }
+ assert(enif_is_empty_list(env,head));
+ */
+ data->rt_arr[0].t = enif_open_resource_type(env,"Gold",resource_dtor,
+ ERL_NIF_RT_CREATE,NULL);
+ data->rt_arr[1].t = enif_open_resource_type(env,"Silver",resource_dtor,
+ ERL_NIF_RT_CREATE,NULL);
+
*priv_data = data;
return 0;
}
@@ -80,11 +117,19 @@ static ERL_NIF_TERM make_call_history(ErlNifEnv* env, CallInfo** headp)
while (*headp != NULL) {
CallInfo* call = *headp;
- ERL_NIF_TERM tpl = enif_make_tuple(env, 4,
- enif_make_atom(env,call->func_name),
- enif_make_int(env,call->lib_ver),
- enif_make_int(env,call->static_cntA),
- enif_make_int(env,call->static_cntB));
+ ERL_NIF_TERM func_term = enif_make_atom(env,call->func_name);
+ ERL_NIF_TERM tpl;
+ if (call->arg != NULL) {
+ ErlNifBinary arg_bin;
+ enif_alloc_binary(env, call->arg_sz, &arg_bin);
+ memcpy(arg_bin.data, call->arg, call->arg_sz);
+ func_term = enif_make_tuple2(env, func_term,
+ enif_make_binary(env, &arg_bin));
+ }
+ tpl = enif_make_tuple4(env, func_term,
+ enif_make_int(env,call->lib_ver),
+ enif_make_int(env,call->static_cntA),
+ enif_make_int(env,call->static_cntB));
list = enif_make_list_cell(env, tpl, list);
*headp = call->next;
enif_free(env,call);
@@ -117,11 +162,14 @@ static ERL_NIF_TERM hold_nif_mod_priv_data(ErlNifEnv* env, int argc, const ERL_N
static ERL_NIF_TERM nif_mod_call_history(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
PrivData* data = (PrivData*) enif_get_data(env);
-
+ ERL_NIF_TERM ret;
if (data->nif_mod == NULL) {
- return enif_make_string(env,"nif_mod pointer is NULL");
+ return enif_make_string(env,"nif_mod pointer is NULL", ERL_NIF_LATIN1);
}
- return make_call_history(env,&data->nif_mod->call_history);
+ enif_mutex_lock(data->nif_mod->mtx);
+ ret = make_call_history(env, &data->nif_mod->call_history);
+ enif_mutex_unlock(data->nif_mod->mtx);
+ return ret;
}
static ERL_NIF_TERM list_seq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -150,12 +198,34 @@ static int test_int(ErlNifEnv* env, int i1)
return 1;
}
+static int test_uint(ErlNifEnv* env, unsigned i1)
+{
+ unsigned i2 = 0;
+ ERL_NIF_TERM int_term = enif_make_uint(env, i1);
+ if (!enif_get_uint(env,int_term, &i2) || i1 != i2) {
+ fprintf(stderr, "test_uint(%u) ...FAILED i2=%u\r\n", i1, i2);
+ return 0;
+ }
+ return 1;
+}
+
+static int test_long(ErlNifEnv* env, long i1)
+{
+ long i2 = 0;
+ ERL_NIF_TERM int_term = enif_make_long(env, i1);
+ if (!enif_get_long(env,int_term, &i2) || i1 != i2) {
+ fprintf(stderr, "test_long(%ld) ...FAILED i2=%ld\r\n", i1, i2);
+ return 0;
+ }
+ return 1;
+}
+
static int test_ulong(ErlNifEnv* env, unsigned long i1)
{
unsigned long i2 = 0;
ERL_NIF_TERM int_term = enif_make_ulong(env, i1);
if (!enif_get_ulong(env,int_term, &i2) || i1 != i2) {
- fprintf(stderr, "SVERK: test_ulong(%lu) ...FAILED i2=%lu\r\n", i1, i2);
+ fprintf(stderr, "test_ulong(%lu) ...FAILED i2=%lu\r\n", i1, i2);
return 0;
}
return 1;
@@ -166,7 +236,7 @@ static int test_double(ErlNifEnv* env, double d1)
double d2 = 0;
ERL_NIF_TERM term = enif_make_double(env, d1);
if (!enif_get_double(env,term, &d2) || d1 != d2) {
- fprintf(stderr, "SVERK: test_double(%e) ...FAILED i2=%e\r\n", d1, d2);
+ fprintf(stderr, "test_double(%e) ...FAILED i2=%e\r\n", d1, d2);
return 0;
}
return 1;
@@ -180,32 +250,59 @@ static int test_double(ErlNifEnv* env, double d1)
static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int i;
- unsigned long u;
+ int sint;
+ unsigned uint;
+ long slong;
+ unsigned long ulong;
double d;
ERL_NIF_TERM atom, ref1, ref2;
- i = INT_MIN;
+ sint = INT_MIN;
do {
- if (!test_int(env,i)) {
+ if (!test_int(env,sint)) {
goto error;
}
- i += ~i / 3 + 1;
- } while (i < 0);
- i = INT_MAX;
+ sint += ~sint / 3 + 1;
+ } while (sint < 0);
+ sint = INT_MAX;
do {
- if (!test_int(env,i)) {
+ if (!test_int(env,sint)) {
goto error;
}
- i -= i / 3 + 1;
- } while (i >= 0);
+ sint -= sint / 3 + 1;
+ } while (sint >= 0);
- u = ULONG_MAX;
+ slong = LONG_MIN;
+ do {
+ if (!test_long(env,slong)) {
+ goto error;
+ }
+ slong += ~slong / 3 + 1;
+ } while (slong < 0);
+ slong = LONG_MAX;
+ do {
+ if (!test_long(env,slong)) {
+ goto error;
+ }
+ slong -= slong / 3 + 1;
+ } while (slong >= 0);
+
+
+ uint = UINT_MAX;
for (;;) {
- if (!test_ulong(env,u)) {
+ if (!test_uint(env,uint)) {
}
- if (u == 0) break;
- u -= u / 3 + 1;
+ if (uint == 0) break;
+ uint -= uint / 3 + 1;
+ }
+ ulong = ULONG_MAX;
+ for (;;) {
+ if (!test_ulong(env,ulong)) {
+
+ }
+ if (ulong == 0) break;
+ ulong -= ulong / 3 + 1;
}
if (MAX_SMALL < INT_MAX) { /* 32-bit */
@@ -219,11 +316,17 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
goto error;
}
}
+ for (i=-10 ; i <= 10; i++) {
+ if (!test_uint(env,MAX_SMALL+i)) {
+ goto error;
+ }
+ }
}
assert((MAX_SMALL < INT_MAX) == (MIN_SMALL > INT_MIN));
- for (u=0 ; u < 10; u++) {
- if (!test_ulong(env,MAX_SMALL+u) || !test_ulong(env,MAX_SMALL-u)) {
+ for (i=-10 ; i < 10; i++) {
+ if (!test_long(env,MAX_SMALL+i) || !test_ulong(env,MAX_SMALL+i) ||
+ !test_long(env,MIN_SMALL+i)) {
goto error;
}
}
@@ -236,12 +339,12 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
if (!enif_make_existing_atom(env,"nif_SUITE", &atom)
|| !enif_is_identical(env,atom,enif_make_atom(env,"nif_SUITE"))) {
- fprintf(stderr, "SVERK: nif_SUITE not an atom?\r\n");
+ fprintf(stderr, "nif_SUITE not an atom?\r\n");
goto error;
}
for (i=2; i; i--) {
if (enif_make_existing_atom(env,"nif_SUITE_pink_unicorn", &atom)) {
- fprintf(stderr, "SVERK: pink unicorn exist?\r\n");
+ fprintf(stderr, "pink unicorn exist?\r\n");
goto error;
}
}
@@ -249,7 +352,8 @@ static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
ref2 = enif_make_ref(env);
if (!enif_is_ref(env,ref1) || !enif_is_ref(env,ref2)
|| enif_is_identical(env,ref1,ref2) || enif_compare(env,ref1,ref2)==0) {
- fprintf(stderr, "SVERK: strange refs?\r\n");
+ fprintf(stderr, "strange refs?\r\n");
+ goto error;
}
return enif_make_atom(env,"ok");
@@ -260,7 +364,7 @@ error:
static ERL_NIF_TERM tuple_2_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int arity = -1;
- ERL_NIF_TERM* ptr;
+ const ERL_NIF_TERM* ptr;
ERL_NIF_TERM list = enif_make_list(env,0);
if (argc!=1 || !enif_get_tuple(env,argv[0],&arity,&ptr)) {
@@ -304,6 +408,213 @@ badarg:
return enif_make_badarg(env);
}
+static ERL_NIF_TERM clone_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary ibin;
+ if (enif_inspect_binary(env,argv[0],&ibin)) {
+ ErlNifBinary obin;
+ enif_alloc_binary(env,ibin.size,&obin);
+ memcpy(obin.data,ibin.data,ibin.size);
+ /*enif_release_binary(env,&ibin);*/
+ return enif_make_binary(env,&obin);
+ }
+ else {
+ return enif_make_badarg(env);
+ }
+}
+
+static ERL_NIF_TERM make_sub_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int pos, size;
+ if (!enif_get_int(env,argv[1],&pos) || !enif_get_int(env,argv[2],&size)) {
+ return enif_make_badarg(env);
+ }
+ return enif_make_sub_binary(env,argv[0],pos,size);
+}
+
+static ERL_NIF_TERM string_to_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary obin;
+ unsigned size;
+ int n;
+ if (!enif_get_int(env,argv[1],(int*)&size)
+ || !enif_alloc_binary(env,size,&obin)) {
+ return enif_make_badarg(env);
+ }
+ n = enif_get_string(env, argv[0], (char*)obin.data, size, ERL_NIF_LATIN1);
+ return enif_make_tuple(env, 2, enif_make_int(env,n),
+ enif_make_binary(env,&obin));
+}
+
+static ERL_NIF_TERM atom_to_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary obin;
+ unsigned size;
+ int n;
+ if (!enif_get_int(env,argv[1],(int*)&size)
+ || !enif_alloc_binary(env,size,&obin)) {
+ return enif_make_badarg(env);
+ }
+ n = enif_get_atom(env, argv[0], (char*)obin.data, size);
+ return enif_make_tuple(env, 2, enif_make_int(env,n),
+ enif_make_binary(env,&obin));
+}
+
+static ERL_NIF_TERM macros(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ const ERL_NIF_TERM* a;
+ ERL_NIF_TERM lists, tuples;
+ int arity;
+ if (!enif_get_tuple(env, argv[0], &arity, &a) || arity != 9) {
+ return enif_make_badarg(env);
+ }
+
+ lists = enif_make_list(env,9,
+ enif_make_list1(env,a[0]),
+ enif_make_list2(env,a[0],a[1]),
+ enif_make_list3(env,a[0],a[1],a[2]),
+ enif_make_list4(env,a[0],a[1],a[2],a[3]),
+ enif_make_list5(env,a[0],a[1],a[2],a[3],a[4]),
+ enif_make_list6(env,a[0],a[1],a[2],a[3],a[4],a[5]),
+ enif_make_list7(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6]),
+ enif_make_list8(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),
+ enif_make_list9(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]));
+ tuples = enif_make_list(env,9,
+ enif_make_tuple1(env,a[0]),
+ enif_make_tuple2(env,a[0],a[1]),
+ enif_make_tuple3(env,a[0],a[1],a[2]),
+ enif_make_tuple4(env,a[0],a[1],a[2],a[3]),
+ enif_make_tuple5(env,a[0],a[1],a[2],a[3],a[4]),
+ enif_make_tuple6(env,a[0],a[1],a[2],a[3],a[4],a[5]),
+ enif_make_tuple7(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6]),
+ enif_make_tuple8(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),
+ enif_make_tuple9(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]));
+ return enif_make_tuple2(env,lists,tuples);
+}
+
+static ERL_NIF_TERM tuple_2_list_and_tuple(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ const ERL_NIF_TERM* arr;
+ int arity;
+ if (!enif_get_tuple(env,argv[0],&arity,&arr)) {
+ return enif_make_badarg(env);
+ }
+ return enif_make_tuple2(env,
+ enif_make_list_from_array(env, arr, arity),
+ enif_make_tuple_from_array(env, arr, arity));
+}
+
+static ERL_NIF_TERM iolist_2_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary obin;
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &obin)) {
+ return enif_make_badarg(env);
+ }
+ return enif_make_binary(env,&obin);
+}
+
+static ERL_NIF_TERM last_resource_dtor_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary bin;
+ ERL_NIF_TERM ret;
+ if (resource_dtor_last != NULL) {
+ enif_alloc_binary(env, resource_dtor_last_sz, &bin);
+ memcpy(bin.data, resource_dtor_last_data, resource_dtor_last_sz);
+ ret = enif_make_tuple3(env,
+ enif_make_long(env, (long)resource_dtor_last),
+ enif_make_binary(env, &bin),
+ enif_make_int(env, resource_dtor_cnt));
+ }
+ else {
+ ret = enif_make_list(env,0);
+ }
+ resource_dtor_last = NULL;
+ resource_dtor_last_sz = 0;
+ resource_dtor_cnt = 0;
+ return ret;
+}
+
+static ERL_NIF_TERM get_resource_type(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ PrivData* data = (PrivData*) enif_get_data(env);
+ int ix;
+
+ if (!enif_get_int(env, argv[0], &ix) || ix >= 2) {
+ return enif_make_badarg(env);
+ }
+ return enif_make_long(env, data->rt_arr[ix].l);
+}
+
+static ERL_NIF_TERM alloc_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary data_bin;
+ union { ErlNifResourceType* t; long l;} type;
+ union { void* p; long l;} data;
+ if (!enif_get_long(env, argv[0], &type.l)
+ || !enif_inspect_binary(env, argv[1], &data_bin)
+ || (data.p = enif_alloc_resource(env, type.t, data_bin.size))==NULL) {
+
+ return enif_make_badarg(env);
+ }
+ memcpy(data.p, data_bin.data, data_bin.size);
+ return enif_make_long(env, data.l);
+}
+
+static ERL_NIF_TERM make_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ union { void* p; long l; } data;
+ if (!enif_get_long(env, argv[0], &data.l)) {
+ return enif_make_badarg(env);
+ }
+ return enif_make_resource(env, data.p);
+}
+
+static ERL_NIF_TERM make_new_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary data_bin;
+ union { ErlNifResourceType* t; long l;} type;
+ void* data;
+ ERL_NIF_TERM ret;
+ if (!enif_get_long(env, argv[0], &type.l)
+ || !enif_inspect_binary(env, argv[1], &data_bin)
+ || (data = enif_alloc_resource(env, type.t, data_bin.size))==NULL) {
+
+ return enif_make_badarg(env);
+ }
+ ret = enif_make_resource(env, data);
+ memcpy(data, data_bin.data, data_bin.size);
+ enif_release_resource(env, data);
+ return ret;
+}
+
+static ERL_NIF_TERM get_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary data_bin;
+ union { ErlNifResourceType* t; long l; } type;
+ union { void* p; long l; } data;
+
+ if (!enif_get_long(env, argv[0], &type.l)
+ || !enif_get_resource(env, argv[1], type.t, &data.p)) {
+ return enif_make_badarg(env);
+ }
+
+ enif_alloc_binary(env, enif_sizeof_resource(env,data.p), &data_bin);
+ memcpy(data_bin.data, data.p, data_bin.size);
+ return enif_make_tuple2(env, enif_make_long(env,data.l),
+ enif_make_binary(env, &data_bin));
+}
+
+static ERL_NIF_TERM release_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ union { void* p; long l; } data;
+ if (!enif_get_long(env, argv[0], &data.l)) {
+ return enif_make_badarg(env);
+ }
+ enif_release_resource(env, data.p);
+ return enif_make_atom(env,"ok");
+}
+
+
static ErlNifFunc nif_funcs[] =
{
{"lib_version", 0, lib_version},
@@ -315,7 +626,22 @@ static ErlNifFunc nif_funcs[] =
{"tuple_2_list", 1, tuple_2_list},
{"is_identical",2,is_identical},
{"compare",2,compare},
- {"many_args_100", 100, many_args_100}
+ {"many_args_100", 100, many_args_100},
+ {"clone_bin", 1, clone_bin},
+ {"make_sub_bin", 3, make_sub_bin},
+ {"string_to_bin", 2, string_to_bin},
+ {"atom_to_bin", 2, atom_to_bin},
+ {"macros", 1, macros},
+ {"tuple_2_list_and_tuple",1,tuple_2_list_and_tuple},
+ {"iolist_2_bin", 1, iolist_2_bin},
+ {"get_resource_type", 1, get_resource_type},
+ {"alloc_resource", 2, alloc_resource},
+ {"make_resource", 1, make_resource},
+ {"get_resource", 2, get_resource},
+ {"release_resource", 1, release_resource},
+ {"last_resource_dtor_call", 0, last_resource_dtor_call},
+ {"make_new_resource", 2, make_new_resource}
+
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,reload,upgrade,unload)
diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c
index 2f2267cf78..c075b74c57 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_mod.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c
@@ -1,51 +1,182 @@
#include "erl_nif.h"
#include <string.h>
-#include <assert.h>
+#include <stdio.h>
#include "nif_mod.h"
+#define CHECK(X) ((void)((X) || (check_abort(__LINE__),1)))
+#ifdef __GNUC__
+static void check_abort(unsigned line) __attribute__((noreturn));
+#endif
+static void check_abort(unsigned line)
+{
+ enif_fprintf(stderr, "Test CHECK failed at %s:%u\r\n",
+ __FILE__, line);
+ abort();
+}
static int static_cntA; /* zero by default */
static int static_cntB = NIF_LIB_VER * 100;
-static void add_call(ErlNifEnv* env, NifModPrivData* data, const char* func_name)
+static ERL_NIF_TERM am_true;
+static ERL_NIF_TERM am_null;
+static ERL_NIF_TERM am_resource_type;
+static ERL_NIF_TERM am_resource_dtor_A;
+static ERL_NIF_TERM am_resource_dtor_B;
+
+static void init(ErlNifEnv* env)
+{
+ am_true = enif_make_atom(env, "true");
+ am_null = enif_make_atom(env, "null");
+ am_resource_type = enif_make_atom(env, "resource_type");
+ am_resource_dtor_A = enif_make_atom(env, "resource_dtor_A");
+ am_resource_dtor_B = enif_make_atom(env, "resource_dtor_B");
+}
+
+static void add_call_with_arg(ErlNifEnv* env, NifModPrivData* data, const char* func_name,
+ const char* arg, int arg_sz)
{
- CallInfo* call = enif_alloc(env, sizeof(CallInfo)+strlen(func_name));
+ CallInfo* call = enif_alloc(env, sizeof(CallInfo)+strlen(func_name) + arg_sz);
strcpy(call->func_name, func_name);
call->lib_ver = NIF_LIB_VER;
call->static_cntA = ++static_cntA;
call->static_cntB = ++static_cntB;
+ call->arg_sz = arg_sz;
+ if (arg != NULL) {
+ call->arg = call->func_name + strlen(func_name) + 1;
+ memcpy(call->arg, arg, arg_sz);
+ }
+ else {
+ call->arg = NULL;
+ }
+ enif_mutex_lock(data->mtx);
call->next = data->call_history;
data->call_history = call;
+ enif_mutex_unlock(data->mtx);
+}
+
+static void add_call(ErlNifEnv* env, NifModPrivData* data,const char* func_name)
+{
+ add_call_with_arg(env, data, func_name, NULL, 0);
+}
+
+#define ADD_CALL(FUNC_NAME) add_call(env, enif_priv_data(env),FUNC_NAME)
+
+#define STRINGIFY_(X) #X
+#define STRINGIFY(X) STRINGIFY_(X)
+
+static void resource_dtor_A(ErlNifEnv* env, void* a)
+{
+ const char dtor_name[] = "resource_dtor_A_v" STRINGIFY(NIF_LIB_VER);
+
+ add_call_with_arg(env, enif_priv_data(env), dtor_name,
+ a, enif_sizeof_resource(env, a));
+}
+
+static void resource_dtor_B(ErlNifEnv* env, void* a)
+{
+ const char dtor_name[] = "resource_dtor_B_v" STRINGIFY(NIF_LIB_VER);
+
+ add_call_with_arg(env, enif_priv_data(env), dtor_name,
+ a, enif_sizeof_resource(env, a));
+}
+
+/* {resource_type, Ix|null, ErlNifResourceFlags in, "TypeName", dtor(A|B|null), ErlNifResourceFlags out}*/
+static void open_resource_type(ErlNifEnv* env, ERL_NIF_TERM op_tpl)
+{
+ NifModPrivData* data = enif_priv_data(env);
+ const ERL_NIF_TERM* arr;
+ int arity;
+ char rt_name[30];
+ union { enum ErlNifResourceFlags e; int i; } flags, exp_res, got_res;
+ unsigned ix;
+ ErlNifResourceDtor* dtor;
+ ErlNifResourceType* got_ptr;
+
+ CHECK(enif_get_tuple(env, op_tpl, &arity, &arr));
+ CHECK(arity == 6);
+ CHECK(enif_is_identical(env, arr[0], am_resource_type));
+ CHECK(enif_get_int(env, arr[2], &flags.i));
+ CHECK(enif_get_string(env, arr[3], rt_name, sizeof(rt_name), ERL_NIF_LATIN1) > 0);
+ CHECK(enif_get_int(env, arr[5], &exp_res.i));
+
+ if (enif_is_identical(env, arr[4], am_null)) {
+ dtor = NULL;
+ }
+ else if (enif_is_identical(env, arr[4], am_resource_dtor_A)) {
+ dtor = resource_dtor_A;
+ }
+ else {
+ CHECK(enif_is_identical(env, arr[4], am_resource_dtor_B));
+ dtor = resource_dtor_B;
+ }
+
+ got_ptr = enif_open_resource_type(env, rt_name, dtor,
+ flags.e, &got_res.e);
+
+ if (enif_get_uint(env, arr[1], &ix) && ix < RT_MAX && got_ptr != NULL) {
+ data->rt_arr[ix] = got_ptr;
+ }
+ else {
+ CHECK(enif_is_identical(env, arr[1], am_null));
+ CHECK(got_ptr == NULL);
+ }
+ CHECK(got_res.e == exp_res.e);
}
-#define ADD_CALL(FUNC_NAME) add_call(env, enif_get_data(env),FUNC_NAME)
+static void do_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info)
+{
+ NifModPrivData* data = enif_priv_data(env);
+ ERL_NIF_TERM head, tail;
+ unsigned ix;
+ for (ix=0; ix<RT_MAX; ix++) {
+ data->rt_arr[ix] = NULL;
+ }
+ for (head = load_info; enif_get_list_cell(env, head, &head, &tail);
+ head = tail) {
+
+ open_resource_type(env, head);
+ }
+ CHECK(enif_is_empty_list(env, head));
+}
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
- NifModPrivData* data = enif_alloc(env, sizeof(NifModPrivData));
- assert(data != NULL);
+ NifModPrivData* data;
+
+ init(env);
+ data = enif_alloc(env, sizeof(NifModPrivData));
+ CHECK(data != NULL);
+ *priv_data = data;
+ data->mtx = enif_mutex_create("nif_mod_priv_data");
data->ref_cnt = 1;
data->call_history = NULL;
add_call(env, data, "load");
- data->calls = 0;
- *priv_data = data;
+ do_load_info(env, load_info);
+ data->calls = 0;
return 0;
}
static int reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
+ init(env);
add_call(env, *priv_data, "reload");
+
+ do_load_info(env, load_info);
return 0;
}
static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
{
NifModPrivData* data = *old_priv_data;
+ init(env);
add_call(env, data, "upgrade");
data->ref_cnt++;
+
*priv_data = *old_priv_data;
+ do_load_info(env, load_info);
+
return 0;
}
@@ -53,9 +184,13 @@ static void unload(ErlNifEnv* env, void* priv_data)
{
NifModPrivData* data = priv_data;
add_call(env, data, "unload");
+ enif_mutex_lock(data->mtx);
if (--data->ref_cnt == 0) {
+ enif_mutex_unlock(data->mtx);
+ enif_mutex_destroy(data->mtx);
enif_free(env, data);
}
+ enif_mutex_unlock(data->mtx);
}
static ERL_NIF_TERM lib_version(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -64,35 +199,51 @@ static ERL_NIF_TERM lib_version(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
return enif_make_int(env, NIF_LIB_VER);
}
-static ERL_NIF_TERM call_history(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+static ERL_NIF_TERM get_priv_data_ptr(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- NifModPrivData* data = (NifModPrivData*) enif_get_data(env);
- ERL_NIF_TERM list = enif_make_list(env, 0); /* NIL */
+ ADD_CALL("get_priv_data_ptr");
+ return enif_make_ulong(env, (unsigned long)enif_priv_data(env));
+}
- while (data->call_history != NULL) {
- CallInfo* call = data->call_history;
- ERL_NIF_TERM tpl = enif_make_tuple(env, 2,
- enif_make_atom(env,call->func_name),
- enif_make_int(env,call->lib_ver));
- list = enif_make_list_cell(env, tpl, list);
- data->call_history = call->next;
- enif_free(env,call);
+static ERL_NIF_TERM make_new_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ NifModPrivData* data = (NifModPrivData*) enif_priv_data(env);
+ ErlNifBinary ibin;
+ char* a;
+ ERL_NIF_TERM ret;
+ unsigned ix;
+ if (!enif_get_uint(env, argv[0], &ix) || ix >= RT_MAX
+ || !enif_inspect_binary(env, argv[1], &ibin)) {
+ return enif_make_badarg(env);
}
- return list;
+ a = enif_alloc_resource(env, data->rt_arr[ix], ibin.size);
+ memcpy(a, ibin.data, ibin.size);
+ ret = enif_make_resource(env, a);
+ enif_release_resource(env, a);
+ return ret;
}
-static ERL_NIF_TERM get_priv_data_ptr(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+static ERL_NIF_TERM get_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
- ADD_CALL("get_priv_data_ptr");
- return enif_make_ulong(env, (unsigned long)enif_get_data(env));
+ NifModPrivData* data = (NifModPrivData*) enif_priv_data(env);
+ ErlNifBinary obin;
+ unsigned ix;
+ void* a;
+ if (!enif_get_uint(env, argv[0], &ix) || ix >= RT_MAX
+ || !enif_get_resource(env, argv[1], data->rt_arr[ix], &a)
+ || !enif_alloc_binary(env, enif_sizeof_resource(env, a), &obin)) {
+ return enif_make_badarg(env);
+ }
+ memcpy(obin.data, a, obin.size);
+ return enif_make_binary(env, &obin);
}
-
static ErlNifFunc nif_funcs[] =
{
{"lib_version", 0, lib_version},
- {"call_history", 0, call_history},
- {"get_priv_data_ptr", 0, get_priv_data_ptr}
+ {"get_priv_data_ptr", 0, get_priv_data_ptr},
+ {"make_new_resource", 2, make_new_resource},
+ {"get_resource", 2, get_resource}
};
#if NIF_LIB_VER != 3
diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.erl b/erts/emulator/test/nif_SUITE_data/nif_mod.erl
index 93da6590a0..7888a589e7 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_mod.erl
+++ b/erts/emulator/test/nif_SUITE_data/nif_mod.erl
@@ -21,15 +21,19 @@
-include("test_server.hrl").
--export([load_nif_lib/2, start/0, lib_version/0, call_history/0, get_priv_data_ptr/0]).
+-export([load_nif_lib/2, load_nif_lib/3, start/0, lib_version/0, call_history/0,
+ get_priv_data_ptr/0, make_new_resource/2, get_resource/2]).
-export([loop/0, upgrade/1]).
-define(nif_stub,nif_stub_error(?LINE)).
load_nif_lib(Config, Ver) ->
+ load_nif_lib(Config, Ver, []).
+
+load_nif_lib(Config, Ver, LoadInfo) ->
?line Path = ?config(data_dir, Config),
- erlang:load_nif(filename:join(Path,libname(Ver)), 0).
+ erlang:load_nif(filename:join(Path,libname(Ver)), LoadInfo).
libname(no_init) -> libname(3);
libname(Ver) when is_integer(Ver) ->
@@ -59,6 +63,8 @@ lib_version() -> % NIF
call_history() -> ?nif_stub.
get_priv_data_ptr() -> ?nif_stub.
+make_new_resource(_,_) -> ?nif_stub.
+get_resource(_,_) -> ?nif_stub.
nif_stub_error(Line) ->
exit({nif_not_loaded,module,?MODULE,line,Line}).
diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.h b/erts/emulator/test/nif_SUITE_data/nif_mod.h
index 2dfdc75176..0eaf91d6e1 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_mod.h
+++ b/erts/emulator/test/nif_SUITE_data/nif_mod.h
@@ -4,14 +4,19 @@ typedef struct call_info_t
unsigned lib_ver;
int static_cntA;
int static_cntB;
+ char* arg;
+ int arg_sz;
char func_name[1]; /* must be last */
}CallInfo;
+#define RT_MAX 5
typedef struct
{
+ ErlNifMutex* mtx;
int calls;
int ref_cnt;
CallInfo* call_history;
+ ErlNifResourceType* rt_arr[RT_MAX];
}NifModPrivData;
diff --git a/erts/emulator/test/nif_SUITE_data/tail.txt b/erts/emulator/test/nif_SUITE_data/tail.txt
new file mode 100644
index 0000000000..7f06c118c1
--- /dev/null
+++ b/erts/emulator/test/nif_SUITE_data/tail.txt
@@ -0,0 +1,5 @@
+
+
+#include "tester.c" /* poor mans linker */
+
+
diff --git a/erts/emulator/test/nif_SUITE_data/testcase_driver.h b/erts/emulator/test/nif_SUITE_data/testcase_driver.h
new file mode 100644
index 0000000000..98339e4746
--- /dev/null
+++ b/erts/emulator/test/nif_SUITE_data/testcase_driver.h
@@ -0,0 +1,59 @@
+/* ``The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved via the world wide web at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ * AB. All Rights Reserved.''
+ *
+ * $Id$
+ */
+
+#ifndef TESTCASE_DRIVER_H__
+#define TESTCASE_DRIVER_H__
+
+#include "erl_nif.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef struct {
+ char *testcase_name;
+ char *command;
+ int command_len;
+ void *extra;
+} TestCaseState_t;
+
+#define ASSERT_CLNUP(TCS, B, CLN) \
+do { \
+ if (!(B)) { \
+ CLN; \
+ testcase_assertion_failed((TCS), __FILE__, __LINE__, #B); \
+ } \
+} while (0)
+
+#define ASSERT(TCS, B) ASSERT_CLNUP(TCS, B, (void) 0)
+
+
+void testcase_printf(TestCaseState_t *tcs, char *frmt, ...);
+void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...);
+void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...);
+void testcase_failed(TestCaseState_t *tcs, char *frmt, ...);
+int testcase_assertion_failed(TestCaseState_t *tcs, char *file, int line,
+ char *assertion);
+void *testcase_alloc(size_t size);
+void *testcase_realloc(void *ptr, size_t size);
+void testcase_free(void *ptr);
+
+
+char *testcase_name(void);
+void testcase_run(TestCaseState_t *tcs);
+void testcase_cleanup(TestCaseState_t *tcs);
+
+#endif
diff --git a/erts/emulator/test/nif_SUITE_data/tester.c b/erts/emulator/test/nif_SUITE_data/tester.c
new file mode 100644
index 0000000000..08466d0f18
--- /dev/null
+++ b/erts/emulator/test/nif_SUITE_data/tester.c
@@ -0,0 +1,73 @@
+#include "erl_nif.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+
+void testcase_printf(TestCaseState_t *tcs, char *frmt, ...)
+{
+ va_list va;
+ va_start(va, frmt);
+ vfprintf(stderr, frmt, va);
+ va_end(va);
+ fprintf(stderr, "\r");
+}
+
+void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...)
+{
+}
+
+void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...)
+{
+}
+
+void testcase_failed(TestCaseState_t *tcs, char *frmt, ...)
+{
+ va_list va;
+ va_start(va, frmt);
+ vfprintf(stderr, frmt, va);
+ va_end(va);
+ abort();
+}
+
+int testcase_assertion_failed(TestCaseState_t *tcs, char *file, int line,
+ char *assertion)
+{
+ testcase_failed(tcs, "ASSERTION '%s' FAILED at %s:%d\r\n",
+ assertion, file, line);
+ abort();
+ return 0; /*?*/
+}
+
+void *testcase_alloc(size_t size)
+{
+ return malloc(size);
+}
+void *testcase_realloc(void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+void testcase_free(void *ptr)
+{
+ free(ptr);
+}
+
+void testcase_run(TestCaseState_t *tcs);
+
+static int reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ return 0;
+}
+
+static ERL_NIF_TERM run(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ testcase_run(NULL);
+ return enif_make_atom(env, "ok");
+}
+
+static ErlNifFunc nif_funcs[] =
+{
+ {"run", 0, run}
+};
+
+ERL_NIF_INIT(tester,nif_funcs,NULL,reload,NULL,NULL)
+
diff --git a/erts/emulator/test/nif_SUITE_data/tester.erl b/erts/emulator/test/nif_SUITE_data/tester.erl
new file mode 100644
index 0000000000..9df2158200
--- /dev/null
+++ b/erts/emulator/test/nif_SUITE_data/tester.erl
@@ -0,0 +1,13 @@
+-module(tester).
+
+-include("test_server.hrl").
+
+-export([load_nif_lib/2, run/0]).
+
+
+load_nif_lib(Config, LibName) ->
+ ?line Path = ?config(data_dir, Config),
+ erlang:load_nif(filename:join(Path,LibName), []).
+
+run() ->
+ exit({nif_not_loaded,?MODULE,?LINE}).