aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/emulator/beam/erl_nif.h1
-rw-r--r--erts/emulator/sys/common/erl_check_io.c33
-rw-r--r--erts/emulator/test/nif_SUITE.erl85
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c1
4 files changed, 101 insertions, 19 deletions
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index ce8caaf729..bf0f7b1f15 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -146,7 +146,6 @@ typedef struct {
typedef struct enif_resource_type_t ErlNifResourceType;
typedef void ErlNifResourceDtor(ErlNifEnv*, void*);
typedef void ErlNifResourceStop(ErlNifEnv*, void*);
-typedef void ErlNifResourceExit(ErlNifEnv*, void*);
//#ifndef ERL_SYS_DRV
typedef int ErlNifEvent; /* An event to be selected on. */
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 6f61fc8a28..aeda49b2c6 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1256,11 +1256,18 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
on = 0;
mode = ERL_DRV_READ | ERL_DRV_WRITE | ERL_DRV_USE;
wake_poller = 1; /* to eject fd from pollset (if needed) */
+ ctl_events = ERTS_POLL_EV_IN | ERTS_POLL_EV_OUT;
}
else {
on = 1;
ASSERT(mode);
wake_poller = 0;
+ if (mode & ERL_DRV_READ) {
+ ctl_events |= ERTS_POLL_EV_IN;
+ }
+ if (mode & ERL_DRV_WRITE) {
+ ctl_events |= ERTS_POLL_EV_OUT;
+ }
}
#ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS
@@ -1303,16 +1310,6 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
break;
}}
- ASSERT(state->type == ERTS_EV_TYPE_NONE ||
- state->type == ERTS_EV_TYPE_NIF);
-
- if (mode & ERL_DRV_READ) {
- ctl_events |= ERTS_POLL_EV_IN;
- }
- if (mode & ERL_DRV_WRITE) {
- ctl_events |= ERTS_POLL_EV_OUT;
- }
-
ASSERT((state->type == ERTS_EV_TYPE_NIF) ||
(state->type == ERTS_EV_TYPE_NONE && !state->events));
@@ -1355,7 +1352,6 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
ASSERT(state->type == ERTS_EV_TYPE_NIF);
ASSERT(state->driver.stop.resource == resource);
if (ctl_events & ERTS_POLL_EV_IN) {
- ASSERT(is_nil(state->driver.nif->in.pid));
state->driver.nif->in.pid = id;
if (is_immed(ref)) {
state->driver.nif->in.immed = ref;
@@ -1370,7 +1366,6 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
state->driver.nif->in.ddeselect_cnt = 0;
}
if (ctl_events & ERTS_POLL_EV_OUT) {
- ASSERT(is_nil(state->driver.nif->out.pid));
state->driver.nif->out.pid = id;
if (is_immed(ref)) {
state->driver.nif->out.immed = ref;
@@ -1384,7 +1379,6 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
}
state->driver.nif->out.ddeselect_cnt = 0;
}
- state->flags |= ERTS_EV_FLAG_USED;
}
else { /* off */
if (state->type == ERTS_EV_TYPE_NIF) {
@@ -1395,7 +1389,6 @@ ERTS_CIO_EXPORT(enif_select)(ErlNifEnv* env,
state->driver.nif->out.pid = NIL;
state->driver.nif->in.ddeselect_cnt = 0;
state->driver.nif->out.ddeselect_cnt = 0;
- state->flags &= ~ERTS_EV_FLAG_USED;
if (old_events != 0) {
remember_removed(state, &pollset);
}
@@ -2063,7 +2056,7 @@ send_event_tuple(struct erts_nif_select_event* e, ErlNifResource* resource,
ErlOffHeap* ohp;
ErtsBinary* bin;
Eterm* hp;
- Uint hsz = 5 + PROC_BIN_SIZE + REF_THING_SIZE; /* {select, Resource, Ref, EventAtom} */
+ Uint hsz;
Eterm resource_term, ref_term, tuple;
if (!rp) {
@@ -2073,6 +2066,14 @@ send_event_tuple(struct erts_nif_select_event* e, ErlNifResource* resource,
bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource);
+ /* {select, Resource, Ref, EventAtom} */
+ if (is_value(e->immed)) {
+ hsz = 5 + PROC_BIN_SIZE;
+ }
+ else {
+ hsz = 5 + PROC_BIN_SIZE + REF_THING_SIZE;
+ }
+
mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp);
resource_term = erts_mk_magic_binary_term(&hp, ohp, &bin->binary);
@@ -2083,8 +2084,8 @@ send_event_tuple(struct erts_nif_select_event* e, ErlNifResource* resource,
else {
write_ref_thing(hp, e->refn[0], e->refn[1], e->refn[2]);
ref_term = make_internal_ref(hp);
+ hp += REF_THING_SIZE;
}
- hp += REF_THING_SIZE;
tuple = TUPLE4(hp, am_select, resource_term, ref_term, event_atom);
ERL_MESSAGE_TOKEN(mp) = am_undefined;
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index 8c761202d6..2401ff708c 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -447,6 +447,8 @@ select(Config) when is_list(Config) ->
{R,W} = pipe_nif(),
ok = write_nif(W, <<"hej">>),
<<"hej">> = read_nif(R, 3),
+
+ %% Wait for read
eagain = read_nif(R, 3),
0 = select_nif(R,?ERL_NIF_SELECT_READ,R,Ref),
[] = flush(),
@@ -454,15 +456,94 @@ select(Config) when is_list(Config) ->
[{select, R, Ref, ready_input}] = flush(),
<<"hej">> = read_nif(R, 3),
- %% To be extended...
+ %% Wait for write
+ Written = write_full(W, $a),
+ 0 = select_nif(W,?ERL_NIF_SELECT_WRITE,W,Ref),
+ [] = flush(),
+ Half = byte_size(Written) div 2,
+ <<First:Half/binary,Second/binary>> = Written,
+ First = read_nif(R,Half),
+ [{select, W, Ref, ready_output}] = flush(),
+ Third = write_full(W, $A),
+ Half2 = byte_size(Second),
+ <<Second:Half2/binary, Third/binary>> = read_nif(R, byte_size(Written)),
+
+ %% Close write and wait for EOF
+ eagain = read_nif(R, 1),
+ 0 = select_nif(W,?ERL_NIF_SELECT_STOP,W,Ref),
+ timer:sleep(10),
+ true = is_closed_nif(W),
+ [] = flush(),
+ 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,Ref),
+ [{select, R, Ref, ready_input}] = flush(),
+ eof = read_nif(R,1),
0 = select_nif(R,?ERL_NIF_SELECT_STOP,R,Ref),
- 0 = select_nif(W,?ERL_NIF_SELECT_STOP,W,Ref),
+ timer:sleep(10),
+ true = is_closed_nif(R),
+
+ select_2(Config).
+
+select_2(Config) ->
+ erlang:garbage_collect(),
+ {_,_,2} = last_resource_dtor_call(),
+
+ Ref1 = make_ref(),
+ Ref2 = make_ref(),
+ {R,W} = pipe_nif(),
+
+ %% Change ref
+ eagain = read_nif(R, 1),
+ 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,Ref1),
+ 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,Ref2),
+
+ [] = flush(),
+ ok = write_nif(W, <<"hej">>),
+ [{select, R, Ref2, ready_input}] = flush(),
+ <<"hej">> = read_nif(R, 3),
+
+ %% Change pid
+ eagain = read_nif(R, 1),
+ 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,Ref1),
+ Papa = self(),
+ Pid2 = spawn_link(fun() ->
+ 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,Ref1),
+ [] = flush(),
+ Papa ! sync,
+ [{select, R, Ref1, ready_input}] = flush(),
+ <<"hej">> = read_nif(R, 3),
+ Papa ! done
+ end),
+ sync = receive_any(),
+ ok = write_nif(W, <<"hej">>),
+ done = receive_any(),
+ [] = flush(),
+
+ 0 = select_nif(R,?ERL_NIF_SELECT_STOP,R,Ref1),
+ 0 = select_nif(W,?ERL_NIF_SELECT_STOP,W,Ref1),
timer:sleep(10),
true = is_closed_nif(R),
true = is_closed_nif(W),
+
+ select_3(Config).
+
+select_3(Config) ->
+ erlang:garbage_collect(),
+ {_,_,2} = last_resource_dtor_call(),
ok.
+
+
+write_full(W, C) ->
+ write_full(W, C, <<>>).
+write_full(W, C, Acc) ->
+ case write_nif(W, <<C>>) of
+ ok ->
+ write_full(W, (C+1) band 255, <<Acc/binary, C>>);
+ {eagain,0} ->
+ Acc
+ end.
+
hipe(Config) when is_list(Config) ->
Data = proplists:get_value(data_dir, Config),
Priv = proplists:get_value(priv_dir, Config),
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index a4915b13c4..f6ccd3e6ba 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -2176,6 +2176,7 @@ static ERL_NIF_TERM is_closed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
static void fd_resource_dtor(ErlNifEnv* env, void* obj)
{
struct fd_resource* rsrc = (struct fd_resource*)obj;
+ resource_dtor(env, obj);
if (rsrc->fd >= 0)
close(rsrc->fd);
}