From 884afd8efb8672df2889df98b5b94f3dbb53952c Mon Sep 17 00:00:00 2001
From: Steve Vinoski
Date: Mon, 27 Apr 2015 23:39:37 -0400
Subject: Enhance enif_has_pending_exception
Sverker Eriksson came up with the following idea: to handle a future
ability for NIFs to raise more than just badarg exceptions, modify the
recently-added enif_has_pending_exception function to take a second
argument: a pointer to ERL_NIF_TERM. If this argument is a null
pointer, ignore it. Otherwise, if the first argument, an ErlNifEnv*,
has an associated exception, set the pointed-to ERL_NIF_TERM of the
second argument to the value of the exception term. Add new tests and
documentation for this modification.
---
erts/doc/src/erl_nif.xml | 12 +++++++++---
erts/emulator/beam/erl_nif.c | 5 ++++-
erts/emulator/beam/erl_nif_api_funcs.h | 2 +-
erts/emulator/test/nif_SUITE.erl | 18 ++++++++++--------
erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 25 +++++++++++++++++--------
5 files changed, 41 insertions(+), 21 deletions(-)
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 4bad8b253c..155aee2f42 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -778,11 +778,17 @@ typedef enum {
and return true, or return false if term is not an unsigned integer or is
outside the bounds of type unsigned long.
- intenif_has_pending_exception(ErlNifEnv* env)
+ intenif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)
Check if an exception has been raised.
Return true if a pending exception is associated
- with the environment env. The only possible exception is currently
- badarg (see enif_make_badarg).
+ with the environment env. If reason is a null pointer, ignore it.
+ Otherwise, if there's a pending exception associated with env, set the ERL_NIF_TERM
+ to which reason points to the value of the exception's term. For example, if
+ enif_make_badarg is called to set a
+ pending badarg exception, a subsequent call to enif_has_pending_exception(env, &reason)
+ will set reason to the atom badarg, then return true.
+ See also: enif_make_badarg.
+
intenif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin)
Inspect the content of a binary
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 7876785a76..36da6519f7 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -741,8 +741,11 @@ Eterm enif_make_badarg(ErlNifEnv* env)
BIF_ERROR(env->proc, BADARG);
}
-int enif_has_pending_exception(ErlNifEnv* env)
+int enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)
{
+ if (env->exception_thrown && reason != NULL) {
+ *reason = am_badarg;
+ }
return env->exception_thrown;
}
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index bdcbb32c46..fe25c803ec 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -156,7 +156,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIte
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter));
ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value));
ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[]));
-ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env));
+ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env, ERL_NIF_TERM* reason));
/*
** ADD NEW ENTRIES HERE (before this comment) !!!
diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl
index c35c71dd5b..434d1c38d0 100644
--- a/erts/emulator/test/nif_SUITE.erl
+++ b/erts/emulator/test/nif_SUITE.erl
@@ -1387,7 +1387,7 @@ is_checks(Config) when is_list(Config) ->
self(), hd(erlang:ports()), [], [1,9,9,8],
{hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551616.2e2),
try
- ?line error = check_is_exception(),
+ ?line check_is_exception(),
?line throw(expected_badarg)
catch
error:badarg ->
@@ -1599,9 +1599,9 @@ dirty_nif_exception(Config) when is_list(Config) ->
N when is_integer(N) ->
ensure_lib_loaded(Config),
try
- %% this checks that the expected exception
- %% occurs when the NIF returns the result
- %% of enif_make_badarg directly
+ %% this checks that the expected exception occurs when the
+ %% dirty NIF returns the result of enif_make_badarg
+ %% directly
call_dirty_nif_exception(1),
?t:fail(expected_badarg)
catch
@@ -1611,10 +1611,9 @@ dirty_nif_exception(Config) when is_list(Config) ->
ok
end,
try
- %% this checks that the expected exception
- %% occurs when the NIF calls enif_make_badarg
- %% at some point but then returns a value that
- %% isn't an exception
+ %% this checks that the expected exception occurs when the
+ %% dirty NIF calls enif_make_badarg at some point but then
+ %% returns a value that isn't an exception
call_dirty_nif_exception(0),
?t:fail(expected_badarg)
catch
@@ -1631,6 +1630,9 @@ dirty_nif_exception(Config) when is_list(Config) ->
nif_exception(Config) when is_list(Config) ->
ensure_lib_loaded(Config),
try
+ %% this checks that the expected exception occurs when the NIF
+ %% calls enif_make_badarg at some point but then tries to return a
+ %% value that isn't an exception
call_nif_exception(),
?t:fail(expected_badarg)
catch
diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
index 58d9a9a88c..95e361690f 100644
--- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
+++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
@@ -883,16 +883,19 @@ static ERL_NIF_TERM check_is(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]
*
* This function is separate from check_is because it calls enif_make_badarg
* and so it must return the badarg exception as its return value. Thus, the
- * badarg exception indicates success. Failure is indicated by returning an
- * error atom.
+ * badarg exception indicates success.
*/
static ERL_NIF_TERM check_is_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
+ ERL_NIF_TERM badarg, exc_term;
ERL_NIF_TERM error_atom = enif_make_atom(env, "error");
- ERL_NIF_TERM badarg = enif_make_badarg(env);
- if (enif_is_exception(env, error_atom)) return error_atom;
- if (!enif_is_exception(env, badarg)) return error_atom;
- if (!enif_has_pending_exception(env)) return error_atom;
+ ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg");
+ assert(!enif_is_exception(env, error_atom));
+ badarg = enif_make_badarg(env);
+ assert(enif_is_exception(env, badarg));
+ assert(enif_has_pending_exception(env, NULL));
+ assert(enif_has_pending_exception(env, &exc_term));
+ assert(enif_is_identical(exc_term, badarg_atom));
return badarg;
}
@@ -1621,7 +1624,7 @@ static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL
for (i = 1; i < 255; i++)
args[i] = enif_make_int(env, i);
return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND,
- call_dirty_nif_exception, 255, argv);
+ call_dirty_nif_exception, 255, args);
}
case 2: {
int return_badarg_directly;
@@ -1660,7 +1663,13 @@ static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL
*/
static ERL_NIF_TERM call_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
+ ERL_NIF_TERM exc_term;
+ ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg");
+
/* ignore return value */ enif_make_badarg(env);
+ assert(enif_has_pending_exception(env, NULL));
+ assert(enif_has_pending_exception(env, &exc_term));
+ assert(enif_is_identical(badarg_atom, exc_term));
return enif_make_atom(env, "ok");
}
@@ -1696,7 +1705,7 @@ static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_
}
res = enif_make_double(env, val);
assert(enif_is_exception(env, res));
- assert(enif_has_pending_exception(env));
+ assert(enif_has_pending_exception(env, NULL));
if (strcmp(arg, "tuple") == 0) {
return enif_make_tuple2(env, argv[0], res);
} else {
--
cgit v1.2.3