From 7a0a52a9a1adc8f87365936f4e76739b7850f1b3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 12 Mar 2019 14:56:20 +0100 Subject: erts: Schedule resource destructors always to run user NIF code in a more known execution context. Fixes problems like user calling enif_whereis_pid() in destructor which may need to release process main lock in order to lock reg_tab. --- erts/emulator/test/nif_SUITE.erl | 17 +++++++++++++++-- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'erts/emulator/test') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index ca5f90621f..6ca0990a8a 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -28,6 +28,7 @@ -include_lib("stdlib/include/assert.hrl"). -export([all/0, suite/0, groups/0, + init_per_suite/1, end_per_suite/1, init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2, basic/1, reload_error/1, upgrade/1, heap_frag/1, @@ -105,6 +106,14 @@ all() -> nif_whereis, nif_whereis_parallel, nif_whereis_threaded, nif_ioq]. +init_per_suite(Config) -> + erts_debug:set_internal_state(available_internal_state, true), + Config. + +end_per_suite(_Config) -> + catch erts_debug:set_internal_state(available_internal_state, false), + ok. + groups() -> [{G, [], api_repeaters()} || G <- api_groups()] ++ @@ -114,7 +123,6 @@ groups() -> monitor_process_d, demonitor_process]}]. - api_groups() -> [api_latest, api_2_4, api_2_0]. api_repeaters() -> [upgrade, resource_takeover, t_on_load]. @@ -1711,6 +1719,7 @@ read_resource(Type, {Holder,Id}) -> forget_resource({Holder,Id}) -> Holder ! {self(), forget, Id}, {Holder, forget_ok, Id} = receive_any(), + erts_debug:set_internal_state(wait, aux_work), ok. @@ -3327,6 +3336,10 @@ make_unaligned_binary(Bin0) -> <<0:3,Bin:Size/binary,31:5>> = id(<<0:3,Bin0/binary,31:5>>), Bin. +last_resource_dtor_call() -> + erts_debug:set_internal_state(wait, aux_work), + last_resource_dtor_call_nif(). + id(I) -> I. %% The NIFs: @@ -3354,7 +3367,7 @@ make_resource(_) -> ?nif_stub. get_resource(_,_) -> ?nif_stub. release_resource(_) -> ?nif_stub. release_resource_from_thread(_) -> ?nif_stub. -last_resource_dtor_call() -> ?nif_stub. +last_resource_dtor_call_nif() -> ?nif_stub. make_new_resource(_,_) -> ?nif_stub. check_is(_,_,_,_,_,_,_,_,_,_,_) -> ?nif_stub. check_is_exception() -> ?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 f2ce6dbe67..17ba3ce297 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -866,7 +866,7 @@ static ERL_NIF_TERM iolist_2_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar return enif_make_binary(env,&obin); } -static ERL_NIF_TERM last_resource_dtor_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +static ERL_NIF_TERM last_resource_dtor_call_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM ret; if (resource_dtor_last != NULL) { @@ -3512,7 +3512,7 @@ static ErlNifFunc nif_funcs[] = {"get_resource", 2, get_resource}, {"release_resource", 1, release_resource}, {"release_resource_from_thread", 1, release_resource_from_thread}, - {"last_resource_dtor_call", 0, last_resource_dtor_call}, + {"last_resource_dtor_call_nif", 0, last_resource_dtor_call_nif}, {"make_new_resource", 2, make_new_resource}, {"check_is", 11, check_is}, {"check_is_exception", 0, check_is_exception}, -- cgit v1.2.3 From 358df187825617008c2c3705c6b390f44978d4e3 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 19 Mar 2019 20:01:10 +0100 Subject: erts: Simplify nif_SUITE:nif_whereis* tests and change some argc checks from badarg to assert. --- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 138 +++++++++++--------------- 1 file changed, 60 insertions(+), 78 deletions(-) (limited to 'erts/emulator/test') diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 17ba3ce297..9ae5966e80 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -27,6 +27,7 @@ #ifndef __WIN32__ #include #include +#include #endif #include "nif_mod.h" @@ -707,28 +708,23 @@ static ERL_NIF_TERM tuple_2_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar static ERL_NIF_TERM is_identical(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - if (argc != 2) { - return enif_make_badarg(env); - } + assert(argc == 2); return enif_make_atom(env, (enif_is_identical(argv[0],argv[1]) ? "true" : "false")); } static ERL_NIF_TERM compare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - if (argc != 2) { - return enif_make_badarg(env); - } + assert(argc == 2); return enif_make_int(env, enif_compare(argv[0],argv[1])); } static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - if (argc != 3) { - return enif_make_badarg(env); - } - ErlNifHash type; + ErlNifUInt64 salt; + + assert(argc == 3); if (enif_is_identical(argv[0], enif_make_atom(env, "internal"))) { type = ERL_NIF_INTERNAL_HASH; } @@ -739,7 +735,6 @@ static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] return enif_make_badarg(env); } - ErlNifUInt64 salt; if (! enif_get_uint64(env, argv[2], &salt)) { return enif_make_badarg(env); } @@ -1010,6 +1005,7 @@ static ERL_NIF_TERM release_resource(ErlNifEnv* env, int argc, const ERL_NIF_TER static void* threaded_release_resource(void* resource) { enif_release_resource(resource); + return NULL; } static ERL_NIF_TERM release_resource_from_thread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) @@ -1201,7 +1197,6 @@ static void fill(void* dst, unsigned bytes, int seed) enum { /* results */ WHEREIS_SUCCESS, - WHEREIS_ERROR_TYPE, WHEREIS_ERROR_LOOKUP, WHEREIS_ERROR_SEND, /* types */ @@ -1221,6 +1216,7 @@ typedef struct { whereis_term_data_t res; ErlNifTid tid; int type; + int rc; } whereis_thread_resource_t; static whereis_thread_resource_t* whereis_thread_resource_create(void) @@ -1239,15 +1235,17 @@ static void whereis_thread_resource_dtor(ErlNifEnv* env, void* obj) enif_free_env(rp->env); } -static int whereis_type(ERL_NIF_TERM type) +static int whereis_type(ERL_NIF_TERM type_term, int* type_p) { - if (enif_is_identical(type, atom_pid)) - return WHEREIS_LOOKUP_PID; - - if (enif_is_identical(type, atom_port)) - return WHEREIS_LOOKUP_PORT; - - return WHEREIS_ERROR_TYPE; + if (enif_is_identical(type_term, atom_pid)) { + *type_p = WHEREIS_LOOKUP_PID; + return 1; + } + if (enif_is_identical(type_term, atom_port)) { + *type_p = WHEREIS_LOOKUP_PORT; + return 1; + } + return 0; } static int whereis_lookup_internal( @@ -1261,7 +1259,7 @@ static int whereis_lookup_internal( return enif_whereis_port(env, name, & out->port) ? WHEREIS_SUCCESS : WHEREIS_ERROR_LOOKUP; - return WHEREIS_ERROR_TYPE; + abort(); } static int whereis_send_internal( @@ -1275,23 +1273,20 @@ static int whereis_send_internal( return enif_port_command(env, & to->port, NULL, msg) ? WHEREIS_SUCCESS : WHEREIS_ERROR_SEND; - return WHEREIS_ERROR_TYPE; + abort(); } -static int whereis_resolved_term( - ErlNifEnv* env, int type, whereis_term_data_t* res, ERL_NIF_TERM* out) +static ERL_NIF_TERM whereis_resolved_term( + ErlNifEnv* env, int type, whereis_term_data_t* res) { switch (type) { case WHEREIS_LOOKUP_PID: - *out = enif_make_pid(env, & res->pid); - break; + return enif_make_pid(env, &res->pid); case WHEREIS_LOOKUP_PORT: - *out = enif_make_port(env, & res->port); - break; + return enif_make_port(env, &res->port); default: - return WHEREIS_ERROR_TYPE; + abort(); } - return WHEREIS_SUCCESS; } static ERL_NIF_TERM whereis_result_term(ErlNifEnv* env, int result) @@ -1307,9 +1302,6 @@ static ERL_NIF_TERM whereis_result_term(ErlNifEnv* env, int result) case WHEREIS_ERROR_SEND: err = atom_send; break; - case WHEREIS_ERROR_TYPE: - err = atom_badarg; - break; default: err = enif_make_int(env, -result); break; @@ -1320,14 +1312,10 @@ static ERL_NIF_TERM whereis_result_term(ErlNifEnv* env, int result) static void* whereis_lookup_thread(void* arg) { whereis_thread_resource_t* rp = (whereis_thread_resource_t*) arg; - int rc; - /* enif_whereis_xxx should work with allocated or null env */ - rc = whereis_lookup_internal( - ((rp->type == WHEREIS_LOOKUP_PID) ? NULL : rp->env), - rp->type, rp->name, & rp->res); + rp->rc = whereis_lookup_internal(NULL, rp->type, rp->name, &rp->res); - return (((char*) NULL) + rc); + return NULL; } /* whereis_term(Type, Name) -> pid() | port() | false */ @@ -1338,17 +1326,14 @@ whereis_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ERL_NIF_TERM ret; int type, rc; - if (argc != 2) /* allow non-atom name for testing */ - return enif_make_badarg(env); - - if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE) + assert(argc == 2); + if (!whereis_type(argv[0], &type)) return enif_make_badarg(env); rc = whereis_lookup_internal(env, type, argv[1], & res); - if (rc == WHEREIS_SUCCESS) { - rc = whereis_resolved_term(env, type, & res, & ret); - } - return (rc == WHEREIS_SUCCESS) ? ret : atom_false; + return (rc == WHEREIS_SUCCESS ? + whereis_resolved_term(env, type, &res) : + atom_false); } /* whereis_send(Type, Name, Message) -> ok | {error, Reason} */ @@ -1358,10 +1343,11 @@ whereis_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) whereis_term_data_t to; int type, rc; - if (argc != 3 || !enif_is_atom(env, argv[1])) + assert(argc == 3); + if (!enif_is_atom(env, argv[1])) return enif_make_badarg(env); - if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE) + if (!whereis_type(argv[0], &type)) return enif_make_badarg(env); rc = whereis_lookup_internal(env, type, argv[1], & to); @@ -1378,10 +1364,11 @@ whereis_thd_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) whereis_thread_resource_t* rp; int type, rc; - if (argc != 2 || !enif_is_atom(env, argv[1])) + assert(argc == 3); + if (!enif_is_atom(env, argv[1])) return enif_make_badarg(env); - if ((type = whereis_type(argv[0])) == WHEREIS_ERROR_TYPE) + if (!whereis_type(argv[0], &type)) return enif_make_badarg(env); rp = whereis_thread_resource_create(); @@ -1406,22 +1393,21 @@ whereis_thd_result(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { whereis_thread_resource_t* rp; ERL_NIF_TERM ret; - char* thdret; /* so we can keep compilers happy converting to int */ - int rc; + int join_rc; - if (argc != 1 - || !enif_get_resource(env, argv[0], whereis_resource_type, (void**) & rp)) + assert(argc == 1); + if (!enif_get_resource(env, argv[0], whereis_resource_type, (void**) & rp)) return enif_make_badarg(env); - if ((rc = enif_thread_join(rp->tid, (void**) & thdret)) != 0) - return enif_make_tuple2(env, atom_error, enif_make_int(env, rc)); + if ((join_rc = enif_thread_join(rp->tid, NULL)) != 0) + return enif_make_tuple2(env, atom_error, enif_make_int(env, join_rc)); - rc = (int)(thdret - ((char*) NULL)); - if (rc == WHEREIS_SUCCESS) { - rc = whereis_resolved_term(env, rp->type, & rp->res, & ret); + if (rp->rc == WHEREIS_SUCCESS) { + ret = enif_make_tuple2(env, atom_ok, + whereis_resolved_term(env, rp->type, &rp->res)); } - ret = (rc == WHEREIS_SUCCESS) - ? enif_make_tuple2(env, atom_ok, ret) : whereis_result_term(env, rc); + else + ret = whereis_result_term(env, rp->rc); enif_release_resource(rp); return ret; @@ -1996,8 +1982,7 @@ static ERL_NIF_TERM nif_sched1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM result; - if (argc != 2) - return enif_make_atom(env, "false"); + assert(argc == 2); result = enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv); assert(!enif_is_exception(env, result)); return result; @@ -2144,7 +2129,8 @@ static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_T ERL_NIF_TERM result, cell; unsigned count; - if (argc != 1 || !enif_get_list_length(env, argv[0], &count)) { + assert(argc == 1); + if (!enif_get_list_length(env, argv[0], &count)) { return enif_make_badarg(env); } @@ -2191,7 +2177,8 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER ErlNifMapIterator iter_b; int cnt, next_ret, prev_ret; - if (argc != 1 && !enif_is_map(env, map)) + assert(argc == 1); + if (!enif_is_map(env, map)) return enif_make_int(env, __LINE__); if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_FIRST)) @@ -2252,9 +2239,7 @@ static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM { ErlNifTimeUnit time_unit; - if (argc != 1) - return atom_false; - + assert(argc == 1); if (enif_compare(argv[0], atom_second) == 0) time_unit = ERL_NIF_SEC; else if (enif_compare(argv[0], atom_millisecond) == 0) @@ -2273,9 +2258,7 @@ static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg { ErlNifTimeUnit time_unit; - if (argc != 1) - return atom_false; - + assert(argc == 1); if (enif_compare(argv[0], atom_second) == 0) time_unit = ERL_NIF_SEC; else if (enif_compare(argv[0], atom_millisecond) == 0) @@ -2295,9 +2278,7 @@ static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TE ErlNifTime val; ErlNifTimeUnit from, to; - if (argc != 3) - return atom_false; - + assert(argc == 3); if (!enif_get_int64(env, argv[0], &i64)) return enif_make_badarg(env); @@ -3281,7 +3262,7 @@ static int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail, Erl } /* Attempt to write the data */ - n = writev(fd, sysiovec, iovcnt); + n = writev(fd, (struct iovec*)sysiovec, iovcnt); saved_errno = errno; if (enif_ioq_size(q) == 0) { @@ -3355,10 +3336,11 @@ static ERL_NIF_TERM ioq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if (enif_is_identical(argv[0], enif_make_atom(env, "example"))) { #ifndef __WIN32__ - int fd[2], res = 0, cnt = 0, queue_cnt; + int fd[2], res = 0, cnt = 0; ERL_NIF_TERM tail; char buff[255]; - pipe(fd); + res = pipe(fd); + assert(res == 0); fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_NONBLOCK); fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL) | O_NONBLOCK); -- cgit v1.2.3 From 6b414fbc67d2c7f703fe677f2acda1a6fa67d8ed Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 19 Mar 2019 20:01:45 +0100 Subject: erts: Add test of enif_whereis* from resource destructor --- erts/emulator/test/nif_SUITE.erl | 27 ++++++++++++++------- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 34 ++++++++++++++++++--------- 2 files changed, 41 insertions(+), 20 deletions(-) (limited to 'erts/emulator/test') diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index 6ca0990a8a..d8c2cb0e43 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -3065,22 +3065,31 @@ nif_whereis_threaded(Config) when is_list(Config) -> RegName = nif_whereis_test_threaded, undefined = erlang:whereis(RegName), - Ref = make_ref(), - {Pid, Mon} = spawn_monitor(?MODULE, nif_whereis_proxy, [Ref]), - true = register(RegName, Pid), + Self = self(), + true = register(RegName, Self), - {ok, ProcThr} = whereis_thd_lookup(pid, RegName), - {ok, Pid} = whereis_thd_result(ProcThr), + {ok, ProcThr} = whereis_thd_lookup(pid, RegName, "dtor to proc"), + {ok, Self} = whereis_thd_result(ProcThr), - Pid ! {Ref, quit}, - ok = receive {'DOWN', Mon, process, Pid, normal} -> ok end, + nif_whereis_threaded_2(RegName). + +nif_whereis_threaded_2(RegName) -> + erlang:garbage_collect(), + "dtor to proc" = receive_any(1000), + true = unregister(RegName), Port = open_port({spawn, echo_drv}, [eof]), true = register(RegName, Port), - {ok, PortThr} = whereis_thd_lookup(port, RegName), + {ok, PortThr} = whereis_thd_lookup(port, RegName, "dtor to port"), {ok, Port} = whereis_thd_result(PortThr), + nif_whereis_threaded_3(Port). + +nif_whereis_threaded_3(Port) -> + erlang:garbage_collect(), + {Port, {data, "dtor to port"}} = receive_any(1000), + port_close(Port), ok. @@ -3426,7 +3435,7 @@ ioq_nif(_,_,_,_) -> ?nif_stub. %% whereis whereis_send(_Type,_Name,_Msg) -> ?nif_stub. whereis_term(_Type,_Name) -> ?nif_stub. -whereis_thd_lookup(_Type,_Name) -> ?nif_stub. +whereis_thd_lookup(_Type,_Name, _Msg) -> ?nif_stub. whereis_thd_result(_Thd) -> ?nif_stub. %% maps diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 9ae5966e80..463aa3b246 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -1217,6 +1217,7 @@ typedef struct { ErlNifTid tid; int type; int rc; + ERL_NIF_TERM dtor_msg; } whereis_thread_resource_t; static whereis_thread_resource_t* whereis_thread_resource_create(void) @@ -1229,9 +1230,21 @@ static whereis_thread_resource_t* whereis_thread_resource_create(void) return rp; } +static int whereis_lookup_internal(ErlNifEnv*, int type, ERL_NIF_TERM name, + whereis_term_data_t* out); +static int whereis_send_internal(ErlNifEnv*, int type, whereis_term_data_t* to, + ERL_NIF_TERM msg); + + static void whereis_thread_resource_dtor(ErlNifEnv* env, void* obj) { whereis_thread_resource_t* rp = (whereis_thread_resource_t*) obj; + whereis_term_data_t to; + + if (whereis_lookup_internal(env, rp->type, rp->name, &to) + == WHEREIS_SUCCESS) { + whereis_send_internal(env, rp->type, &to, rp->dtor_msg); + } enif_free_env(rp->env); } @@ -1323,7 +1336,6 @@ static ERL_NIF_TERM whereis_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { whereis_term_data_t res; - ERL_NIF_TERM ret; int type, rc; assert(argc == 2); @@ -1357,12 +1369,13 @@ whereis_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return whereis_result_term(env, rc); } -/* whereis_thd_lookup(Type, Name) -> {ok, Resource} | {error, SysErrno} */ +/* whereis_thd_lookup(Type, Name, DtorMsg) -> {ok, Resource} | {error, SysErrno} */ static ERL_NIF_TERM whereis_thd_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { whereis_thread_resource_t* rp; int type, rc; + ERL_NIF_TERM ret; assert(argc == 3); if (!enif_is_atom(env, argv[1])) @@ -1374,17 +1387,17 @@ whereis_thd_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) rp = whereis_thread_resource_create(); rp->type = type; rp->name = enif_make_copy(rp->env, argv[1]); + rp->dtor_msg = enif_make_copy(rp->env, argv[2]); rc = enif_thread_create( "nif_SUITE:whereis_thd", & rp->tid, whereis_lookup_thread, rp, NULL); - if (rc == 0) { - return enif_make_tuple2(env, atom_ok, enif_make_resource(env, rp)); - } - else { - enif_release_resource(rp); - return enif_make_tuple2(env, atom_error, enif_make_int(env, rc)); - } + if (rc == 0) + ret = enif_make_tuple2(env, atom_ok, enif_make_resource(env, rp)); + else + ret = enif_make_tuple2(env, atom_error, enif_make_int(env, rc)); + enif_release_resource(rp); + return ret; } /* whereis_thd_result(Resource) -> {ok, pid() | port()} | {error, ErrNum} */ @@ -1409,7 +1422,6 @@ whereis_thd_result(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) else ret = whereis_result_term(env, rp->rc); - enif_release_resource(rp); return ret; } @@ -3564,7 +3576,7 @@ static ErlNifFunc nif_funcs[] = {"monitor_frenzy_nif", 4, monitor_frenzy_nif}, {"whereis_send", 3, whereis_send}, {"whereis_term", 2, whereis_term}, - {"whereis_thd_lookup", 2, whereis_thd_lookup}, + {"whereis_thd_lookup", 3, whereis_thd_lookup}, {"whereis_thd_result", 1, whereis_thd_result}, {"ioq_nif", 1, ioq}, {"ioq_nif", 2, ioq}, -- cgit v1.2.3