aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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/test/nif_SUITE_data/nif_SUITE.c44
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_mod.c18
-rw-r--r--system/doc/efficiency_guide/advanced.xml12
-rw-r--r--system/doc/tutorial/complex6.erl11
-rw-r--r--system/doc/tutorial/complex6_nif.c32
-rw-r--r--system/doc/tutorial/nif.xmlsrc136
-rw-r--r--system/doc/tutorial/part.xml9
-rw-r--r--system/doc/tutorial/xmlfiles.mk13
14 files changed, 346 insertions, 58 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/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>
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 7898f925b9..2383e3cf3d 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -47,11 +47,13 @@
<cell align="center" valign="middle">Memory size</cell>
</row>
<row>
- <cell align="left" valign="middle">Integer (-16#7FFFFFF &lt; i &lt;16#7FFFFFF)</cell>
- <cell align="left" valign="middle">1 word</cell>
+ <cell align="left" valign="middle">Small integer</cell>
+ <cell align="left" valign="middle">1 word<br></br>
+On 32-bit architectures: -134217729 &lt; i &lt; 134217728 (28 bits)<br></br>
+On 64-bit architectures: -576460752303423489 &lt; i &lt; 576460752303423488 (60 bits)</cell>
</row>
<row>
- <cell align="left" valign="middle">Integer (big numbers)</cell>
+ <cell align="left" valign="middle">Big integer</cell>
<cell align="left" valign="middle">3..N words</cell>
</row>
<row>
@@ -72,11 +74,11 @@ On 64-bit architectures: 3 words</cell>
</row>
<row>
<cell align="left" valign="middle">List</cell>
- <cell align="left" valign="middle">1 word per element + the size of each element</cell>
+ <cell align="left" valign="middle">1 word + 1 word per element + the size of each element</cell>
</row>
<row>
<cell align="left" valign="middle">String (is the same as a list of integers)</cell>
- <cell align="left" valign="middle">2 words per character</cell>
+ <cell align="left" valign="middle">1 word + 2 words per character</cell>
</row>
<row>
<cell align="left" valign="middle">Tuple</cell>
diff --git a/system/doc/tutorial/complex6.erl b/system/doc/tutorial/complex6.erl
new file mode 100644
index 0000000000..a5f51886c8
--- /dev/null
+++ b/system/doc/tutorial/complex6.erl
@@ -0,0 +1,11 @@
+-module(complex6).
+-export([foo/1, bar/1]).
+-on_load(init/0).
+
+init() ->
+ ok = erlang:load_nif("./complex6_nif", 0).
+
+foo(_X) ->
+ exit(nif_library_not_loaded).
+bar(_Y) ->
+ exit(nif_library_not_loaded).
diff --git a/system/doc/tutorial/complex6_nif.c b/system/doc/tutorial/complex6_nif.c
new file mode 100644
index 0000000000..b656ed43ce
--- /dev/null
+++ b/system/doc/tutorial/complex6_nif.c
@@ -0,0 +1,32 @@
+#include "erl_nif.h"
+
+extern int foo(int x);
+extern int bar(int y);
+
+static ERL_NIF_TERM foo_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int x, ret;
+ if (!enif_get_int(env, argv[0], &x)) {
+ return enif_make_badarg(env);
+ }
+ ret = foo(x);
+ return enif_make_int(env, ret);
+}
+
+static ERL_NIF_TERM bar_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ int y, ret;
+ if (!enif_get_int(env, argv[0], &y)) {
+ return enif_make_badarg(env);
+ }
+ ret = bar(y);
+ return enif_make_int(env, ret);
+}
+
+static ErlNifFunc nif_funcs[] = {
+ {"foo", 1, foo_nif},
+ {"bar", 1, bar_nif}
+};
+
+ERL_NIF_INIT(complex6, nif_funcs, NULL, NULL, NULL, NULL)
+
diff --git a/system/doc/tutorial/nif.xmlsrc b/system/doc/tutorial/nif.xmlsrc
new file mode 100644
index 0000000000..f9197c69dd
--- /dev/null
+++ b/system/doc/tutorial/nif.xmlsrc
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2000</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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.
+
+ </legalnotice>
+
+ <title>NIFs</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>nif.xml</file>
+ </header>
+ <p>This is an example of how to solve the <seealso marker="example">example problem</seealso>
+ by using NIFs. NIFs where introduced in R13B03 as an experimental
+ feature. It is a simpler and more efficient way of calling C-code
+ than using port drivers. NIFs are most suitable for synchronous functions like
+ <c>foo</c> and <c>bar</c> in the example, that does some
+ relatively short calculations without side effects and return the result.</p>
+ <section>
+ <title>NIFs</title>
+ <p>A NIF (Native Implemented Function) is a function that is
+ implemented in C instead of Erlang. NIFs appear as any other functions to
+ the callers. They belong to a module and are called like any other Erlang
+ functions. The NIFs of a module are compiled and linked into a dynamic
+ loadable shared library (SO in Unix, DLL in Windows). The NIF library must
+ be loaded in runtime by the Erlang code of the module.</p>
+ <p>Since a NIF library is dynamically linked into the emulator
+ process, this is the fastest way of calling C-code from Erlang (alongside
+ port drivers). Calling NIFs requires no context switches. But it is also
+ the least safe, because a crash in a NIF will bring the emulator down
+ too.</p>
+ </section>
+
+ <section>
+ <title>Erlang Program</title>
+ <p>Even if all functions of a module will be NIFs, you still need an Erlang
+ module for two reasons. First, the NIF library must be explicitly loaded
+ by Erlang code in the same module. Second, all NIFs of a module must have
+ an Erlang implementation as well. Normally these are minimal stub
+ implementations that throw an exception. But it can also be used as
+ fallback implementations for functions that do not have native
+ implemenations on some architectures.</p>
+ <p>NIF libraries are loaded by calling <c>erlang:load_nif/2</c>, with the
+ name of the shared library as argument. The second argument can be any
+ term that will be passed on to the library and used for
+ initialization.</p>
+
+ <codeinclude file="complex6.erl" tag="" type="none"></codeinclude>
+
+ <p>We use the directive <c>on_load</c> to get function <c>init</c> to be
+ automatically called when the module is loaded. If <c>init</c>
+ returns anything other than <c>ok</c>, such when the loading of
+ the NIF library fails in this example, the module will be
+ unloaded and calls to functions within it will fail.</p>
+ <p>Loading the NIF library will override the stub implementations
+ and cause calls to <c>foo</c> and <c>bar</c> to be dispatched to
+ the NIF implementations instead.</p>
+ </section>
+ <section>
+ <title>NIF library code</title>
+ <p>The NIFs of the module are compiled and linked into a
+ shared library. Each NIF is implemented as a normal C function. The macro
+ <c>ERL_NIF_INIT</c> together with an array of structures defines the names,
+ arity and function pointers of all the NIFs in the module. The header
+ file <c>erl_nif.h</c> must be included. Since the library is a shared
+ module, not a program, no main function should be present.</p>
+ <p>The function arguments passed to a NIF appears in an array <c>argv</c>,
+ with <c>argc</c> as the length of the array and thus the arity of the
+ function. The Nth argument of the function can be accessed as
+ <c>argv[N-1]</c>. NIFs also takes an environment argument that
+ serves as an opaque handle that is needed to be passed on to
+ most API functions. The environment contains information about
+ the calling Erlang process.</p>
+
+ <codeinclude file="complex6_nif.c" tag="" type="none"></codeinclude>
+
+ <p>The first argument to <c>ERL_NIF_INIT</c> must be the name of the
+ Erlang module as a C-identifier. It will be stringified by the
+ macro. The second argument is the array of <c>ErlNifFunc</c>
+ structures containing name, arity and function pointer of
+ each NIF. The other arguments are pointers to callback functions
+ that can be used to initialize the library. We do not use them
+ is this simple example so we set them all to <c>NULL</c>.</p>
+ <p>Function arguments and return values are represented as values
+ of type <c>ERL_NIF_TERM</c>. We use functions like <c>enif_get_int</c>
+ and <c>enif_make_int</c> to convert between Erlang term and C-type.
+ If the function argument <c>argv[0]</c> is not an integer then
+ <c>enif_get_int</c> will return false, in which case we return
+ by throwing a <c>badarg</c>-exception with <c>enif_make_badarg</c>.</p>
+ </section>
+
+ <section>
+ <title>Running the Example</title>
+ <p>1. Compile the C code.</p>
+ <pre>
+unix> <input>gcc -o complex6_nif.so -fpic -shared complex.c complex6_nif.c</input>
+windows> <input>cl -LD -MD -Fe complex6_nif.dll complex.c complex6_nif.c</input></pre>
+ <p>2. Start Erlang and compile the Erlang code.</p>
+ <pre>
+> <input>erl</input>
+Erlang R13B04 (erts-5.7.5) [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]
+
+Eshell V5.7.5 (abort with ^G)
+1> <input>c(complex6).</input>
+{ok,complex6}</pre>
+ <p>3. Run the example.</p>
+<pre>
+3> <input>complex6:foo(3).</input>
+4
+4> <input>complex6:bar(5).</input>
+10
+5> <input>complex6:foo("not an integer").</input>
+** exception error: bad argument
+ in function complex6:foo/1
+ called as comlpex6:foo("not an integer")
+</pre>
+</section>
+</chapter>
diff --git a/system/doc/tutorial/part.xml b/system/doc/tutorial/part.xml
index 1a8a873242..ff73b668c0 100644
--- a/system/doc/tutorial/part.xml
+++ b/system/doc/tutorial/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2000</year><year>2009</year>
+ <year>2000</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,19 +13,19 @@
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.
-
+
</legalnotice>
<title>Interoperability Tutorial</title>
<prepared>Gunilla Hugosson</prepared>
<docno></docno>
<date>2000-01-18</date>
- <rev>R7B</rev>
+ <rev>R14A</rev>
</header>
<xi:include href="introduction.xml"/>
<xi:include href="overview.xml"/>
@@ -34,5 +34,6 @@
<xi:include href="erl_interface.xml"/>
<xi:include href="c_portdriver.xml"/>
<xi:include href="cnode.xml"/>
+ <xi:include href="nif.xml"/>
</part>
diff --git a/system/doc/tutorial/xmlfiles.mk b/system/doc/tutorial/xmlfiles.mk
index 794ef49774..95562bec60 100644
--- a/system/doc/tutorial/xmlfiles.mk
+++ b/system/doc/tutorial/xmlfiles.mk
@@ -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%
#
TUTORIAL_CHAPTER_FILES = \
@@ -23,7 +23,8 @@ TUTORIAL_CHAPTER_FILES = \
erl_interface.xml \
c_portdriver.xml \
example.xml\
- overview.xml
+ overview.xml\
+ nif.xml
# appendix.xml
# distribution.xml (to be part of tutorial later)