aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVitaliy Batichko <[email protected]>2010-11-29 20:42:28 +0200
committerVitaliy Batichko <[email protected]>2010-12-10 17:32:02 +0200
commit7992d6a6bfa0f460f8663c2d72d07b60755a4857 (patch)
treead9ff808f00832c32ea84f8a447a8e106a99f3e5
parent95392e693e210189df82173c9bcd345b58b004fe (diff)
downloadotp-7992d6a6bfa0f460f8663c2d72d07b60755a4857.tar.gz
otp-7992d6a6bfa0f460f8663c2d72d07b60755a4857.tar.bz2
otp-7992d6a6bfa0f460f8663c2d72d07b60755a4857.zip
Correct erl_global{register, unregister} functions
C node needs DFLAG_DIST_MONITOR flag set when connecting, and support for processing monitoring start/end messages received from counterpart, to make global registration actually work.
-rw-r--r--lib/erl_interface/include/ei.h29
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c1
-rw-r--r--lib/erl_interface/src/legacy/global_register.c12
-rw-r--r--lib/erl_interface/src/legacy/global_unregister.c12
-rw-r--r--lib/erl_interface/test/Makefile1
-rw-r--r--lib/erl_interface/test/erl_global_SUITE.erl125
-rw-r--r--lib/erl_interface/test/erl_global_SUITE_data/Makefile.first21
-rw-r--r--lib/erl_interface/test/erl_global_SUITE_data/Makefile.src41
-rw-r--r--lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c263
9 files changed, 486 insertions, 19 deletions
diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h
index 466d84bb99..ae815b414a 100644
--- a/lib/erl_interface/include/ei.h
+++ b/lib/erl_interface/include/ei.h
@@ -80,21 +80,24 @@
#define ERL_NO_TIMEOUT -1
/* these are the control message types */
-#define ERL_LINK 1
-#define ERL_SEND 2
-#define ERL_EXIT 3
-#define ERL_UNLINK 4
-#define ERL_NODE_LINK 5
-#define ERL_REG_SEND 6
-#define ERL_GROUP_LEADER 7
-#define ERL_EXIT2 8
-#define ERL_PASS_THROUGH 'p'
+#define ERL_LINK 1
+#define ERL_SEND 2
+#define ERL_EXIT 3
+#define ERL_UNLINK 4
+#define ERL_NODE_LINK 5
+#define ERL_REG_SEND 6
+#define ERL_GROUP_LEADER 7
+#define ERL_EXIT2 8
+#define ERL_PASS_THROUGH 'p'
/* new ones for tracing, from Kenneth */
-#define ERL_SEND_TT 12
-#define ERL_EXIT_TT 13
-#define ERL_REG_SEND_TT 16
-#define ERL_EXIT2_TT 18
+#define ERL_SEND_TT 12
+#define ERL_EXIT_TT 13
+#define ERL_REG_SEND_TT 16
+#define ERL_EXIT2_TT 18
+#define ERL_MONITOR_P 19
+#define ERL_DEMONITOR_P 20
+#define ERL_MONITOR_P_EXIT 21
/* -------------------------------------------------------------------- */
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index 99ccba0686..10824c8a5f 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -1329,6 +1329,7 @@ static int send_name_or_challenge(int fd, char *nodename,
put8(s, 'n');
put16be(s, version);
put32be(s, (DFLAG_EXTENDED_REFERENCES
+ | DFLAG_DIST_MONITOR
| DFLAG_EXTENDED_PIDS_PORTS
| DFLAG_FUN_TAGS
| DFLAG_NEW_FUN_TAGS
diff --git a/lib/erl_interface/src/legacy/global_register.c b/lib/erl_interface/src/legacy/global_register.c
index 3a4de8b08e..f12eb6b448 100644
--- a/lib/erl_interface/src/legacy/global_register.c
+++ b/lib/erl_interface/src/legacy/global_register.c
@@ -31,7 +31,7 @@ int erl_global_register(int fd, const char *name, ETERM *pid)
int index = 0;
erlang_pid self;
erlang_msg msg;
- int needlink, needatom;
+ int needlink, needatom, needmonitor;
int arity;
int version;
int msglen;
@@ -65,7 +65,7 @@ int erl_global_register(int fd, const char *name, ETERM *pid)
if (ei_send_reg_encoded(fd,&self,"rex",buf,index)) return -1;
/* get the reply: expect link and an atom, or just an atom */
- needlink = needatom = 1;
+ needlink = needatom = needmonitor = 1;
while (1) {
/* get message */
while (1) {
@@ -78,9 +78,15 @@ int erl_global_register(int fd, const char *name, ETERM *pid)
case ERL_LINK:
/* got link */
if (!needlink) return -1;
- needlink = 0;
+ needlink = 0;
break;
+ case ERL_MONITOR_P-10:
+ /* got monitor */
+ if (!needmonitor) { return -1;}
+ needmonitor = 0;
+ break;
+
case ERL_SEND:
/* got message - does it contain our atom? */
if (!needatom) return -1;
diff --git a/lib/erl_interface/src/legacy/global_unregister.c b/lib/erl_interface/src/legacy/global_unregister.c
index 514dbc3c68..97a1c2d03c 100644
--- a/lib/erl_interface/src/legacy/global_unregister.c
+++ b/lib/erl_interface/src/legacy/global_unregister.c
@@ -37,7 +37,7 @@ int erl_global_unregister(int fd, const char *name)
erlang_msg msg;
int i;
int version,arity,msglen;
- int needunlink, needatom;
+ int needunlink, needatom, needdemonitor;
/* make a self pid */
self->num = fd;
@@ -57,7 +57,7 @@ int erl_global_unregister(int fd, const char *name)
if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return -1;
/* get the reply: expect unlink and an atom, or just an atom */
- needunlink = needatom = 1;
+ needunlink = needatom = needdemonitor = 1;
while (1) {
/* get message */
while (1) {
@@ -68,11 +68,17 @@ int erl_global_unregister(int fd, const char *name)
switch (i) {
case ERL_UNLINK:
- /* got link */
+ /* got unlink */
if (!needunlink) return -1;
needunlink = 0;
break;
+ case ERL_DEMONITOR_P-10:
+ /* got demonitor */
+ if (!needdemonitor) return -1;
+ needdemonitor = 0;
+ break;
+
case ERL_SEND:
/* got message - does it contain our atom? */
if (!needatom) return -1;
diff --git a/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile
index b7a1a4e4d8..07404fda4d 100644
--- a/lib/erl_interface/test/Makefile
+++ b/lib/erl_interface/test/Makefile
@@ -33,6 +33,7 @@ MODULES= \
ei_print_SUITE \
ei_tmo_SUITE \
erl_connect_SUITE \
+ erl_global_SUITE \
erl_eterm_SUITE \
erl_ext_SUITE \
erl_format_SUITE \
diff --git a/lib/erl_interface/test/erl_global_SUITE.erl b/lib/erl_interface/test/erl_global_SUITE.erl
new file mode 100644
index 0000000000..9826e47d55
--- /dev/null
+++ b/lib/erl_interface/test/erl_global_SUITE.erl
@@ -0,0 +1,125 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2009. 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(erl_global_SUITE).
+
+-include("test_server.hrl").
+-include("erl_global_SUITE_data/erl_global_test_cases.hrl").
+
+-export([all/1,init_per_testcase/2,fin_per_testcase/2,
+ erl_global_registration/1, erl_global_whereis/1, erl_global_names/1]).
+
+-import(runner, [get_term/1,send_term/2]).
+
+-define(GLOBAL_NAME, global_register_node_test).
+
+all(suite) ->
+ [erl_global_registration].
+
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?t:minutes(0.25)),
+ [{watchdog, Dog}|Config].
+
+fin_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+erl_global_registration(Config) when is_list(Config) ->
+ ?line P = runner:start(?interpret),
+ ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0),
+
+ ?line ok = erl_global_register(P, Fd, ?GLOBAL_NAME),
+ ?line ok = erl_global_unregister(P, Fd, ?GLOBAL_NAME),
+
+ ?line 0 = erl_close_connection(P,Fd),
+ ?line runner:send_eot(P),
+ ?line runner:recv_eot(P),
+ ok.
+
+erl_global_whereis(Config) when is_list(Config) ->
+ ?line P = runner:start(?interpret),
+ ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0),
+
+ ?line Self = self(),
+ ?line global:register_name(?GLOBAL_NAME, Self),
+ ?line Self = erl_global_whereis(P, Fd, ?GLOBAL_NAME),
+ ?line global:unregister_name(?GLOBAL_NAME),
+ ?line 0 = erl_close_connection(P, Fd),
+ ?line runner:send_eot(P),
+ ?line runner:recv_eot(P),
+ ok.
+
+erl_global_names(Config) when is_list(Config) ->
+ ?line P = runner:start(?interpret),
+ ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0),
+
+ ?line Self = self(),
+ ?line global:register_name(?GLOBAL_NAME, Self),
+ ?line {[?GLOBAL_NAME], 1} = erl_global_names(P, Fd),
+ ?line global:unregister_name(?GLOBAL_NAME),
+ ?line 0 = erl_close_connection(P, Fd),
+ ?line runner:send_eot(P),
+ ?line runner:recv_eot(P),
+ ok.
+
+
+%%% Interface functions for erl_interface functions.
+
+erl_connect(P, Node, Num, Cookie, Creation) ->
+ send_command(P, erl_connect, [Num, Node, Cookie, Creation]),
+ case get_term(P) of
+ {term,{Fd,_}} when Fd >= 0 -> {ok,Fd};
+ {term,{-1,Errno}} -> {error,Errno}
+ end.
+
+erl_close_connection(P, FD) ->
+ send_command(P, erl_close_connection, [FD]),
+ case get_term(P) of
+ {term,Int} when is_integer(Int) -> Int
+ end.
+
+erl_global_register(P, Fd, Name) ->
+ send_command(P, erl_global_register, [Fd,Name]),
+ get_send_result(P).
+
+erl_global_whereis(P, Fd, Name) ->
+ send_command(P, erl_global_whereis, [Fd,Name]),
+ get_send_result(P).
+
+erl_global_names(P, Fd) ->
+ send_command(P, erl_global_names, [Fd]),
+ get_send_result(P).
+
+erl_global_unregister(P, Fd, Name) ->
+ send_command(P, erl_global_unregister, [Fd,Name]),
+ get_send_result(P).
+
+get_send_result(P) ->
+ case get_term(P) of
+ {term,{1,_}} -> ok;
+ {term,{0, 0}} -> ok;
+ {term,{-1, Errno}} -> {error,Errno};
+ {term,{_,_}}->
+ ?t:fail(bad_return_value)
+ end.
+
+send_command(P, Name, Args) ->
+ runner:send_term(P, {Name,list_to_tuple(Args)}).
diff --git a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first
new file mode 100644
index 0000000000..8e3fcb924e
--- /dev/null
+++ b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first
@@ -0,0 +1,21 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2001-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%
+#
+
+erl_global_test_decl.c: erl_global_test.c
+ erl -noinput -pa ../all_SUITE_data -s init_tc run erl_global_test -s erlang halt
diff --git a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..ef846bc440
--- /dev/null
+++ b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src
@@ -0,0 +1,41 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2000-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_interface_mk_include@@[email protected]
+
+CC0 = @CC@
+CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
+LD = @LD@
+LIBPATH = @erl_interface_libpath@
+LIBERL = $(LIBPATH)/@erl_interface_lib@
+LIBEI = $(LIBPATH)/@erl_interface_eilib@
+LIBFLAGS = ../all_SUITE_data/runner@obj@ \
+ $(LIBERL) $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
+ @erl_interface_threadlib@
+CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data
+OBJS = erl_global_test@obj@ erl_global_test_decl@obj@
+
+all: erl_global_test@exe@
+
+erl_global_test@exe@: $(OBJS) $(LIBERL) $(LIBEI)
+ $(LD) @CROSSLDFLAGS@ -o $@ $(OBJS) $(LIBFLAGS)
+
+clean:
+ $(RM) $(OBJS)
+ $(RM) erl_global_test@exe@
diff --git a/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c b/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c
new file mode 100644
index 0000000000..fc698ca0c4
--- /dev/null
+++ b/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c
@@ -0,0 +1,263 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-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%
+ */
+
+/*
+ * Purpose: Tests the functions in erl_global.c.
+ *
+ * See the erl_global_SUITE.erl file for a "table of contents".
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "runner.h"
+
+static void cmd_erl_connect(ETERM* args);
+static void cmd_erl_global_register(ETERM *args);
+static void cmd_erl_global_whereis(ETERM *args);
+static void cmd_erl_global_names(ETERM *args);
+static void cmd_erl_global_unregister(ETERM *args);
+static void cmd_erl_close_connection(ETERM *args);
+
+static void send_errno_result(int value);
+
+static struct {
+ char* name;
+ int num_args; /* Number of arguments. */
+ void (*func)(ETERM* args);
+} commands[] = {
+ "erl_connect", 4, cmd_erl_connect,
+ "erl_close_connection", 1, cmd_erl_close_connection,
+ "erl_global_register", 2, cmd_erl_global_register,
+ "erl_global_whereis", 1, cmd_erl_global_whereis,
+ "erl_global_names", 0, cmd_erl_global_names,
+ "erl_global_unregister", 2, cmd_erl_global_unregister,
+};
+
+
+/*
+ * Sends a list contaning all data types to the Erlang side.
+ */
+
+TESTCASE(interpret)
+{
+ ETERM* term;
+
+ erl_init(NULL, 0);
+
+ outer_loop:
+
+ term = get_term();
+
+ if (term == NULL) {
+ report(1);
+ return;
+ } else {
+ ETERM* Func;
+ ETERM* Args;
+ int i;
+
+ if (!ERL_IS_TUPLE(term) || ERL_TUPLE_SIZE(term) != 2) {
+ fail("term should be a tuple of size 2");
+ }
+
+ Func = erl_element(1, term);
+ if (!ERL_IS_ATOM(Func)) {
+ fail("function name should be an atom");
+ }
+ Args = erl_element(2, term);
+ if (!ERL_IS_TUPLE(Args)) {
+ fail("function arguments should be a tuple");
+ }
+ erl_free_term(term);
+ for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
+ int n = strlen(commands[i].name);
+ if (ERL_ATOM_SIZE(Func) != n) {
+ continue;
+ }
+ if (memcmp(ERL_ATOM_PTR(Func), commands[i].name, n) == 0) {
+ erl_free_term(Func);
+ if (ERL_TUPLE_SIZE(Args) != commands[i].num_args) {
+ fail("wrong number of arguments");
+ }
+ commands[i].func(Args);
+ erl_free_term(Args);
+ goto outer_loop;
+ }
+ }
+ fail("bad command");
+ }
+}
+
+#define VERIFY_TYPE(Test, Term) \
+if (!Test(Term)) { \
+ fail("wrong type for " #Term); \
+} else { \
+}
+
+static void
+cmd_erl_connect(ETERM* args)
+{
+ ETERM* number;
+ ETERM* node;
+ ETERM* cookie;
+
+ int res;
+ char buffer[256];
+
+ number = ERL_TUPLE_ELEMENT(args, 0);
+ VERIFY_TYPE(ERL_IS_INTEGER, number);
+ node = ERL_TUPLE_ELEMENT(args, 1);
+ VERIFY_TYPE(ERL_IS_ATOM, node);
+ cookie = ERL_TUPLE_ELEMENT(args, 2);
+ VERIFY_TYPE(ERL_IS_ATOM, cookie);
+
+ if (ERL_ATOM_SIZE(cookie) == 0) {
+ res = erl_connect_init(ERL_INT_VALUE(number), 0, 0);
+ } else {
+ memcpy(buffer, ERL_ATOM_PTR(cookie), ERL_ATOM_SIZE(cookie));
+ buffer[ERL_ATOM_SIZE(cookie)] = '\0';
+ res = erl_connect_init(ERL_INT_VALUE(number), buffer, 0);
+ }
+
+ if(!res) {
+ send_errno_result(res);
+ return;
+ }
+
+ memcpy(buffer, ERL_ATOM_PTR(node), ERL_ATOM_SIZE(node));
+ buffer[ERL_ATOM_SIZE(node)] = '\0';
+ send_errno_result(erl_connect(buffer));
+}
+
+static void
+cmd_erl_close_connection(ETERM* args)
+{
+ ETERM* number;
+ ETERM* res;
+
+ number = ERL_TUPLE_ELEMENT(args, 0);
+ VERIFY_TYPE(ERL_IS_INTEGER, number);
+ res = erl_mk_int(erl_close_connection(ERL_INT_VALUE(number)));
+ send_term(res);
+ erl_free_term(res);
+}
+
+static void
+cmd_erl_global_register(ETERM* args)
+{
+ ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
+ ETERM* name = ERL_TUPLE_ELEMENT(args, 1);
+ ETERM* pid = erl_mk_pid(erl_thisnodename(), 14, 0, 0);
+
+ char buffer[256];
+
+ VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
+ VERIFY_TYPE(ERL_IS_ATOM, name);
+
+ memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name));
+ buffer[ERL_ATOM_SIZE(name)] = '\0';
+
+ send_errno_result(erl_global_register(ERL_INT_VALUE(fd_term), buffer, pid));
+ erl_free_term(pid);
+}
+
+static void
+cmd_erl_global_whereis(ETERM* args)
+{
+ ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
+ ETERM* name = ERL_TUPLE_ELEMENT(args, 1);
+ ETERM* pid = NULL;
+
+ char buffer[256];
+
+ VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
+ VERIFY_TYPE(ERL_IS_ATOM, name);
+
+ memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name));
+ buffer[ERL_ATOM_SIZE(name)] = '\0';
+
+ pid = erl_global_whereis(ERL_INT_VALUE(fd_term), buffer, NULL);
+ send_term(pid);
+ erl_free_term(pid);
+}
+
+static void
+cmd_erl_global_names(ETERM* args)
+{
+ ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
+
+ ETERM* res_array[2], *res_tuple, *name;
+ char** names = NULL;
+ int count = 0, i;
+
+ VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
+
+ names = erl_global_names(ERL_INT_VALUE(fd_term), &count);
+
+ res_array[0] = erl_mk_empty_list();
+ for(i=0; i<count; i++) {
+ name = erl_mk_string(names[i]);
+ res_array[0] = erl_cons(name, res_array[0]);
+ }
+
+ free(names);
+
+ res_array[1] = erl_mk_int(count);
+ res_tuple = erl_mk_tuple(res_array, 2);
+
+ send_term(res_tuple);
+
+ erl_free_compound(res_array[0]);
+ erl_free_term(res_array[1]);
+ erl_free_term(res_tuple);
+}
+
+static void
+cmd_erl_global_unregister(ETERM* args)
+{
+ ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
+ ETERM* name = ERL_TUPLE_ELEMENT(args, 1);
+
+ char buffer[256];
+
+ VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
+ VERIFY_TYPE(ERL_IS_ATOM, name);
+
+ memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name));
+ buffer[ERL_ATOM_SIZE(name)] = '\0';
+
+ send_errno_result(erl_global_unregister(ERL_INT_VALUE(fd_term), buffer));
+}
+
+static void
+send_errno_result(int value)
+{
+ ETERM* res_array[2];
+ ETERM* res_tuple;
+
+ res_array[0] = erl_mk_int(value);
+ res_array[1] = erl_mk_int(erl_errno);
+ res_tuple = erl_mk_tuple(res_array, 2);
+ send_term(res_tuple);
+ erl_free_term(res_array[0]);
+ erl_free_term(res_array[1]);
+ erl_free_term(res_tuple);
+}