aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/doc/src/erl_nif.xml12
-rw-r--r--erts/emulator/beam/beam_emu.c1
-rw-r--r--erts/emulator/beam/erl_nif.c104
-rw-r--r--erts/emulator/beam/erl_nif.h2
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h4
-rw-r--r--erts/emulator/beam/global.h6
-rw-r--r--erts/emulator/sys/common/erl_mseg.c20
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c44
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.c18
9 files changed, 156 insertions, 55 deletions
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 18b340591c..5ec844e2ad 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -163,6 +163,8 @@ ok
<seealso marker="#enif_make_binary">enif_make_binary</seealso>.
But it does not have to happen in the same NIF call. Read-only binaries
do not have to be released.</p>
+ <p><seealso marker="#enif_make_new_binary">enif_make_new_binary</seealso>
+ can be used as a shortcut to allocate and return a binary in the same NIF call.</p>
<p>Binaries are sequences of whole bytes. Bitstrings with an arbitrary
bit length have no support yet.</p>
</item>
@@ -660,6 +662,16 @@ typedef enum {
<fsummary>Create an integer term from a long int</fsummary>
<desc><p>Create an integer term from a <c>long int</c>.</p></desc>
</func>
+ <func><name><ret>unsigned char*</ret><nametext>enif_make_new_binary(ErlNifEnv* env, unsigned size, ERL_NIF_TERM* termp)</nametext></name>
+ <fsummary>Allocate and create a new binary term</fsummary>
+ <desc><p>Allocate a binary of size <c>size</c> bytes and create an owning
+ term. The binary data is mutable until the calling NIF returns. This is a
+ quick way to create a new binary without having to use
+ <seealso marker="#ErlNifBinary">ErlNifBinary</seealso>. The drawbacks are
+ that the binary can not be kept between NIF calls and it can not be
+ reallocated.</p><p>Return a pointer to the raw binary data and set
+ <c>*termp</c> to the binary term.</p></desc>
+ </func>
<func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_ref(ErlNifEnv* env)</nametext></name>
<fsummary>Create a reference.</fsummary>
<desc><p>Create a reference like <seealso marker="erlang#make_ref-0">erlang:make_ref/0</seealso>.</p></desc>
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 6de423ecf8..b21fd3038f 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -5309,7 +5309,6 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, BifFunction bf,
ASSERT(c_p->current);
s->current = c_p->current;
a = s->current[2];
- ASSERT(s->current[2] <= 3);
}
/* Save first stack entry */
ASSERT(pc);
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 41a9e17c86..2790020117 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -51,6 +51,16 @@ struct erl_module_nif {
int is_orphan; /* if erlang module has been purged */
};
+#ifdef DEBUG
+# define READONLY_CHECK
+#endif
+#ifdef READONLY_CHECK
+# define ADD_READONLY_CHECK(ENV,PTR,SIZE) add_readonly_check(ENV,PTR,SIZE)
+static void add_readonly_check(ErlNifEnv*, unsigned char* ptr, unsigned sz);
+#else
+# define ADD_READONLY_CHECK(ENV,PTR,SIZE) ((void)0)
+#endif
+
#define MIN_HEAP_FRAG_SZ 200
static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp);
@@ -106,6 +116,13 @@ static void pre_nif_noproc(ErlNifEnv* env, struct erl_module_nif* mod_nif)
env->tmp_obj_list = NULL;
}
+/* 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[];*/
+};
+
static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env)
{
while (env->tmp_obj_list != NULL) {
@@ -176,7 +193,6 @@ static void disable_halloc(ErlNifEnv* env)
}
}
-
void* enif_priv_data(ErlNifEnv* env)
{
return env->mod_nif->priv_data;
@@ -257,6 +273,7 @@ int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin)
bin->bin_term = bin_term;
bin->size = binary_size(bin_term);
bin->ref_bin = NULL;
+ ADD_READONLY_CHECK(env, bin->data, bin->size);
return 1;
}
@@ -293,6 +310,7 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin)
bin->bin_term = THE_NON_VALUE;
bin->ref_bin = NULL;
io_list_to_buf(term, (char*) bin->data, sz);
+ ADD_READONLY_CHECK(env, bin->data, bin->size);
return 1;
}
@@ -357,6 +375,15 @@ void enif_release_binary(ErlNifEnv* env, ErlNifBinary* bin)
#endif
}
+unsigned char* enif_make_new_binary(ErlNifEnv* env, unsigned size,
+ ERL_NIF_TERM* termp)
+{
+ enable_halloc(env);
+ *termp = new_binary(env->proc, NULL, size);
+ disable_halloc(env);
+ return binary_bytes(*termp);
+}
+
int enif_is_identical(ErlNifEnv* env, Eterm lhs, Eterm rhs)
{
return EQ(lhs,rhs);
@@ -991,21 +1018,6 @@ static BeamInstr** get_func_pp(BeamInstr* mod_code, Eterm f_atom, unsigned arity
return NULL;
}
-/*static void refresh_cached_nif_data(BeamInstr* mod_code,
- struct erl_module_nif* mod_nif)
-{
- int i;
- for (i=0; i < mod_nif->entry->num_of_funcs; i++) {
- Eterm f_atom;
- ErlNifFunc* func = &mod_nif->entry->funcs[i];
- BeamInstr* code_ptr;
-
- 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] = ((BeamInstr) mod_nif->priv_data;
- }
-}*/
-
static Eterm mkatom(const char *str)
{
return am_atom_put(str, sys_strlen(str));
@@ -1097,7 +1109,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
static const char bad_lib[] = "bad_lib";
static const char reload[] = "reload";
static const char upgrade[] = "upgrade";
- char lib_name[256]; /* BUGBUG: Max-length? */
+ char* lib_name = NULL;
void* handle = NULL;
void* init_func;
ErlNifEntry* entry = NULL;
@@ -1112,9 +1124,14 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
int veto;
struct erl_module_nif* lib = NULL;
- len = intlist_to_buf(BIF_ARG_1, lib_name, sizeof(lib_name)-1);
- if (len < 1) {
- /*erts_fprintf(stderr, "Invalid library path name '%T'\r\n", BIF_ARG_1);*/
+ len = list_length(BIF_ARG_1);
+ if (len < 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ lib_name = (char *) erts_alloc(ERTS_ALC_T_TMP, len + 1);
+
+ if (intlist_to_buf(BIF_ARG_1, lib_name, len) != len) {
+ erts_free(ERTS_ALC_T_TMP, lib_name);
BIF_ERROR(BIF_P, BADARG);
}
lib_name[len] = '\0';
@@ -1305,6 +1322,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
erts_smp_release_system();
erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_free(ERTS_ALC_T_TMP, lib_name);
BIF_RET(ret);
}
@@ -1358,3 +1376,49 @@ void erl_nif_init()
resource_type_list.name[0] = '\0';
}
+#ifdef READONLY_CHECK
+/* Use checksums to assert that NIFs do not write into inspected binaries
+*/
+static void readonly_check_dtor(struct enif_tmp_obj_t*);
+static unsigned calc_checksum(unsigned char* ptr, unsigned size);
+
+struct readonly_check_t
+{
+ struct enif_tmp_obj_t hdr;
+ unsigned char* ptr;
+ unsigned size;
+ unsigned checksum;
+};
+static void add_readonly_check(ErlNifEnv* env, unsigned char* ptr, unsigned sz)
+{
+ struct readonly_check_t* obj = erts_alloc(ERTS_ALC_T_TMP,
+ sizeof(struct readonly_check_t));
+ obj->hdr.next = env->tmp_obj_list;
+ env->tmp_obj_list = &obj->hdr;
+ obj->hdr.dtor = &readonly_check_dtor;
+ obj->ptr = ptr;
+ obj->size = sz;
+ obj->checksum = calc_checksum(ptr, sz);
+}
+static void readonly_check_dtor(struct enif_tmp_obj_t* o)
+{
+ struct readonly_check_t* obj = (struct readonly_check_t*) o;
+ unsigned chksum = calc_checksum(obj->ptr, obj->size);
+ if (chksum != obj->checksum) {
+ fprintf(stderr, "\r\nReadonly data written by NIF, checksums differ"
+ " %x != %x\r\nABORTING\r\n", chksum, obj->checksum);
+ abort();
+ }
+ erts_free(ERTS_ALC_T_TMP, obj);
+}
+static unsigned calc_checksum(unsigned char* ptr, unsigned size)
+{
+ unsigned i, sum = 0;
+ for (i=0; i<size; i++) {
+ sum ^= ptr[i] << ((i % 4)*8);
+ }
+ return sum;
+}
+
+#endif /* READONLY_CHECK */
+
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index d0f6424724..a345837569 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -30,7 +30,7 @@
** 1.0: R13B04
*/
#define ERL_NIF_MAJOR_VERSION 1
-#define ERL_NIF_MINOR_VERSION 0
+#define ERL_NIF_MINOR_VERSION 1
#include <stdlib.h>
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index ec07a976b2..fe8d2664e1 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -105,6 +105,8 @@ 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));
+ERL_NIF_API_FUNC_DECL(unsigned char*,enif_make_new_binary,(ErlNifEnv*,unsigned size,ERL_NIF_TERM* termp));
+
/*
** Add last to keep compatibility on Windows!!!
*/
@@ -195,7 +197,7 @@ ERL_NIF_API_FUNC_DECL(unsigned,enif_sizeof_resource,(ErlNifEnv*, void* obj));
# 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)
-
+# define enif_make_new_binary ERL_NIF_API_FUNC_MACRO(enif_make_new_binary)
#endif
#ifndef enif_make_list1
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index cefdf80fb4..fbb40e4202 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -76,12 +76,6 @@ 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 */
{
struct erl_module_nif* mod_nif;
diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c
index 4a9bfd5d08..1c4c37b01a 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -302,13 +302,9 @@ check_schedule_cache_check(void)
static void
mseg_shutdown(void)
{
-#ifdef ERTS_SMP
erts_mtx_lock(&mseg_mutex);
-#endif
mseg_clear_cache();
-#ifdef ERTS_SMP
erts_mtx_unlock(&mseg_mutex);
-#endif
}
static ERTS_INLINE void *
@@ -408,8 +404,9 @@ mseg_recreate(void *old_seg, Uint old_size, Uint new_size)
static ERTS_INLINE cache_desc_t *
alloc_cd(void)
-{
+{
cache_desc_t *cd = free_cache_descs;
+ ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&mseg_mutex));
if (cd)
free_cache_descs = cd->next;
return cd;
@@ -418,6 +415,7 @@ alloc_cd(void)
static ERTS_INLINE void
free_cd(cache_desc_t *cd)
{
+ ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&mseg_mutex));
cd->next = free_cache_descs;
free_cache_descs = cd;
}
@@ -426,6 +424,7 @@ free_cd(cache_desc_t *cd)
static ERTS_INLINE void
link_cd(cache_desc_t *cd)
{
+ ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&mseg_mutex));
if (cache)
cache->prev = cd;
cd->next = cache;
@@ -443,6 +442,7 @@ link_cd(cache_desc_t *cd)
static ERTS_INLINE void
end_link_cd(cache_desc_t *cd)
{
+ ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&mseg_mutex));
if (cache_end)
cache_end->next = cd;
cd->next = NULL;
@@ -460,7 +460,7 @@ end_link_cd(cache_desc_t *cd)
static ERTS_INLINE void
unlink_cd(cache_desc_t *cd)
{
-
+ ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&mseg_mutex));
if (cd->next)
cd->next->prev = cd->prev;
else
@@ -478,6 +478,7 @@ static ERTS_INLINE void
check_cache_limits(void)
{
cache_desc_t *cd;
+ ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&mseg_mutex));
max_cached_seg_size = 0;
min_cached_seg_size = ~((Uint) 0);
for (cd = cache; cd; cd = cd->next) {
@@ -496,7 +497,7 @@ adjust_cache_size(int force_check_limits)
int check_limits = force_check_limits;
Sint max_cached = ((Sint) segments.current.watermark
- (Sint) segments.current.no);
-
+ ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&mseg_mutex));
while (((Sint) cache_size) > max_cached && ((Sint) cache_size) > 0) {
ASSERT(cache_end);
cd = cache_end;
@@ -520,9 +521,7 @@ adjust_cache_size(int force_check_limits)
static void
check_cache(void *unused)
{
-#ifdef ERTS_SMP
erts_mtx_lock(&mseg_mutex);
-#endif
is_cache_check_scheduled = 0;
@@ -535,10 +534,7 @@ check_cache(void *unused)
INC_CC(check_cache);
-#ifdef ERTS_SMP
erts_mtx_unlock(&mseg_mutex);
-#endif
-
}
static void
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 7d05a9a880..3ad4f93374 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -1,3 +1,21 @@
+/*
+ * %CopyrightBegin%
+ *
+ * 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 "erl_nif.h"
#include <stdio.h>
@@ -120,11 +138,10 @@ static ERL_NIF_TERM make_call_history(ErlNifEnv* env, CallInfo** headp)
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));
+ ERL_NIF_TERM arg_bin;
+ memcpy(enif_make_new_binary(env, call->arg_sz, &arg_bin),
+ call->arg, call->arg_sz);
+ func_term = enif_make_tuple2(env, func_term, arg_bin);
}
tpl = enif_make_tuple4(env, func_term,
enif_make_int(env,call->lib_ver),
@@ -412,11 +429,10 @@ 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);
+ ERL_NIF_TERM obin;
+ memcpy(enif_make_new_binary(env, ibin.size, &obin),
+ ibin.data, ibin.size);
+ return obin;
}
else {
return enif_make_badarg(env);
@@ -515,14 +531,14 @@ static ERL_NIF_TERM iolist_2_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
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);
+ ERL_NIF_TERM bin;
+ memcpy(enif_make_new_binary(env, resource_dtor_last_sz, &bin),
+ 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),
+ bin,
enif_make_int(env, resource_dtor_cnt));
}
else {
diff --git a/erts/emulator/test/nif_SUITE_data/nif_mod.c b/erts/emulator/test/nif_SUITE_data/nif_mod.c
index c075b74c57..75df9d56d5 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_mod.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_mod.c
@@ -1,3 +1,21 @@
+/*
+ * %CopyrightBegin%
+ *
+ * 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 "erl_nif.h"
#include <string.h>
#include <stdio.h>