Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:23
erlang
2983-erts-Add-new-enif_select_error.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 2983-erts-Add-new-enif_select_error.patch of Package erlang
From 75d49429e0892c8747d3dbf43ac177c6cc807c18 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson <sverker@erlang.org> Date: Fri, 11 Dec 2020 19:03:06 +0100 Subject: [PATCH 3/3] erts: Add new enif_select_error Return ERL_NIF_SELECT_NOTSUP if the underlying poll implementation does not support error events (not Linux). --- erts/emulator/beam/atom.names | 1 + erts/emulator/beam/erl_drv_nif.h | 3 +- erts/emulator/beam/erl_nif.h | 2 + erts/emulator/beam/erl_nif_api_funcs.h | 3 + erts/emulator/sys/common/erl_check_io.c | 73 +++++++++++++--- erts/emulator/sys/common/erl_check_io.h | 1 + erts/emulator/test/nif_SUITE.erl | 85 ++++++++++++++++++- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 26 +++++- 8 files changed, 176 insertions(+), 18 deletions(-) diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index a941534f3b..7febb56e28 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -561,6 +561,7 @@ atom re atom re_pattern atom re_run_trap atom read_concurrency +atom ready_error atom ready_input atom ready_output atom reason diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h index bf14d824b1..53d1a3a531 100644 --- a/erts/emulator/beam/erl_drv_nif.h +++ b/erts/emulator/beam/erl_drv_nif.h @@ -55,7 +55,8 @@ enum ErlNifSelectFlags { ERL_NIF_SELECT_WRITE = (1 << 1), ERL_NIF_SELECT_STOP = (1 << 2), ERL_NIF_SELECT_CANCEL = (1 << 3), - ERL_NIF_SELECT_CUSTOM_MSG= (1 << 4) + ERL_NIF_SELECT_CUSTOM_MSG= (1 << 4), + ERL_NIF_SELECT_ERROR = (1 << 5) }; /* diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 5f25960053..1876193c6c 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -174,6 +174,8 @@ typedef int ErlNifEvent; #define ERL_NIF_SELECT_FAILED (1 << 3) #define ERL_NIF_SELECT_READ_CANCELLED (1 << 4) #define ERL_NIF_SELECT_WRITE_CANCELLED (1 << 5) +#define ERL_NIF_SELECT_ERROR_CANCELLED (1 << 6) +#define ERL_NIF_SELECT_NOTSUP (1 << 7) typedef enum { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index f719dfe868..d8debba6a5 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -642,6 +642,9 @@ static ERL_NIF_INLINE ERL_NIF_TERM enif_make_list9(ErlNifEnv* env, # define enif_select_write(ENV, E, OBJ, PID, MSG, MSG_ENV) \ enif_select_x(ENV, E, ERL_NIF_SELECT_WRITE | ERL_NIF_SELECT_CUSTOM_MSG, \ OBJ, PID, MSG, MSG_ENV) +# define enif_select_error(ENV, E, OBJ, PID, MSG, MSG_ENV) \ + enif_select_x(ENV, E, ERL_NIF_SELECT_ERROR | ERL_NIF_SELECT_CUSTOM_MSG, \ + OBJ, PID, MSG, MSG_ENV) #if SIZEOF_LONG == 8 # define enif_get_int64 enif_get_long diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index fb0b6699ef..02ecd66cbc 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -91,6 +91,7 @@ typedef enum { #else ERTS_EV_FLAG_FALLBACK = ERTS_EV_FLAG_CLEAR, #endif + ERTS_EV_FLAG_WANT_ERROR = 0x10, /* ERL_NIF_SELECT_ERROR turned on */ /* Combinations */ ERTS_EV_FLAG_USED_FALLBACK = ERTS_EV_FLAG_USED | ERTS_EV_FLAG_FALLBACK, @@ -366,6 +367,7 @@ alloc_nif_select_data(void) sizeof(ErtsNifSelectDataState)); dsp->in.pid = NIL; dsp->out.pid = NIL; + dsp->err.pid = NIL; return dsp; } @@ -717,6 +719,7 @@ deselect(ErtsDrvEventState *state, int mode) case ERTS_EV_TYPE_NIF: clear_select_event(&state->driver.nif->in); clear_select_event(&state->driver.nif->out); + clear_select_event(&state->driver.nif->err); enif_release_resource(state->driver.stop.resource->data); state->driver.stop.resource = NULL; break; @@ -1092,7 +1095,7 @@ enif_select_x(ErlNifEnv* env, } on = 0; mode = ERL_DRV_READ | ERL_DRV_WRITE | ERL_DRV_USE; - ctl_events = ERTS_POLL_EV_IN | ERTS_POLL_EV_OUT; + ctl_events = ERTS_POLL_EV_IN | ERTS_POLL_EV_OUT | ERTS_POLL_EV_ERR; ctl_op = ERTS_POLL_OP_DEL; } else { @@ -1104,6 +1107,14 @@ enif_select_x(ErlNifEnv* env, if (mode & ERL_DRV_WRITE) { ctl_events |= ERTS_POLL_EV_OUT; } + if (mode & ERL_NIF_SELECT_ERROR) { +#if (!ERTS_ENABLE_KERNEL_POLL || ERTS_POLL_USE_EPOLL) && defined(ERTS_USE_POLL) + ctl_events |= ERTS_POLL_EV_ERR; +#else + erts_mtx_unlock(fd_mtx(fd)); + return INT_MIN | ERL_NIF_SELECT_NOTSUP; +#endif + } } state = new_drv_ev_state(state,fd); @@ -1152,6 +1163,8 @@ enif_select_x(ErlNifEnv* env, state->active_events |= ctl_events; if (state->type == ERTS_EV_TYPE_NONE) ctl_op = ERTS_POLL_OP_ADD; + if (ctl_events & ERTS_POLL_EV_ERR) + state->flags |= ERTS_EV_FLAG_WANT_ERROR; } else { ctl_events &= old_events; @@ -1162,7 +1175,8 @@ enif_select_x(ErlNifEnv* env, if (ctl_events || ctl_op == ERTS_POLL_OP_DEL) { ErtsPollEvents new_events; - new_events = erts_io_control_wakeup(state, ctl_op, + new_events = erts_io_control_wakeup(state, + ctl_op, state->active_events, &wake_poller); @@ -1172,6 +1186,7 @@ enif_select_x(ErlNifEnv* env, state->flags = 0; state->driver.nif->in.pid = NIL; state->driver.nif->out.pid = NIL; + state->driver.nif->err.pid = NIL; state->driver.stop.resource = NULL; } ret = INT_MIN | ERL_NIF_SELECT_FAILED; @@ -1203,6 +1218,11 @@ enif_select_x(ErlNifEnv* env, if (mode & ERL_DRV_WRITE) { prepare_select_msg(&state->driver.nif->out, mode, recipient, resource, msg, msg_env, am_ready_output); + msg_env = NULL; + } + if (mode & ERL_NIF_SELECT_ERROR) { + prepare_select_msg(&state->driver.nif->err, mode, recipient, + resource, msg, msg_env, am_ready_error); } ret = 0; } @@ -1219,6 +1239,11 @@ enif_select_x(ErlNifEnv* env, clear_select_event(&state->driver.nif->out); ret |= ERL_NIF_SELECT_WRITE_CANCELLED; } + if (mode & ERL_NIF_SELECT_ERROR + && is_not_nil(state->driver.nif->err.pid)) { + clear_select_event(&state->driver.nif->err); + ret |= ERL_NIF_SELECT_ERROR_CANCELLED; + } } if (mode & ERL_NIF_SELECT_STOP) { ASSERT(state->events==0); @@ -1249,6 +1274,7 @@ enif_select_x(ErlNifEnv* env, state->type = ERTS_EV_TYPE_STOP_NIF; ret |= ERL_NIF_SELECT_STOP_SCHEDULED; } + state->flags &= ~ERTS_EV_FLAG_WANT_ERROR; } else ASSERT(mode & ERL_NIF_SELECT_CANCEL); @@ -1371,8 +1397,9 @@ steal(erts_dsprintf_buf_t *dsbufp, ErtsDrvEventState *state, int mode) break; } case ERTS_EV_TYPE_NIF: { - Eterm iid = state->driver.nif->in.pid; - Eterm oid = state->driver.nif->out.pid; + const Eterm iid = state->driver.nif->in.pid; + const Eterm oid = state->driver.nif->out.pid; + const Eterm eid = state->driver.nif->err.pid; const char* with = "with"; ErlNifResourceType* rt = state->driver.stop.resource->type; @@ -1384,6 +1411,10 @@ steal(erts_dsprintf_buf_t *dsbufp, ErtsDrvEventState *state, int mode) } if (is_not_nil(oid)) { erts_dsprintf(dsbufp, " %s out-pid %T", with, oid); + with = "and"; + } + if (is_not_nil(eid)) { + erts_dsprintf(dsbufp, " %s err-pid %T", with, eid); } deselect(state, 0); erts_dsprintf(dsbufp, "\n"); @@ -1738,7 +1769,8 @@ erts_check_io(ErtsPollThread *psi, ErtsMonotonicTime timeout_time, int poll_only DEBUG_PRINT_FD("triggered %s", state, ev2str(revents)); - if (revents & ERTS_POLL_EV_ERR) { + if (revents & ERTS_POLL_EV_ERR + && !(state->flags & ERTS_EV_FLAG_WANT_ERROR)) { /* * Handle error events by triggering all in/out events * that has been selected on. @@ -1812,13 +1844,14 @@ erts_check_io(ErtsPollThread *psi, ErtsMonotonicTime timeout_time, int poll_only } case ERTS_EV_TYPE_NIF: { /* Requested via enif_select()... */ - struct erts_nif_select_event in = {NIL}; - struct erts_nif_select_event out = {NIL}; + struct erts_nif_select_event in_ev = {NIL}; + struct erts_nif_select_event out_ev = {NIL}; + struct erts_nif_select_event err_ev = {NIL}; - if (revents & (ERTS_POLL_EV_IN|ERTS_POLL_EV_OUT)) { + if (revents & (ERTS_POLL_EV_IN | ERTS_POLL_EV_OUT | ERTS_POLL_EV_ERR)) { if (revents & ERTS_POLL_EV_OUT) { if (is_not_nil(state->driver.nif->out.pid)) { - out = state->driver.nif->out; + out_ev = state->driver.nif->out; resource = state->driver.stop.resource; state->driver.nif->out.pid = NIL; state->driver.nif->out.mp = NULL; @@ -1826,12 +1859,20 @@ erts_check_io(ErtsPollThread *psi, ErtsMonotonicTime timeout_time, int poll_only } if (revents & ERTS_POLL_EV_IN) { if (is_not_nil(state->driver.nif->in.pid)) { - in = state->driver.nif->in; + in_ev = state->driver.nif->in; resource = state->driver.stop.resource; state->driver.nif->in.pid = NIL; state->driver.nif->in.mp = NULL; } } + if (revents & ERTS_POLL_EV_ERR) { + if (is_not_nil(state->driver.nif->err.pid)) { + err_ev = state->driver.nif->err; + resource = state->driver.stop.resource; + state->driver.nif->err.pid = NIL; + state->driver.nif->err.mp = NULL; + } + } state->events &= ~revents; } else if (revents & ERTS_POLL_EV_NVAL) { @@ -1841,11 +1882,14 @@ erts_check_io(ErtsPollThread *psi, ErtsMonotonicTime timeout_time, int poll_only erts_mtx_unlock(fd_mtx(fd)); - if (is_not_nil(in.pid)) { - send_select_msg(&in); + if (is_not_nil(in_ev.pid)) { + send_select_msg(&in_ev); + } + if (is_not_nil(out_ev.pid)) { + send_select_msg(&out_ev); } - if (is_not_nil(out.pid)) { - send_select_msg(&out); + if (is_not_nil(err_ev.pid)) { + send_select_msg(&err_ev); } continue; } @@ -2724,6 +2768,7 @@ static int erts_debug_print_checkio_state(erts_dsprintf_buf_t *dsbufp, #endif erts_dsprintf(dsbufp, " inpid=%T", state->driver.nif->in.pid); erts_dsprintf(dsbufp, " outpid=%T", state->driver.nif->out.pid); + erts_dsprintf(dsbufp, " errpid=%T", state->driver.nif->err.pid); r = state->driver.stop.resource; erts_dsprintf(dsbufp, " resource=%p(%T:%T)", r, r->type->module, r->type->name); } diff --git a/erts/emulator/sys/common/erl_check_io.h b/erts/emulator/sys/common/erl_check_io.h index ac285119bc..b96f4f9609 100644 --- a/erts/emulator/sys/common/erl_check_io.h +++ b/erts/emulator/sys/common/erl_check_io.h @@ -148,6 +148,7 @@ struct erts_nif_select_event { typedef struct { struct erts_nif_select_event in; struct erts_nif_select_event out; + struct erts_nif_select_event err; } ErtsNifSelectDataState; #endif /* #ifndef ERL_CHECK_IO_INTERNAL__ */ diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index df4876a294..147b15bb59 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -37,6 +37,7 @@ t_call_nif_early/1, load_traced_nif/1, select/1, select_steal/1, + select_error/1, monitor_process_a/1, monitor_process_b/1, monitor_process_c/1, @@ -91,7 +92,7 @@ all() -> [{group, G} || G <- api_groups()] ++ [reload_error, heap_frag, types, many_args, - select, select_steal, + {group, select}, {group, monitor}, monitor_frenzy, hipe, @@ -135,7 +136,10 @@ groups() -> monitor_process_c, monitor_process_d, monitor_process_purge, - demonitor_process]}]. + demonitor_process]}, + {select, [], [select, + select_error, + select_steal]}]. api_groups() -> [api_latest, api_2_4, api_2_0]. @@ -639,6 +643,7 @@ load_traced_nif(Config) when is_list(Config) -> -define(ERL_NIF_SELECT_STOP, (1 bsl 2)). -define(ERL_NIF_SELECT_CANCEL, (1 bsl 3)). -define(ERL_NIF_SELECT_CUSTOM_MSG, (1 bsl 4)). +-define(ERL_NIF_SELECT_ERROR, (1 bsl 5)). -define(ERL_NIF_SELECT_STOP_CALLED, (1 bsl 0)). -define(ERL_NIF_SELECT_STOP_SCHEDULED, (1 bsl 1)). @@ -646,6 +651,8 @@ load_traced_nif(Config) when is_list(Config) -> -define(ERL_NIF_SELECT_FAILED, (1 bsl 3)). -define(ERL_NIF_SELECT_READ_CANCELLED, (1 bsl 4)). -define(ERL_NIF_SELECT_WRITE_CANCELLED, (1 bsl 5)). +-define(ERL_NIF_SELECT_ERROR_CANCELLED, (1 bsl 6)). +-define(ERL_NIF_SELECT_NOTSUP, (1 bsl 7)). select(Config) when is_list(Config) -> ensure_lib_loaded(Config), @@ -781,6 +788,79 @@ receive_ready(_, Msg, _) -> [Got] = flush(), {true,_,_} = {Got=:=Msg, Got, Msg}. + +select_error(Config) when is_list(Config) -> + ensure_lib_loaded(Config), + + case os:type() of + {unix,linux} -> + select_error_do(); + _ -> + {skipped, "not Linux"} + end. + +select_error_do() -> + RefBin = list_to_binary(lists:duplicate(100, $x)), + + select_error_do1(0, make_ref(), null), + select_error_do1(?ERL_NIF_SELECT_CUSTOM_MSG, [a, "list", RefBin], null), + select_error_do1(?ERL_NIF_SELECT_CUSTOM_MSG, [a, "list", RefBin], alloc_env), + ok. + +select_error_do1(Flag, Ref, MsgEnv) -> + {{R, _R_ptr}, {W, W_ptr}} = pipe_nif(), + ok = write_nif(W, <<"hej">>), + <<"hej">> = read_nif(R, 3), + + %% Wait for error on write end when read end is closed + 0 = select_nif(W, ?ERL_NIF_SELECT_ERROR bor Flag, W, null, Ref, MsgEnv), + [] = flush(0), + 0 = close_nif(R), + receive_ready(W, Ref, ready_error), + + check_stop_ret(select_nif(W, ?ERL_NIF_SELECT_STOP, W, null, Ref, null)), + [{fd_resource_stop, W_ptr, _}] = flush(), + {1, {W_ptr,_}} = last_fd_stop_call(), + true = is_closed_nif(W), + select_error_do2(Flag, Ref, MsgEnv). + +select_error_do2(Flag, Ref, MsgEnv) -> + {{R, _R_ptr}, {W, W_ptr}} = pipe_nif(), + ok = write_nif(W, <<"hej">>), + <<"hej">> = read_nif(R, 3), + + %% Same again but test cancel of error works + 0 = select_nif(W, ?ERL_NIF_SELECT_ERROR bor Flag, W, null, Ref, MsgEnv), + ?ERL_NIF_SELECT_ERROR_CANCELLED = + select_nif(W, ?ERL_NIF_SELECT_ERROR bor ?ERL_NIF_SELECT_CANCEL, W, null, Ref, null), + 0 = close_nif(R), + [] = flush(0), + 0 = select_nif(W, ?ERL_NIF_SELECT_ERROR bor Flag, W, null, Ref, MsgEnv), + receive_ready(W, Ref, ready_error), + + check_stop_ret(select_nif(W, ?ERL_NIF_SELECT_STOP, W, null, Ref, null)), + [{fd_resource_stop, W_ptr, _}] = flush(), + {1, {W_ptr,_}} = last_fd_stop_call(), + true = is_closed_nif(W), + ok. + +-ifdef(NOT_DEFINED). +check_select_error_supported() -> + {{_R, _R_ptr}, {W, W_ptr}} = pipe_nif(), + Ref = make_ref(), + case select_nif(W, ?ERL_NIF_SELECT_ERROR, W, null, Ref, null) of + 0 -> + check_stop_ret(select_nif(W, ?ERL_NIF_SELECT_STOP, W, null, Ref, null)), + [{fd_resource_stop, W_ptr, _}] = flush(), + {1, {W_ptr,_}} = last_fd_stop_call(), + true = is_closed_nif(W), + true; + + Err when Err < 0, (Err band ?ERL_NIF_SELECT_NOTSUP) =/= 0 -> + false + end. +-endif. + %% @doc The stealing child process for the select_steal test. Duplicates given %% W/RFds and runs select on them to steal select_steal_child_process(Parent, RFd) -> @@ -3730,6 +3810,7 @@ dupe_resource_nif(_) -> ?nif_stub. pipe_nif() -> ?nif_stub. write_nif(_,_) -> ?nif_stub. read_nif(_,_) -> ?nif_stub. +close_nif(_) -> ?nif_stub. is_closed_nif(_) -> ?nif_stub. clear_select_nif(_) -> ?nif_stub. last_fd_stop_call() -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 93708fa99c..2c089b430c 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -2550,7 +2550,6 @@ static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv ref_or_msg = enif_make_copy(msg_env, ref_or_msg); } - fdr->was_selected = 1; enif_self(env, &fdr->pid); switch (mode) { case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_READ: @@ -2559,10 +2558,17 @@ static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_WRITE: retval = enif_select_write(env, fdr->fd, obj, pid, ref_or_msg, msg_env); break; + case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_ERROR: + retval = enif_select_error(env, fdr->fd, obj, pid, ref_or_msg, msg_env); + break; default: retval = enif_select(env, fdr->fd, mode, obj, pid, ref_or_msg); } + if (retval >= 0) { + fdr->was_selected = 1; + } + if (msg_env) enif_free_env(msg_env); @@ -2698,6 +2704,23 @@ static ERL_NIF_TERM read_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] } } +static ERL_NIF_TERM close_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + struct fd_resource* fdr; + int ret; + + if (!get_fd(env, argv[0], &fdr)) + return enif_make_badarg(env); + + assert(fdr->fd > 0); + assert(!fdr->was_selected); + + ret = close(fdr->fd); + fdr->fd = -1; + + return enif_make_int(env, ret); +} + static ERL_NIF_TERM is_closed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { struct fd_resource* fdr; @@ -3729,6 +3752,7 @@ static ErlNifFunc nif_funcs[] = {"write_nif", 2, write_nif}, {"dupe_resource_nif", 1, dupe_resource_nif}, {"read_nif", 2, read_nif}, + {"close_nif", 1, close_nif}, {"is_closed_nif", 1, is_closed_nif}, {"clear_select_nif", 1, clear_select_nif}, #endif -- 2.26.2
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor