aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erl_nif.xml35
-rw-r--r--erts/emulator/beam/erl_drv_nif.h8
-rw-r--r--erts/emulator/sys/common/erl_check_io.c27
-rw-r--r--erts/emulator/sys/common/erl_check_io.h4
-rw-r--r--erts/emulator/sys/unix/sys.c2
-rw-r--r--erts/emulator/test/nif_SUITE.erl18
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c4
7 files changed, 72 insertions, 26 deletions
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 94aff7c67b..13b72863f3 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -2526,7 +2526,7 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
</func>
<func>
- <name><ret>int</ret>
+ <name><ret>enum ErlNifSelectReturn</ret>
<nametext>enif_select(ErlNifEnv* env, ErlNifEvent event,
enum ErlNifSelectFlags mode, void* obj, Eterm ref)</nametext>
</name>
@@ -2567,7 +2567,38 @@ enif_map_iterator_destroy(env, &amp;iter);</code>
the event object. This safe way of closing event objects must be used
even if all notifications have been received and no further calls to
<c>enif_select</c> have been made.</p>
- <p>Returns 0 on success, or -1 if invalid arguments.</p>
+ <p>Returns an integer where different bits indicate the outcome of the call:</p>
+ <taglist>
+ <tag><c>ERL_NIF_SELECT_ERROR</c></tag>
+ <item>The master error bit. It will always be set if the call failed for
+ any reason.</item>
+ <tag><c>ERL_NIF_SELECT_STOP_CALLED</c></tag>
+ <item>The stop callback was called directly by <c>enif_select</c>.</item>
+ <tag><c>ERL_NIF_SELECT_STOP_SCHEDULED</c></tag>
+ <item>The stop callback was scheduled to run on some other thread
+ or later by this thread.</item>
+ <tag><c>ERL_NIF_SELECT_INVALID_EVENT</c></tag>
+ <item>Argument <c>event</c> is not a valid OS event object.</item>
+ <tag><c>ERL_NIF_SELECT_FAILED</c></tag>
+ <item>The system call failed to add the event object to the poll set.</item>
+ </taglist>
+ <p>The return value from a successful call with <c>mode</c> as <c>ERL_NIF_SELECT_STOP</c>,
+ will contain either bit <c>ERL_NIF_SELECT_STOP_CALLED</c> or
+ <c>ERL_NIF_SELECT_STOP_SCHEDULED</c>.</p>
+ <note>
+ <p>Always use bitwise AND to test the return value. New significant bits
+ may be added in future releases to give more detailed information for both
+ failed and successful calls. Do NOT use equallity tests like <c>==</c>, as
+ that may cause your application to stop working.</p>
+ <p>Example:</p>
+ <code type="none">
+retval = enif_select(env, fd, ERL_NIF_SELECT_READ, resource, ref);
+if (retval &amp; ERL_NIF_SELECT_ERROR) {
+ /* handle error */
+}
+/* Success! */
+</code>
+ </note>
</desc>
</func>
diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h
index e4ebcdb1d4..46bb06d642 100644
--- a/erts/emulator/beam/erl_drv_nif.h
+++ b/erts/emulator/beam/erl_drv_nif.h
@@ -56,6 +56,14 @@ enum ErlNifSelectFlags {
ERL_NIF_SELECT_STOP = (1 << 2)
};
+enum ErlNifSelectReturn {
+ ERL_NIF_SELECT_ERROR = (1 << 0),
+ ERL_NIF_SELECT_STOP_CALLED = (1 << 1),
+ ERL_NIF_SELECT_STOP_SCHEDULED = (1 << 2),
+ ERL_NIF_SELECT_INVALID_EVENT = (1 << 3),
+ ERL_NIF_SELECT_FAILED = (1 << 4)
+};
+
#ifdef SIZEOF_CHAR
# define SIZEOF_CHAR_SAVED__ SIZEOF_CHAR
# undef SIZEOF_CHAR
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index a533a10c22..4fc95624c7 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1198,7 +1198,7 @@ done_unknown:
return ret;
}
-int
+enum ErlNifSelectReturn
ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
ErlNifEvent e,
enum ErlNifSelectFlags mode,
@@ -1213,7 +1213,7 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
ErtsPollEvents new_events, old_events;
ErtsDrvEventState *state;
int wake_poller;
- int ret;
+ enum ErlNifSelectReturn ret;
enum { NO_STOP=0, CALL_STOP, CALL_STOP_AND_RELEASE } call_stop = NO_STOP;
#if ERTS_CIO_HAVE_DRV_EVENT
ErtsDrvEventDataState *free_event = NULL;
@@ -1227,11 +1227,11 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
if ((unsigned)fd >= (unsigned)erts_smp_atomic_read_nob(&drv_ev_state_len)) {
if (fd < 0) {
- return -1;
+ return ERL_NIF_SELECT_ERROR | ERL_NIF_SELECT_INVALID_EVENT;
}
if (fd >= max_fds) {
nif_select_large_fd_error(fd, mode, resource, ref);
- return -1;
+ return ERL_NIF_SELECT_ERROR | ERL_NIF_SELECT_INVALID_EVENT;
}
grow_drv_ev_state(fd);
}
@@ -1250,7 +1250,7 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
if (IS_FD_UNKNOWN(state)) {
/* fast track to stop callback */
call_stop = CALL_STOP;
- ret = 0;
+ ret = ERL_NIF_SELECT_STOP_CALLED;
goto done_unknown;
}
on = 0;
@@ -1303,7 +1303,7 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
print_nif_select_op(dsbufp, fd, mode, resource, ref);
steal_pending_stop_nif(dsbufp, resource, state, mode, on);
if (state->type == ERTS_EV_TYPE_STOP_NIF) {
- ret = 0;
+ ret = ERL_NIF_SELECT_STOP_SCHEDULED; /* ?? */
goto done;
}
ASSERT(state->type == ERTS_EV_TYPE_NONE);
@@ -1325,7 +1325,7 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
state->driver.nif->out.ddeselect_cnt = 0;
state->driver.stop.resource = NULL;
}
- ret = -1;
+ ret = ERL_NIF_SELECT_ERROR | ERL_NIF_SELECT_FAILED;
goto done;
}
@@ -1339,8 +1339,7 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
|| state->type == ERTS_EV_TYPE_NONE);
state->events = new_events;
- if (ctl_events) {
- if (on) {
+ if (on) {
Uint32* refn;
if (!state->driver.nif)
state->driver.nif = alloc_nif_select_data();
@@ -1379,8 +1378,9 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
}
state->driver.nif->out.ddeselect_cnt = 0;
}
- }
- else { /* off */
+ ret = 0;
+ }
+ else { /* off */
if (state->type == ERTS_EV_TYPE_NIF) {
//erts_fprintf(stderr, "SVERK: enif select clear fd=%d inpid=%T inrsrc=%p\n",
// state->fd, state->driver.nif->inpid,
@@ -1408,6 +1408,7 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
call_stop = CALL_STOP;
}
state->type = ERTS_EV_TYPE_NONE;
+ ret = ERL_NIF_SELECT_STOP_CALLED;
}
else {
/* Not safe to close fd, postpone stop_select callback. */
@@ -1418,12 +1419,10 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
enif_keep_resource(resource);
}
state->type = ERTS_EV_TYPE_STOP_NIF;
+ ret = ERL_NIF_SELECT_STOP_SCHEDULED;
}
- }
}
- ret = 0;
-
done:
check_fd_cleanup(state,
diff --git a/erts/emulator/sys/common/erl_check_io.h b/erts/emulator/sys/common/erl_check_io.h
index 242e7cfcf5..41e2e9210a 100644
--- a/erts/emulator/sys/common/erl_check_io.h
+++ b/erts/emulator/sys/common/erl_check_io.h
@@ -34,8 +34,8 @@
int driver_select_kp(ErlDrvPort, ErlDrvEvent, int, int);
int driver_select_nkp(ErlDrvPort, ErlDrvEvent, int, int);
-int enif_select_kp(ErlNifEnv*, ErlNifEvent, enum ErlNifSelectFlags, void*, Eterm);
-int enif_select_nkp(ErlNifEnv*, ErlNifEvent, enum ErlNifSelectFlags, void*, Eterm);
+enum ErlNifSelectReturn enif_select_kp(ErlNifEnv*, ErlNifEvent, enum ErlNifSelectFlags, void*, Eterm);
+enum ErlNifSelectReturn enif_select_nkp(ErlNifEnv*, ErlNifEvent, enum ErlNifSelectFlags, void*, Eterm);
int driver_event_kp(ErlDrvPort, ErlDrvEvent, ErlDrvEventData);
int driver_event_nkp(ErlDrvPort, ErlDrvEvent, ErlDrvEventData);
Uint erts_check_io_size_kp(void);
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 789b455f2d..5ce57b7b1b 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -161,7 +161,7 @@ int erts_use_kernel_poll = 0;
struct {
int (*select)(ErlDrvPort, ErlDrvEvent, int, int);
- int (*enif_select)(ErlNifEnv*, ErlNifEvent, enum ErlNifSelectFlags, void*, Eterm);
+ enum ErlNifSelectReturn (*enif_select)(ErlNifEnv*, ErlNifEvent, enum ErlNifSelectFlags, void*, Eterm);
int (*event)(ErlDrvPort, ErlDrvEvent, ErlDrvEventData);
void (*check_io_as_interrupt)(void);
void (*check_io_interrupt)(int);
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index c2429c3405..8795a3b24a 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -440,6 +440,13 @@ t_on_load(Config) when is_list(Config) ->
-define(ERL_NIF_SELECT_WRITE, (1 bsl 1)).
-define(ERL_NIF_SELECT_STOP, (1 bsl 2)).
+-define(ERL_NIF_SELECT_ERROR, (1 bsl 0)).
+-define(ERL_NIF_SELECT_STOP_CALLED, (1 bsl 1)).
+-define(ERL_NIF_SELECT_STOP_SCHEDULED, (1 bsl 2)).
+-define(ERL_NIF_SELECT_INVALID_EVENT, (1 bsl 3)).
+-define(ERL_NIF_SELECT_FAILED, (1 bsl 4)).
+
+
select(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
@@ -470,7 +477,7 @@ select(Config) when is_list(Config) ->
%% Close write and wait for EOF
eagain = read_nif(R, 1),
- 0 = select_nif(W,?ERL_NIF_SELECT_STOP,W,Ref),
+ check_stop_ret(select_nif(W,?ERL_NIF_SELECT_STOP,W,Ref)),
timer:sleep(10),
{1, {W_ptr,_}} = last_fd_stop_call(),
true = is_closed_nif(W),
@@ -479,7 +486,7 @@ select(Config) when is_list(Config) ->
[{select, R, Ref, ready_input}] = flush(),
eof = read_nif(R,1),
- 0 = select_nif(R,?ERL_NIF_SELECT_STOP,R,Ref),
+ check_stop_ret(select_nif(R,?ERL_NIF_SELECT_STOP,R,Ref)),
timer:sleep(10),
{1, {R_ptr,_}} = last_fd_stop_call(),
true = is_closed_nif(R),
@@ -521,12 +528,12 @@ select_2(Config) ->
done = receive_any(),
[] = flush(),
- 0 = select_nif(R,?ERL_NIF_SELECT_STOP,R,Ref1),
+ check_stop_ret(select_nif(R,?ERL_NIF_SELECT_STOP,R,Ref1)),
timer:sleep(10),
{1, {R_ptr,_}} = last_fd_stop_call(),
true = is_closed_nif(R),
- 0 = select_nif(W,?ERL_NIF_SELECT_STOP,W,Ref1),
+ ?ERL_NIF_SELECT_STOP_CALLED = select_nif(W,?ERL_NIF_SELECT_STOP,W,Ref1),
timer:sleep(10),
{1, {W_ptr,1}} = last_fd_stop_call(),
true = is_closed_nif(W),
@@ -538,7 +545,8 @@ select_3(Config) ->
{_,_,2} = last_resource_dtor_call(),
ok.
-
+check_stop_ret(?ERL_NIF_SELECT_STOP_CALLED) -> ok;
+check_stop_ret(?ERL_NIF_SELECT_STOP_SCHEDULED) -> ok.
write_full(W, C) ->
write_full(W, C, <<>>).
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 813d19ae90..d2af081a22 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -2042,7 +2042,7 @@ static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
enum ErlNifSelectFlags mode;
void* obj;
ERL_NIF_TERM ref;
- int retval;
+ enum ErlNifSelectReturn retval;
if (!get_fd(env, argv[0], &fdr)
|| !enif_get_uint(env, argv[1], &mode)
@@ -2056,7 +2056,7 @@ static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
fdr->was_selected = 1;
retval = enif_select(env, fdr->fd, mode, obj, ref);
- return enif_make_int(env, retval);
+ return enif_make_int(env, (int)retval);
}
static ERL_NIF_TERM pipe_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])