/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2009-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* %CopyrightEnd%
*/
#include <erl_nif.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include <errno.h>
#ifndef __WIN32__
#include <unistd.h>
#include <fcntl.h>
#endif
#include "nif_mod.h"
#if 0
static ErlNifMutex* dbg_trace_lock;
#define DBG_TRACE_INIT dbg_trace_lock = enif_mutex_create("nif_SUITE.DBG_TRACE")
#define DBG_TRACE_FINI enif_mutex_destroy(dbg_trace_lock)
#define DBG_TRACE_LOCK enif_mutex_lock(dbg_trace_lock)
#define DBG_TRACE_UNLOCK enif_mutex_unlock(dbg_trace_lock)
#define DBG_TRACE0(FMT) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT); DBG_TRACE_UNLOCK; }while(0)
#define DBG_TRACE1(FMT, A) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A); DBG_TRACE_UNLOCK; }while(0)
#define DBG_TRACE2(FMT, A, B) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A, B); DBG_TRACE_UNLOCK; }while(0)
#define DBG_TRACE3(FMT, A, B, C) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A, B, C); DBG_TRACE_UNLOCK; }while(0)
#define DBG_TRACE4(FMT, A, B, C, D) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A, B, C, D); DBG_TRACE_UNLOCK; }while(0)
#else
#define DBG_TRACE_INIT
#define DBG_TRACE_FINI
#define DBG_TRACE0(FMT)
#define DBG_TRACE1(FMT, A)
#define DBG_TRACE2(FMT, A, B)
#define DBG_TRACE3(FMT, A, B, C)
#define DBG_TRACE4(FMT, A, B, C, D)
#endif
static int static_cntA; /* zero by default */
static int static_cntB = NIF_SUITE_LIB_VER * 100;
static ERL_NIF_TERM atom_false;
static ERL_NIF_TERM atom_true;
static ERL_NIF_TERM atom_self;
static ERL_NIF_TERM atom_ok;
static ERL_NIF_TERM atom_join;
static ERL_NIF_TERM atom_binary_resource_type;
static ERL_NIF_TERM atom_second;
static ERL_NIF_TERM atom_millisecond;
static ERL_NIF_TERM atom_microsecond;
static ERL_NIF_TERM atom_nanosecond;
static ERL_NIF_TERM atom_eagain;
static ERL_NIF_TERM atom_eof;
static ERL_NIF_TERM atom_error;
static ERL_NIF_TERM atom_fd_resource_stop;
static ERL_NIF_TERM atom_monitor_resource_type;
static ERL_NIF_TERM atom_monitor_resource_down;
static ERL_NIF_TERM atom_init;
static ERL_NIF_TERM atom_stats;
static ERL_NIF_TERM atom_done;
static ERL_NIF_TERM atom_stop;
static ERL_NIF_TERM atom_null;
typedef struct
{
int ref_cnt;
CallInfo* call_history;
NifModPrivData* nif_mod;
union { ErlNifResourceType* t; void* vp; } rt_arr[2];
} PrivData;
/*
* Use a union for pointer type conversion to avoid compiler warnings
* about strict-aliasing violations with gcc-4.1. gcc >= 4.2 does not
* emit the warning.
* TODO: Reconsider use of union once gcc-4.1 is obsolete?
*/
typedef union {
void* vp;
struct make_term_info* p;
} mti_t;
void add_call(ErlNifEnv* env, PrivData* data, const char* func_name)
{
CallInfo* call = enif_alloc(sizeof(CallInfo)+strlen(func_name));
strcpy(call->func_name, func_name);
call->lib_ver = NIF_SUITE_LIB_VER;
call->next = data->call_history;
call->static_cntA = ++static_cntA;
call->static_cntB = ++static_cntB;
data->call_history = call;
call->arg = NULL;
call->arg_sz = 0;
}
#define ADD_CALL(FUNC_NAME) add_call(env, enif_priv_data(env),FUNC_NAME)
static void* resource_dtor_last = NULL;
static unsigned resource_dtor_last_sz = 0;
static char resource_dtor_last_data[20];
static int resource_dtor_cnt = 0;
static void resource_dtor(ErlNifEnv* env, void* obj)
{
resource_dtor_last = obj;
resource_dtor_cnt++;
resource_dtor_last_sz = enif_sizeof_resource(obj);
assert(resource_dtor_last_sz <= sizeof(resource_dtor_last_data));
memcpy(resource_dtor_last_data, obj, resource_dtor_last_sz);
}
static ErlNifResourceType* msgenv_resource_type;
static void msgenv_dtor(ErlNifEnv* env, void* obj);
static ErlNifResourceType* binary_resource_type;
static void binary_resource_dtor(ErlNifEnv* env, void* obj);
struct binary_resource {
unsigned char* data;
unsigned size;
};
static ErlNifResourceType* fd_resource_type;
static void fd_resource_dtor(ErlNifEnv* env, void* obj);
static void fd_resource_stop(ErlNifEnv* env, void* obj, ErlNifEvent, int);
static ErlNifResourceTypeInit fd_rt_init = {
fd_resource_dtor,
fd_resource_stop
};
struct fd_resource {
ErlNifEvent fd;
int was_selected;
ErlNifPid pid;
};
static ErlNifResourceType* monitor_resource_type;
static void monitor_resource_dtor(ErlNifEnv* env, void* obj);
static void monitor_resource_down(ErlNifEnv*, void* obj, ErlNifPid*, ErlNifMonitor*);
static ErlNifResourceTypeInit monitor_rt_init = {
monitor_resource_dtor,
NULL,
monitor_resource_down
};
struct monitor_resource {
ErlNifPid receiver;
int use_msgenv;
};
static ErlNifResourceType* frenzy_resource_type;
static void frenzy_resource_dtor(ErlNifEnv* env, void* obj);
static void frenzy_resource_down(ErlNifEnv*, void* obj, ErlNifPid*, ErlNifMonitor*);
static ErlNifResourceTypeInit frenzy_rt_init = {
frenzy_resource_dtor,
NULL,
frenzy_resource_down
};
static int get_pointer(ErlNifEnv* env, ERL_NIF_TERM term, void** pp)
{
ErlNifBinary bin;
int r = enif_inspect_binary(env, term, &bin);
if (r) {
*pp = *(void**)bin.data;
}
return r;
}
static ERL_NIF_TERM make_pointer(ErlNifEnv* env, void* p)
{
void** bin_data;
ERL_NIF_TERM res;
bin_data = (void**)enif_make_new_binary(env, sizeof(void*), &res);
*bin_data = p;
return res;
}
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
PrivData* data = enif_alloc(sizeof(PrivData));
assert(data != NULL);
data->ref_cnt = 1;
data->call_history = NULL;
data->nif_mod = NULL;
DBG_TRACE_INIT;
add_call(env, data, "load");
data->rt_arr[0].t = enif_open_resource_type(env,NULL,"Gold",resource_dtor,
ERL_NIF_RT_CREATE,NULL);
data->rt_arr[1].t = enif_open_resource_type(env,NULL,"Silver",resource_dtor,
ERL_NIF_RT_CREATE,NULL);
binary_resource_type = enif_open_resource_type(env,NULL,"nif_SUITE.binary",
binary_resource_dtor,
ERL_NIF_RT_CREATE, NULL);
msgenv_resource_type = enif_open_resource_type(env,NULL,"nif_SUITE.msgenv",
msgenv_dtor,
ERL_NIF_RT_CREATE, NULL);
fd_resource_type = enif_open_resource_type_x(env, "nif_SUITE.fd",
&fd_rt_init,
ERL_NIF_RT_CREATE, NULL);
monitor_resource_type = enif_open_resource_type_x(env, "nif_SUITE.monitor",
&monitor_rt_init,
ERL_NIF_RT_CREATE, NULL);
frenzy_resource_type = enif_open_resource_type_x(env, "nif_SUITE.monitor_frenzy",
&frenzy_rt_init,
ERL_NIF_RT_CREATE, NULL);
atom_false = enif_make_atom(env,"false");
atom_true = enif_make_atom(env,"true");
atom_self = enif_make_atom(env,"self");
atom_ok = enif_make_atom(env,"ok");
atom_join = enif_make_atom(env,"join");
atom_binary_resource_type = enif_make_atom(env,"binary_resource_type");
atom_second = enif_make_atom(env,"second");
atom_millisecond = enif_make_atom(env,"millisecond");
atom_microsecond = enif_make_atom(env,"microsecond");
atom_nanosecond = enif_make_atom(env,"nanosecond");
atom_eagain = enif_make_atom(env, "eagain");
atom_eof = enif_make_atom(env, "eof");
atom_error = enif_make_atom(env, "error");
atom_fd_resource_stop = enif_make_atom(env, "fd_resource_stop");
atom_monitor_resource_type = enif_make_atom(env, "monitor_resource_type");
atom_monitor_resource_down = enif_make_atom(env, "monitor_resource_down");
atom_init = enif_make_atom(env,"init");
atom_stats = enif_make_atom(env,"stats");
atom_done = enif_make_atom(env,"done");
atom_stop = enif_make_atom(env,"stop");
atom_null = enif_make_atom(env,"null");
*priv_data = data;
return 0;
}
static void resource_takeover(ErlNifEnv* env, PrivData* priv)
{
ErlNifResourceFlags tried;
ErlNifResourceType* rt;
rt = enif_open_resource_type(env, NULL, "Gold", resource_dtor,
ERL_NIF_RT_TAKEOVER, &tried);
assert(rt == priv->rt_arr[0].t);
assert(tried == ERL_NIF_RT_TAKEOVER);
rt = enif_open_resource_type(env, NULL, "Silver", resource_dtor,
ERL_NIF_RT_TAKEOVER, &tried);
assert(rt == priv->rt_arr[1].t);
assert(tried == ERL_NIF_RT_TAKEOVER);
rt = enif_open_resource_type(env, NULL, "nif_SUITE.binary", binary_resource_dtor,
ERL_NIF_RT_TAKEOVER, &tried);
assert(rt != NULL);
assert(tried == ERL_NIF_RT_TAKEOVER);
assert(binary_resource_type==NULL || binary_resource_type == rt);
binary_resource_type = rt;
rt = enif_open_resource_type(env, NULL, "nif_SUITE.msgenv", msgenv_dtor,
ERL_NIF_RT_TAKEOVER, &tried);
assert(rt != NULL);
assert(tried == ERL_NIF_RT_TAKEOVER);
assert(msgenv_resource_type==NULL || msgenv_resource_type == rt);
msgenv_resource_type = rt;
}
static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
{
PrivData* priv = (PrivData*) *old_priv_data;
add_call(env, priv, "upgrade");
priv->ref_cnt++;
*priv_data = *old_priv_data;
resource_takeover(env,priv);
return 0;
}
static void unload(ErlNifEnv* env, void* priv_data)
{
PrivData* data = priv_data;
add_call(env, data, "unload");
if (--data->ref_cnt == 0) {
if (data->nif_mod != NULL) {
NifModPrivData_release(data->nif_mod);
}
enif_free(priv_data);
}
DBG_TRACE_FINI;
}
static ERL_NIF_TERM lib_version(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ADD_CALL("lib_version");
return enif_make_int(env, NIF_SUITE_LIB_VER);
}
static ERL_NIF_TERM make_call_history(ErlNifEnv* env, CallInfo** headp)
{
ERL_NIF_TERM list = enif_make_list(env, 0); /* NIL */
while (*headp != NULL) {
CallInfo* call = *headp;
ERL_NIF_TERM func_term = enif_make_atom(env,call->func_name);
ERL_NIF_TERM tpl;
if (call->arg != NULL) {
ERL_NIF_TERM arg_bin;
memcpy(enif_make_new_binary(env, call->arg_sz, &arg_bin),
call->arg, call->arg_sz);
func_term = enif_make_tuple2(env, func_term, arg_bin);
}
tpl = enif_make_tuple4(env, func_term,
enif_make_int(env,call->lib_ver),
enif_make_int(env,call->static_cntA),
enif_make_int(env,call->static_cntB));
list = enif_make_list_cell(env, tpl, list);
*headp = call->next;
enif_free(call);
}
return list;
}
static ERL_NIF_TERM call_history(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
PrivData* data = (PrivData*) enif_priv_data(env);
return make_call_history(env,&data->call_history);
}
static ERL_NIF_TERM hold_nif_mod_priv_data(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
PrivData* data = (PrivData*) enif_priv_data(env);
void* ptr;
if (!get_pointer(env,argv[0],&ptr)) {
return enif_make_badarg(env);
}
if (data->nif_mod != NULL) {
NifModPrivData_release(data->nif_mod);
}
data->nif_mod = (NifModPrivData*) ptr;
return enif_make_int(env,++(data->nif_mod->ref_cnt));
}
static ERL_NIF_TERM nif_mod_call_history(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
PrivData* data = (PrivData*) enif_priv_data(env);
ERL_NIF_TERM ret;
if (data->nif_mod == NULL) {
return enif_make_string(env,"nif_mod pointer is NULL", ERL_NIF_LATIN1);
}
enif_mutex_lock(data->nif_mod->mtx);
ret = make_call_history(env, &data->nif_mod->call_history);
enif_mutex_unlock(data->nif_mod->mtx);
return ret;
}
static ERL_NIF_TERM list_seq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM list;
int n;
if (!enif_get_int(env, argv[0], &n)) {
return enif_make_badarg(env);
}
list = enif_make_list(env, 0); /* NIL */
while (n > 0) {
list = enif_make_list_cell(env, enif_make_int(env,n), list);
n--;
}
return list;
}
static int test_int(ErlNifEnv* env, int i1)
{
int i2 = 0;
ERL_NIF_TERM int_term = enif_make_int(env, i1);
if (!enif_get_int(env,int_term, &i2) || i1 != i2) {
fprintf(stderr, "test_int(%d) ...FAILED i2=%d\r\n", i1, i2);
return 0;
}
return 1;
}
static int test_uint(ErlNifEnv* env, unsigned i1)
{
unsigned i2 = 0;
ERL_NIF_TERM int_term = enif_make_uint(env, i1);
if (!enif_get_uint(env,int_term, &i2) || i1 != i2) {
fprintf(stderr, "test_uint(%u) ...FAILED i2=%u\r\n", i1, i2);
return 0;
}
return 1;
}
static int test_long(ErlNifEnv* env, long i1)
{
long i2 = 0;
ERL_NIF_TERM int_term = enif_make_long(env, i1);
if (!enif_get_long(env,int_term, &i2) || i1 != i2) {
fprintf(stderr, "test_long(%ld) ...FAILED i2=%ld\r\n", i1, i2);
return 0;
}
return 1;
}
static int test_ulong(ErlNifEnv* env, unsigned long i1)
{
unsigned long i2 = 0;
ERL_NIF_TERM int_term = enif_make_ulong(env, i1);
if (!enif_get_ulong(env,int_term, &i2) || i1 != i2) {
fprintf(stderr, "test_ulong(%lu) ...FAILED i2=%lu\r\n", i1, i2);
return 0;
}
return 1;
}
static int test_int64(ErlNifEnv* env, ErlNifSInt64 i1)
{
ErlNifSInt64 i2 = 0;
ERL_NIF_TERM int_term = enif_make_int64(env, i1);
if (!enif_get_int64(env,int_term, &i2) || i1 != i2) {
fprintf(stderr, "test_int64(%ld) ...FAILED i2=%ld\r\n",
(long)i1, (long)i2);
return 0;
}
return 1;
}
static int test_uint64(ErlNifEnv* env, ErlNifUInt64 i1)
{
ErlNifUInt64 i2 = 0;
ERL_NIF_TERM int_term = enif_make_uint64(env, i1);
if (!enif_get_uint64(env,int_term, &i2) || i1 != i2) {
fprintf(stderr, "test_ulong(%lu) ...FAILED i2=%lu\r\n",
(unsigned long)i1, (unsigned long)i2);
return 0;
}
return 1;
}
static int test_double(ErlNifEnv* env, double d1)
{
double d2 = 0;
ERL_NIF_TERM term = enif_make_double(env, d1);
if (!enif_get_double(env,term, &d2) || d1 != d2) {
fprintf(stderr, "test_double(%e) ...FAILED i2=%e\r\n", d1, d2);
return 0;
}
return 1;
}
#define TAG_BITS 4
#define SMALL_BITS (sizeof(void*)*8 - TAG_BITS)
#ifdef _WIN64
#define MAX_SMALL ((1LL << (SMALL_BITS-1))-1)
#define MIN_SMALL (-(1LL << (SMALL_BITS-1)))
#else
#define MAX_SMALL ((1L << (SMALL_BITS-1))-1)
#define MIN_SMALL (-(1L << (SMALL_BITS-1)))
#endif
static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int i;
int sint;
unsigned uint;
long slong;
unsigned long ulong;
ErlNifSInt64 sint64;
ErlNifUInt64 uint64;
double d;
ERL_NIF_TERM atom, ref1, ref2;
sint = INT_MIN;
do {
if (!test_int(env,sint)) {
goto error;
}
sint += ~sint / 3 + 1;
} while (sint < 0);
sint = INT_MAX;
do {
if (!test_int(env,sint)) {
goto error;
}
sint -= sint / 3 + 1;
} while (sint >= 0);
slong = LONG_MIN;
do {
if (!test_long(env,slong)) {
goto error;
}
slong += ~slong / 3 + 1;
} while (slong < 0);
slong = LONG_MAX;
do {
if (!test_long(env,slong)) {
goto error;
}
slong -= slong / 3 + 1;
} while (slong >= 0);
sint64 = ((ErlNifSInt64)1 << 63); /* INT64_MIN */
do {
if (!test_int64(env,sint64)) {
goto error;
}
sint64 += ~sint64 / 3 + 1;
} while (sint64 < 0);
sint64 = ((ErlNifUInt64)1 << 63) - 1; /* INT64_MAX */
do {
if (!test_int64(env,sint64)) {
goto error;
}
sint64 -= sint64 / 3 + 1;
} while (sint64 >= 0);
uint = UINT_MAX;
for (;;) {
if (!test_uint(env,uint)) {
goto error;
}
if (uint == 0) break;
uint -= uint / 3 + 1;
}
ulong = ULONG_MAX;
for (;;) {
if (!test_ulong(env,ulong)) {
goto error;
}
if (ulong == 0) break;
ulong -= ulong / 3 + 1;
}
uint64 = (ErlNifUInt64)-1; /* UINT64_MAX */
for (;;) {
if (!test_uint64(env,uint64)) {
goto error;
}
if (uint64 == 0) break;
uint64 -= uint64 / 3 + 1;
}
if (MAX_SMALL < INT_MAX) { /* 32-bit */
for (i=-10 ; i <= 10; i++) {
if (!test_int(env,MAX_SMALL+i)) {
goto error;
}
}
for (i=-10 ; i <= 10; i++) {
if (!test_int(env,MIN_SMALL+i)) {
goto error;
}
}
for (i=-10 ; i <= 10; i++) {
if (!test_uint(env,MAX_SMALL+i)) {
goto error;
}
}
}
assert((MAX_SMALL < INT_MAX) == (MIN_SMALL > INT_MIN));
for (i=-10 ; i < 10; i++) {
if (!test_long(env,MAX_SMALL+i) || !test_ulong(env,MAX_SMALL+i) ||
!test_long(env,MIN_SMALL+i) ||
!test_int64(env,MAX_SMALL+i) || !test_uint64(env,MAX_SMALL+i) ||
!test_int64(env,MIN_SMALL+i)) {
goto error;
}
if (MAX_SMALL < INT_MAX) {
if (!test_int(env,MAX_SMALL+i) || !test_uint(env,MAX_SMALL+i) ||
!test_int(env,MIN_SMALL+i)) {
goto error;
}
}
}
for (d=3.141592e-100 ; d < 1e100 ; d *= 9.97) {
if (!test_double(env,d) || !test_double(env,-d)) {
goto error;
}
}
if (!enif_make_existing_atom(env,"nif_SUITE", &atom, ERL_NIF_LATIN1)
|| !enif_is_identical(atom,enif_make_atom(env,"nif_SUITE"))) {
fprintf(stderr, "nif_SUITE not an atom?\r\n");
goto error;
}
for (i=2; i; i--) {
if (enif_make_existing_atom(env,"nif_SUITE_pink_unicorn", &atom, ERL_NIF_LATIN1)) {
fprintf(stderr, "pink unicorn exist?\r\n");
goto error;
}
}
ref1 = enif_make_ref(env);
ref2 = enif_make_ref(env);
if (!enif_is_ref(env,ref1) || !enif_is_ref(env,ref2)
|| enif_is_identical(ref1,ref2) || enif_compare(ref1,ref2)==0) {
fprintf(stderr, "strange refs?\r\n");
goto error;
}
return enif_make_atom(env,"ok");
error:
return enif_make_atom(env,"error");
}
static ERL_NIF_TERM echo_int(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int sint;
unsigned uint;
long slong;
unsigned long ulong;
ErlNifSInt64 sint64;
ErlNifUInt64 uint64;
ERL_NIF_TERM sint_term = atom_false, uint_term = atom_false;
ERL_NIF_TERM slong_term = atom_false, ulong_term = atom_false;
ERL_NIF_TERM sint64_term = atom_false, uint64_term = atom_false;
if (enif_get_int(env, argv[0], &sint)) {
sint_term = enif_make_int(env, sint);
}
if (enif_get_uint(env, argv[0], &uint)) {
uint_term = enif_make_uint(env, uint);
}
if (enif_get_long(env, argv[0], &slong)) {
slong_term = enif_make_long(env, slong);
}
if (enif_get_ulong(env, argv[0], &ulong)) {
ulong_term = enif_make_ulong(env, ulong);
}
if (enif_get_int64(env, argv[0], &sint64)) {
sint64_term = enif_make_int64(env, sint64);
}
if (enif_get_uint64(env, argv[0], &uint64)) {
uint64_term = enif_make_uint64(env, uint64);
}
return enif_make_list6(env, sint_term, uint_term, slong_term, ulong_term, sint64_term, uint64_term);
}
static ERL_NIF_TERM type_sizes(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return enif_make_tuple2(env, enif_make_int(env, sizeof(int)),
enif_make_int(env, sizeof(long)));
}
static ERL_NIF_TERM tuple_2_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int arity = -1;
const ERL_NIF_TERM* ptr;
ERL_NIF_TERM list = enif_make_list(env,0);
if (argc!=1 || !enif_get_tuple(env,argv[0],&arity,&ptr)) {
return enif_make_badarg(env);
}
while (--arity >= 0) {
list = enif_make_list_cell(env,ptr[arity],list);
}
return list;
}
static ERL_NIF_TERM is_identical(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
if (argc != 2) {
return enif_make_badarg(env);
}
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);
}
return enif_make_int(env, enif_compare(argv[0],argv[1]));
}
static ERL_NIF_TERM phash2_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
if (argc != 1) {
return enif_make_badarg(env);
}
return enif_make_ulong(env, enif_phash2(argv[0]));
}
static ERL_NIF_TERM phash2_ranged_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
unsigned long range;
if (argc != 2 || !enif_get_ulong(env, argv[1], &range)) {
return enif_make_badarg(env);
}
return enif_make_ulong(env, enif_phash2_ranged(argv[0], range));
}
static ERL_NIF_TERM many_args_100(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int i, k;
if (argc == 100) {
for (i=1; i<=100; i++) {
if (!enif_get_int(env,argv[i-1],&k) || k!=i) {
goto badarg;
}
}
return enif_make_atom(env,"ok");
}
badarg:
return enif_make_badarg(env);
}
static ERL_NIF_TERM clone_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary ibin;
if (enif_inspect_binary(env,argv[0],&ibin)) {
ERL_NIF_TERM obin;
memcpy(enif_make_new_binary(env, ibin.size, &obin),
ibin.data, ibin.size);
return obin;
}
else {
return enif_make_badarg(env);
}
}
static ERL_NIF_TERM make_sub_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int pos, size;
if (!enif_get_int(env,argv[1],&pos) || !enif_get_int(env,argv[2],&size)) {
return enif_make_badarg(env);
}
return enif_make_sub_binary(env,argv[0],pos,size);
}
static ERL_NIF_TERM string_to_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary obin;
unsigned size;
int n;
if (!enif_get_int(env,argv[1],(int*)&size)
|| !enif_alloc_binary(size,&obin)) {
return enif_make_badarg(env);
}
n = enif_get_string(env, argv[0], (char*)obin.data, size, ERL_NIF_LATIN1);
return enif_make_tuple(env, 2, enif_make_int(env,n),
enif_make_binary(env,&obin));
}
static ERL_NIF_TERM atom_to_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary obin;
unsigned size;
int n;
if (!enif_get_int(env,argv[1],(int*)&size)
|| !enif_alloc_binary(size,&obin)) {
return enif_make_badarg(env);
}
n = enif_get_atom(env, argv[0], (char*)obin.data, size, ERL_NIF_LATIN1);
return enif_make_tuple(env, 2, enif_make_int(env,n),
enif_make_binary(env,&obin));
}
static ERL_NIF_TERM macros(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
const ERL_NIF_TERM* a;
ERL_NIF_TERM lists, tuples;
int arity;
if (!enif_get_tuple(env, argv[0], &arity, &a) || arity != 9) {
return enif_make_badarg(env);
}
lists = enif_make_list(env,9,
enif_make_list1(env,a[0]),
enif_make_list2(env,a[0],a[1]),
enif_make_list3(env,a[0],a[1],a[2]),
enif_make_list4(env,a[0],a[1],a[2],a[3]),
enif_make_list5(env,a[0],a[1],a[2],a[3],a[4]),
enif_make_list6(env,a[0],a[1],a[2],a[3],a[4],a[5]),
enif_make_list7(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6]),
enif_make_list8(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),
enif_make_list9(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]));
tuples = enif_make_list(env,9,
enif_make_tuple1(env,a[0]),
enif_make_tuple2(env,a[0],a[1]),
enif_make_tuple3(env,a[0],a[1],a[2]),
enif_make_tuple4(env,a[0],a[1],a[2],a[3]),
enif_make_tuple5(env,a[0],a[1],a[2],a[3],a[4]),
enif_make_tuple6(env,a[0],a[1],a[2],a[3],a[4],a[5]),
enif_make_tuple7(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6]),
enif_make_tuple8(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),
enif_make_tuple9(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]));
return enif_make_tuple2(env,lists,tuples);
}
static ERL_NIF_TERM tuple_2_list_and_tuple(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
const ERL_NIF_TERM* arr;
int arity;
if (!enif_get_tuple(env,argv[0],&arity,&arr)) {
return enif_make_badarg(env);
}
return enif_make_tuple2(env,
enif_make_list_from_array(env, arr, arity),
enif_make_tuple_from_array(env, arr, arity));
}
static ERL_NIF_TERM iolist_2_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary obin;
if (!enif_inspect_iolist_as_binary(env, argv[0], &obin)) {
return enif_make_badarg(env);
}
return enif_make_binary(env,&obin);
}
static ERL_NIF_TERM last_resource_dtor_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM ret;
if (resource_dtor_last != NULL) {
ERL_NIF_TERM bin;
memcpy(enif_make_new_binary(env, resource_dtor_last_sz, &bin),
resource_dtor_last_data, resource_dtor_last_sz);
ret = enif_make_tuple3(env,
make_pointer(env, resource_dtor_last),
bin,
enif_make_int(env, resource_dtor_cnt));
}
else {
ret = enif_make_list(env,0);
}
resource_dtor_last = NULL;
resource_dtor_last_sz = 0;
resource_dtor_cnt = 0;
return ret;
}
static ERL_NIF_TERM get_resource_type(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
PrivData* data = (PrivData*) enif_priv_data(env);
int ix;
if (!enif_get_int(env, argv[0], &ix) || ix >= 2) {
return enif_make_badarg(env);
}
return make_pointer(env, data->rt_arr[ix].vp);
}
static ERL_NIF_TERM alloc_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary data_bin;
union { ErlNifResourceType* t; void* vp; } type;
void* data;
if (!get_pointer(env, argv[0], &type.vp)
|| !enif_inspect_binary(env, argv[1], &data_bin)
|| (data = enif_alloc_resource(type.t, data_bin.size))==NULL) {
return enif_make_badarg(env);
}
memcpy(data, data_bin.data, data_bin.size);
return make_pointer(env, data);
}
static ERL_NIF_TERM make_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
void* data;
if (!get_pointer(env, argv[0], &data)) {
return enif_make_badarg(env);
}
return enif_make_resource(env, data);
}
static ERL_NIF_TERM make_new_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary data_bin;
union { ErlNifResourceType* t; void* vp; } type;
void* data;
ERL_NIF_TERM ret;
if (!get_pointer(env, argv[0], &type.vp)
|| !enif_inspect_binary(env, argv[1], &data_bin)
|| (data = enif_alloc_resource(type.t, data_bin.size))==NULL) {
return enif_make_badarg(env);
}
ret = enif_make_resource(env, data);
memcpy(data, data_bin.data, data_bin.size);
enif_release_resource(data);
return ret;
}
static ERL_NIF_TERM make_new_resource_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary data_bin;
union { struct binary_resource* p; void* vp; } br;
void* buf;
ERL_NIF_TERM ret;
if (!enif_inspect_binary(env, argv[0], &data_bin)
|| (br.vp = enif_alloc_resource(binary_resource_type,
sizeof(struct binary_resource)))==NULL
|| (buf = enif_alloc(data_bin.size)) == NULL) {
return enif_make_badarg(env);
}
memset(br.vp,0xba,sizeof(struct binary_resource)); /* avoid valgrind warning */
br.p->data = buf;
br.p->size = data_bin.size;
memcpy(br.p->data, data_bin.data, data_bin.size);
ret = enif_make_resource_binary(env, br.vp, br.p->data, br.p->size);
enif_release_resource(br.p);
return enif_make_tuple2(env, make_pointer(env,br.vp), ret);
}
static void binary_resource_dtor(ErlNifEnv* env, void* obj)
{
struct binary_resource* br = (struct binary_resource*) obj;
resource_dtor(env,obj);
assert(br->data != NULL);
enif_free(br->data);
br->data = NULL;
}
static ERL_NIF_TERM get_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary data_bin;
union { ErlNifResourceType* t; void* vp; } type;
void* data;
type.t = NULL;
if (enif_is_identical(argv[0], atom_binary_resource_type)) {
type.t = binary_resource_type;
}
else if (enif_is_identical(argv[0], atom_monitor_resource_type)) {
type.t = monitor_resource_type;
}
else {
get_pointer(env, argv[0], &type.vp);
}
if (type.t == NULL
|| !enif_get_resource(env, argv[1], type.t, &data)) {
return enif_make_badarg(env);
}
enif_alloc_binary(enif_sizeof_resource(data), &data_bin);
memcpy(data_bin.data, data, data_bin.size);
return enif_make_tuple2(env, make_pointer(env,data),
enif_make_binary(env, &data_bin));
}
static ERL_NIF_TERM release_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
void* data;
if (!get_pointer(env, argv[0], &data)) {
return enif_make_badarg(env);
}
enif_release_resource(data);
return enif_make_atom(env,"ok");
}
/*
* argv[0] an atom
* argv[1] a binary
* argv[2] a ref
* argv[3] 'ok'
* argv[4] a fun
* argv[5] a pid
* argv[6] a port
* argv[7] an empty list
* argv[8] a non-empty list
* argv[9] a tuple
* argv[10] a number (small, big integer or float)
*/
static ERL_NIF_TERM check_is(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM ok_atom = enif_make_atom(env, "ok");
if (!enif_is_atom(env, argv[0])) return enif_make_badarg(env);
if (!enif_is_binary(env, argv[1])) return enif_make_badarg(env);
if (!enif_is_ref(env, argv[2])) return enif_make_badarg(env);
if (!enif_is_identical(argv[3], ok_atom)) return enif_make_badarg(env);
if (!enif_is_fun(env, argv[4])) return enif_make_badarg(env);
if (!enif_is_pid(env, argv[5])) return enif_make_badarg(env);
if (!enif_is_port(env, argv[6])) return enif_make_badarg(env);
if (!enif_is_empty_list(env, argv[7])) return enif_make_badarg(env);
if (!enif_is_list(env, argv[7])) return enif_make_badarg(env);
if (!enif_is_list(env, argv[8])) return enif_make_badarg(env);
if (!enif_is_tuple(env, argv[9])) return enif_make_badarg(env);
if (!enif_is_number(env, argv[10])) return enif_make_badarg(env);
return ok_atom;
}
/*
* no arguments
*
* 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.
*/
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_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;
}
/*
* argv[0] atom with length of 6
* argv[1] list with length of 6
* argv[2] empty list
* argv[3] not an atom
* argv[4] not a list
* argv[5] improper list
*/
static ERL_NIF_TERM length_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
unsigned len;
if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1) || len != 6)
return enif_make_badarg(env);
if (!enif_get_list_length(env, argv[1], &len) || len != 6)
return enif_make_badarg(env);
if (!enif_get_list_length(env, argv[2], &len) || len != 0)
return enif_make_badarg(env);
if (enif_get_atom_length(env, argv[3], &len, ERL_NIF_LATIN1))
return enif_make_badarg(env);
if (enif_get_list_length(env, argv[4], &len))
return enif_make_badarg(env);
if (enif_get_list_length(env, argv[5], &len))
return enif_make_badarg(env);
return enif_make_atom(env, "ok");
}
static ERL_NIF_TERM make_atoms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM arr[7];
ERL_NIF_TERM existingatom0a, existingatom0b;
ERL_NIF_TERM existing0atom0;
const char * const an0atom = "an0atom";
const char an0atom0[8] = {'a','n','\0','a','t','o','m',0};
arr[0] = enif_make_atom(env, "an0atom");
arr[1] = enif_make_atom_len(env, "an0atom", 7);
arr[2] = enif_make_atom_len(env, an0atom, 7);
arr[3] = enif_make_atom_len(env, an0atom0, 8);
if (!enif_make_existing_atom(env, "an0atom", &existingatom0a, ERL_NIF_LATIN1))
return enif_make_atom(env, "error");
arr[4] = existingatom0a;
if (!enif_make_existing_atom_len(env, an0atom, 7, &existingatom0b, ERL_NIF_LATIN1))
return enif_make_atom(env, "error");
arr[5] = existingatom0b;
if (!enif_make_existing_atom_len(env, an0atom0, 8, &existing0atom0, ERL_NIF_LATIN1))
return enif_make_atom(env, "error");
arr[6] = existing0atom0;
return enif_make_tuple7(env,
arr[0],arr[1],arr[2],arr[3],arr[4],arr[5],arr[6]);
}
static ERL_NIF_TERM make_strings(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
const char a0string[8] = {'a','0','s','t','r','i','n','g'};
const char a0string0[9] = {'a','\0','s','t','r','i','n','g',0};
const char astringwith8bits[37] = {'E','r','l','a','n','g',' ',0xE4 /* 'ä' */,'r',' ','e','t','t',' ','g','e','n','e','r','e','l','l','t',' ','p','r','o','g','r','a','m','s','p','r', 0xE5 /* 'å' */,'k',0};
return enif_make_tuple5(env,
enif_make_string(env, "a0string", ERL_NIF_LATIN1),
enif_make_string_len(env, "a0string", 8, ERL_NIF_LATIN1),
enif_make_string_len(env, a0string, 8, ERL_NIF_LATIN1),
enif_make_string_len(env, a0string0, 9, ERL_NIF_LATIN1),
enif_make_string(env, astringwith8bits, ERL_NIF_LATIN1));
}
static ERL_NIF_TERM send_list_seq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifPid to;
ERL_NIF_TERM msg;
ErlNifEnv* msg_env;
int i, res;
if (!enif_get_int(env, argv[0], &i)) {
return enif_make_badarg(env);
}
if (argv[1] == atom_self) {
enif_self(env, &to);
}
else if (!enif_get_local_pid(env, argv[1], &to)) {
return enif_make_badarg(env);
}
msg_env = enif_alloc_env();
msg = enif_make_list(msg_env,0);
for ( ; i>0 ; i--) {
msg = enif_make_list_cell(msg_env, enif_make_int(msg_env, i), msg);
}
res = enif_send(env, &to, msg_env, msg);
enif_free_env(msg_env);
return enif_make_tuple2(env, atom_ok, enif_make_int(env,res));
}
static void fill(void* dst, unsigned bytes, int seed)
{
unsigned char* ptr = dst;
int i;
for (i=bytes; i>0; i--) {
*ptr++ = seed;
seed += 7;
}
}
#define MAKE_TERM_REUSE_LEN 16
struct make_term_info
{
ErlNifEnv* caller_env;
ErlNifEnv* dst_env;
int dst_env_valid;
ERL_NIF_TERM reuse[MAKE_TERM_REUSE_LEN];
unsigned reuse_push;
unsigned reuse_pull;
ErlNifResourceType* resource_type;
void *resource;
ERL_NIF_TERM other_term;
ERL_NIF_TERM blob;
ErlNifPid to_pid;
ErlNifTid tid;
ErlNifCond* cond;
ErlNifMutex* mtx;
int send_it;
int send_res;
unsigned n;
};
static void push_term(struct make_term_info* mti, ERL_NIF_TERM term)
{
unsigned ix = (mti->reuse_push++) % MAKE_TERM_REUSE_LEN;
mti->reuse[ix] = term;
//enif_fprintf(stderr, "push at %u: %T\r\n", ix, term);
}
static ERL_NIF_TERM pull_term(struct make_term_info* mti)
{
unsigned ix;
if (mti->reuse_pull >= mti->reuse_push &&
mti->reuse_push < MAKE_TERM_REUSE_LEN) {
mti->reuse_pull = 0;
if (mti->reuse_push == 0) {
assert(mti->dst_env_valid);
mti->reuse[0] = enif_make_list(mti->dst_env, 0);
}
}
ix = (mti->reuse_pull++) % MAKE_TERM_REUSE_LEN;
//enif_fprintf(stderr, "pull from %u: %T\r\n", ix, mti->reuse[ix]);
return mti->reuse[ix];
}
static int make_term_n(struct make_term_info* mti, int n, ERL_NIF_TERM* res);
static ERL_NIF_TERM make_term_binary(struct make_term_info* mti, int n)
{
ErlNifBinary bin;
enif_alloc_binary(100, &bin);
fill(bin.data, bin.size, n);
return enif_make_binary(mti->dst_env, &bin);
}
static ERL_NIF_TERM make_term_int(struct make_term_info* mti, int n)
{
int i;
fill(&i, sizeof(i), n);
return enif_make_int(mti->dst_env, i);
}
static ERL_NIF_TERM make_term_ulong(struct make_term_info* mti, int n)
{
unsigned long ul;
fill(&ul, sizeof(ul), n);
return enif_make_ulong(mti->dst_env, ul);
}
static ERL_NIF_TERM make_term_double(struct make_term_info* mti, int n)
{
double d = 3.141592;
return enif_make_double(mti->dst_env, d);
}
static ERL_NIF_TERM make_term_atom(struct make_term_info* mti, int n)
{
return enif_make_atom(mti->dst_env, "make_term_n");
}
static ERL_NIF_TERM make_term_existing_atom(struct make_term_info* mti, int n)
{
ERL_NIF_TERM res;
int exist = enif_make_existing_atom(mti->dst_env, "nif_SUITE", &res,
ERL_NIF_LATIN1);
assert(exist);
return res;
}
static ERL_NIF_TERM make_term_string(struct make_term_info* mti, int n)
{
return enif_make_string(mti->dst_env, "Hello!", ERL_NIF_LATIN1);
}
static ERL_NIF_TERM make_term_sub_binary(struct make_term_info* mti, int n)
{
ERL_NIF_TERM orig;
unsigned char* ptr = enif_make_new_binary(mti->dst_env, 10, &orig);
fill(ptr, 10, n);
return enif_make_sub_binary(mti->dst_env, orig, 3, 5);
}
static ERL_NIF_TERM make_term_uint(struct make_term_info* mti, int n)
{
unsigned int ui;
fill(&ui, sizeof(ui), n);
return enif_make_uint(mti->dst_env, ui);
}
static ERL_NIF_TERM make_term_long(struct make_term_info* mti, int n)
{
long l;
fill(&l, sizeof(l), n);
return enif_make_long(mti->dst_env, l);
}
static ERL_NIF_TERM make_term_tuple0(struct make_term_info* mti, int n)
{
return enif_make_tuple(mti->dst_env, 0);
}
static ERL_NIF_TERM make_term_list0(struct make_term_info* mti, int n)
{
return enif_make_list(mti->dst_env, 0);
}
static ERL_NIF_TERM make_term_resource(struct make_term_info* mti, int n)
{
return enif_make_resource(mti->dst_env, mti->resource);
}
static ERL_NIF_TERM make_term_new_binary(struct make_term_info* mti, int n)
{
ERL_NIF_TERM res;
unsigned char* ptr = enif_make_new_binary(mti->dst_env,20,&res);
fill(ptr, 20, n);
return res;
}
static ERL_NIF_TERM make_term_caller_pid(struct make_term_info* mti, int n)
{
ErlNifPid pid;
return enif_make_pid(mti->dst_env, enif_self(mti->caller_env, &pid));
}
static ERL_NIF_TERM make_term_tuple(struct make_term_info* mti, int n)
{
ERL_NIF_TERM t[3];
t[0] = pull_term(mti);
t[1] = pull_term(mti);
t[2] = pull_term(mti);
return enif_make_tuple3(mti->dst_env, t[0], t[1], t[2]);
}
static ERL_NIF_TERM make_term_list(struct make_term_info* mti, int n)
{
ERL_NIF_TERM t[3];
t[0] = pull_term(mti);
t[1] = pull_term(mti);
t[2] = pull_term(mti);
return enif_make_list3(mti->dst_env, t[0], t[1], t[2]);
}
static ERL_NIF_TERM make_term_list_cell(struct make_term_info* mti, int n)
{
ERL_NIF_TERM t[2];
t[0] = pull_term(mti);
t[1] = pull_term(mti);
return enif_make_list_cell(mti->dst_env, t[0], t[1]);
}
static ERL_NIF_TERM make_term_tuple_from_array(struct make_term_info* mti, int n)
{
ERL_NIF_TERM t[3];
t[0] = pull_term(mti);
t[1] = pull_term(mti);
t[2] = pull_term(mti);
return enif_make_tuple_from_array(mti->dst_env, t, 3);
}
static ERL_NIF_TERM make_term_list_from_array(struct make_term_info* mti, int n)
{
ERL_NIF_TERM t[3];
t[0] = pull_term(mti);
t[1] = pull_term(mti);
t[2] = pull_term(mti);
return enif_make_list_from_array(mti->dst_env, t, 3);
}
static ERL_NIF_TERM make_term_garbage(struct make_term_info* mti, int n)
{
(void) enif_make_string(mti->dst_env, "garbage string", ERL_NIF_LATIN1);
return pull_term(mti);
}
static ERL_NIF_TERM make_term_copy(struct make_term_info* mti, int n)
{
return enif_make_copy(mti->dst_env, mti->other_term);
}
typedef ERL_NIF_TERM Make_term_Func(struct make_term_info*, int);
static Make_term_Func* make_funcs[] = {
make_term_binary,
make_term_int,
make_term_ulong,
make_term_double,
make_term_atom,
make_term_existing_atom,
make_term_string,
make_term_sub_binary,
make_term_uint,
make_term_long,
make_term_tuple0,
make_term_list0,
make_term_resource,
make_term_new_binary,
make_term_caller_pid,
make_term_tuple,
make_term_list,
make_term_list_cell,
make_term_tuple_from_array,
make_term_list_from_array,
make_term_garbage,
make_term_copy
};
static unsigned num_of_make_funcs()
{
return sizeof(make_funcs)/sizeof(*make_funcs);
}
static int make_term_n(struct make_term_info* mti, int n, ERL_NIF_TERM* res)
{
if (n < num_of_make_funcs()) {
assert(mti->dst_env_valid);
*res = make_funcs[n](mti, n);
push_term(mti, *res);
return 1;
}
return 0;
}
static void
init_make_blob(struct make_term_info *mti,
ErlNifEnv* caller_env,
ERL_NIF_TERM other_term)
{
PrivData* priv = (PrivData*) enif_priv_data(caller_env);
mti->caller_env = caller_env;
mti->resource_type = priv->rt_arr[0].t;
mti->resource = enif_alloc_resource(mti->resource_type, 10);
fill(mti->resource, 10, 17);
mti->other_term = other_term;
}
static void
fini_make_blob(struct make_term_info *mti)
{
enif_release_resource(mti->resource);
}
static ERL_NIF_TERM make_blob(struct make_term_info *mti,
ErlNifEnv* dst_env)
{
ERL_NIF_TERM term, list;
int n = 0;
mti->reuse_push = 0;
mti->reuse_pull = 0;
mti->dst_env = dst_env;
mti->dst_env_valid = 1;
list = enif_make_list(dst_env, 0);
while (make_term_n(mti, n++, &term)) {
list = enif_make_list_cell(dst_env, term, list);
}
return list;
}
static ERL_NIF_TERM send_new_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifPid to;
ERL_NIF_TERM msg, copy;
ErlNifEnv* msg_env;
int res;
struct make_term_info mti;
if (!enif_get_local_pid(env, argv[0], &to)) {
return enif_make_badarg(env);
}
msg_env = enif_alloc_env();
init_make_blob(&mti, env, argv[1]);
msg = make_blob(&mti,msg_env);
copy = make_blob(&mti,env);
fini_make_blob(&mti);
res = enif_send(env, &to, msg_env, msg);
enif_free_env(msg_env);
return enif_make_tuple3(env, atom_ok, enif_make_int(env,res), copy);
}
static ERL_NIF_TERM alloc_msgenv(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
PrivData* priv = (PrivData*) enif_priv_data(env);
struct make_term_info* mti;
ERL_NIF_TERM ret;
mti = (struct make_term_info*) enif_alloc_resource(msgenv_resource_type,
sizeof(*mti));
mti->caller_env = NULL;
mti->dst_env = enif_alloc_env();
mti->dst_env_valid = 1;
mti->reuse_push = 0;
mti->reuse_pull = 0;
mti->resource_type = priv->rt_arr[0].t;
mti->resource = enif_alloc_resource(mti->resource_type, 10);
fill(mti->resource, 10, 17);
mti->other_term = enif_make_list(mti->dst_env, 0);
mti->blob = enif_make_list(mti->dst_env, 0);
mti->mtx = enif_mutex_create("nif_SUITE:mtx");
mti->cond = enif_cond_create("nif_SUITE:cond");
mti->send_res = 0xcafebabe;
mti->n = 0;
ret = enif_make_resource(env, mti);
enif_release_resource(mti);
return ret;
}
static void msgenv_dtor(ErlNifEnv* env, void* obj)
{
struct make_term_info* mti = (struct make_term_info*) obj;
if (mti->dst_env != NULL) {
enif_free_env(mti->dst_env);
}
enif_release_resource(mti->resource);
enif_mutex_destroy(mti->mtx);
enif_cond_destroy(mti->cond);
}
static ERL_NIF_TERM clear_msgenv(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
mti_t mti;
if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)) {
return enif_make_badarg(env);
}
enif_clear_env(mti.p->dst_env);
mti.p->dst_env_valid = 1;
mti.p->reuse_pull = 0;
mti.p->reuse_push = 0;
mti.p->blob = enif_make_list(mti.p->dst_env, 0);
return atom_ok;
}
static ERL_NIF_TERM grow_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
mti_t mti;
ERL_NIF_TERM term;
if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
|| (argc>2 && !enif_get_uint(env,argv[2], &mti.p->n))) {
return enif_make_badarg(env);
}
mti.p->caller_env = env;
mti.p->other_term = argv[1];
mti.p->n %= num_of_make_funcs();
make_term_n(mti.p, mti.p->n++, &term);
mti.p->blob = enif_make_list_cell(mti.p->dst_env, term, mti.p->blob);
return atom_ok;
}
static ERL_NIF_TERM send_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
mti_t mti;
ErlNifPid to;
ERL_NIF_TERM copy;
int res;
if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
|| !enif_get_local_pid(env, argv[1], &to)) {
return enif_make_badarg(env);
}
copy = enif_make_copy(env, mti.p->blob);
res = enif_send(env, &to, mti.p->dst_env, mti.p->blob);
if (res)
mti.p->dst_env_valid = 0;
return enif_make_tuple3(env, atom_ok, enif_make_int(env,res), copy);
}
static ERL_NIF_TERM send3_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
mti_t mti;
ErlNifPid to;
int res;
if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
|| !enif_get_local_pid(env, argv[1], &to)) {
return enif_make_badarg(env);
}
mti.p->blob = enif_make_tuple2(mti.p->dst_env,
enif_make_copy(mti.p->dst_env, argv[2]),
mti.p->blob);
res = enif_send(env, &to, mti.p->dst_env, mti.p->blob);
if (res)
mti.p->dst_env_valid = 0;
return enif_make_int(env,res);
}
void* threaded_sender(void *arg)
{
mti_t mti;
mti.vp = arg;
enif_mutex_lock(mti.p->mtx);
while (!mti.p->send_it) {
enif_cond_wait(mti.p->cond, mti.p->mtx);
}
mti.p->send_it = 0;
enif_mutex_unlock(mti.p->mtx);
mti.p->send_res = enif_send(NULL, &mti.p->to_pid, mti.p->dst_env, mti.p->blob);
if (mti.p->send_res)
mti.p->dst_env_valid = 0;
return NULL;
}
static ERL_NIF_TERM send_blob_thread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
mti_t mti;
ERL_NIF_TERM copy;
if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
|| !enif_get_local_pid(env,argv[1], &mti.p->to_pid)) {
return enif_make_badarg(env);
}
copy = enif_make_copy(env, mti.p->blob);
mti.p->send_it = enif_is_identical(argv[2],atom_join);
if (enif_thread_create("nif_SUITE:send_from_thread", &mti.p->tid,
threaded_sender, mti.p, NULL) != 0) {
return enif_make_badarg(env);
}
if (enif_is_identical(argv[2],atom_join)) {
int err = enif_thread_join(mti.p->tid, NULL);
assert(err == 0);
return enif_make_tuple3(env, atom_ok, enif_make_int(env, mti.p->send_res), copy);
}
else {
enif_keep_resource(mti.vp);
return enif_make_tuple2(env, atom_ok, copy);
}
}
static ERL_NIF_TERM join_send_thread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
mti_t mti;
int err;
if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)) {
return enif_make_badarg(env);
}
enif_mutex_lock(mti.p->mtx);
mti.p->send_it = 1;
enif_cond_signal(mti.p->cond);
enif_mutex_unlock(mti.p->mtx);
err = enif_thread_join(mti.p->tid, NULL);
assert(err == 0);
enif_release_resource(mti.vp);
return enif_make_tuple2(env, atom_ok, enif_make_int(env, mti.p->send_res));
}
static ERL_NIF_TERM copy_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
mti_t mti;
if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)) {
return enif_make_badarg(env);
}
return enif_make_copy(env, mti.p->blob);
}
static ERL_NIF_TERM send_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifEnv* menv;
ErlNifPid pid;
int ret;
if (!enif_get_local_pid(env, argv[0], &pid)) {
return enif_make_badarg(env);
}
menv = enif_alloc_env();
ret = enif_send(env, &pid, menv, enif_make_copy(menv, argv[1]));
enif_free_env(menv);
return enif_make_int(env, ret);
}
static ERL_NIF_TERM send_copy_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifPid pid;
int ret;
if (!enif_get_local_pid(env, argv[0], &pid)) {
return enif_make_badarg(env);
}
ret = enif_send(env, &pid, NULL, argv[1]);
return enif_make_int(env, ret);
}
static ERL_NIF_TERM reverse_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
ERL_NIF_TERM rev_list;
if(!enif_make_reverse_list(env, argv[0], &rev_list))
return enif_make_atom(env, "badarg");
return rev_list;
}
static ERL_NIF_TERM otp_9668_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
/* Inspect in process independent env */
ErlNifEnv* myenv = enif_alloc_env();
ERL_NIF_TERM mycopy = enif_make_copy(myenv, argv[0]);
ErlNifBinary obin, cbin;
if ((enif_inspect_binary(env, argv[0], &obin)
&& enif_inspect_binary(myenv, mycopy, &cbin))
||
(enif_inspect_iolist_as_binary(env, argv[0], &obin)
&& enif_inspect_iolist_as_binary(myenv, mycopy, &cbin)))
{
assert(obin.size == cbin.size);
assert(memcmp(obin.data, cbin.data, obin.size) == 0);
}
enif_free_env(myenv);
return atom_ok;
}
static ERL_NIF_TERM otp_9828_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
/* copy a writable binary could reallocate it due to "emasculation"
and thereby render a previous inspection invalid.
*/
ErlNifBinary bin1;
ErlNifEnv* myenv;
if (!enif_inspect_binary(env, argv[0], &bin1)) {
return enif_make_badarg(env);
}
myenv = enif_alloc_env();
enif_make_copy(myenv, argv[0]);
enif_free_env(myenv);
return memcmp(bin1.data, "I'm alive!", 10)==0 ? atom_ok : atom_false;
}
static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int percent;
char atom[10];
if (!enif_get_int(env, argv[0], &percent) ||
!enif_get_atom(env, argv[1], atom, sizeof(atom), ERL_NIF_LATIN1)) {
return enif_make_badarg(env);
}
if (strcmp(atom , "true") == 0) {
int cnt = 1;
while (enif_consume_timeslice(env, percent) == 0 && cnt < 200)
cnt++;
return enif_make_int(env, cnt);
}
else {
return enif_make_int(env, enif_consume_timeslice(env, percent));
}
}
static ERL_NIF_TERM nif_sched2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
char s[64];
if (!enif_get_string(env, argv[2], s, sizeof s, ERL_NIF_LATIN1))
return enif_make_badarg(env);
return enif_make_tuple2(env, argv[3], argv[2]);
}
static ERL_NIF_TERM nif_sched1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM new_argv[4];
new_argv[0] = enif_make_atom(env, "garbage0");
new_argv[1] = enif_make_atom(env, "garbage1");
new_argv[2] = argv[0];
new_argv[3] = argv[1];
return enif_schedule_nif(env, "nif_sched2", 0, nif_sched2, 4, new_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");
result = enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv);
assert(!enif_is_exception(env, result));
return result;
}
/*
* If argv[0] is the integer 0, call enif_make_badarg, but don't return its
* return value. Instead, return ok. Result should still be a badarg
* exception for the erlang caller.
*
* For any other value of argv[0], use it as an exception term and return
* the exception.
*/
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");
int arg;
if (enif_get_int(env, argv[0], &arg) && arg == 0) {
/* 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");
} else {
ERL_NIF_TERM exc_retval = enif_raise_exception(env, argv[0]);
assert(enif_has_pending_exception(env, NULL));
assert(enif_has_pending_exception(env, &exc_term));
assert(enif_is_identical(argv[0], exc_term));
return exc_retval;
}
}
#if !defined(NAN) || !defined(INFINITY)
double zero(void)
{
return 0.0;
}
#endif
static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
double val;
char arg[6];
ERL_NIF_TERM res;
assert(argc == 1);
enif_get_atom(env, argv[0], arg, sizeof arg, ERL_NIF_LATIN1);
if (strcmp(arg, "nan") == 0) {
/* Verify that enif_make_double raises a badarg for NaN */
#ifdef NAN
val = NAN;
#else
val = 0.0/zero();
#endif
} else {
/* Verify that enif_make_double raises a badarg for NaN and infinity */
#ifdef INFINITY
val = INFINITY;
#else
val = 1.0/zero();
#endif
}
res = enif_make_double(env, val);
assert(enif_is_exception(env, res));
assert(enif_has_pending_exception(env, NULL));
if (strcmp(arg, "tuple") == 0) {
return enif_make_tuple2(env, argv[0], res);
} else {
return res;
}
}
static ERL_NIF_TERM call_nif_atom_too_long(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
char str[257];
char arg[4];
size_t len;
int i;
ERL_NIF_TERM res;
assert(argc == 1);
enif_get_atom(env, argv[0], arg, sizeof arg, ERL_NIF_LATIN1);
/* Verify that creating an atom from a string that's too long results in a badarg */
for (i = 0; i < sizeof str; ++i) {
str[i] = 'a';
}
str[256] = '\0';
if (strcmp(arg, "len") == 0) {
len = strlen(str);
res = enif_make_atom_len(env, str, len);
} else {
res = enif_make_atom(env, str);
}
assert(enif_is_exception(env, res));
return res;
}
static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return enif_make_int(env, enif_is_map(env,argv[0]));
}
static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
size_t size = (size_t)-123;
int ret = enif_get_map_size(env, argv[0], &size);
return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, (int)size));
}
static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return enif_make_new_map(env);
}
static ERL_NIF_TERM make_map_put_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
int ret = enif_make_map_put(env, argv[0], argv[1], argv[2], &map_out);
return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
}
static ERL_NIF_TERM get_map_value_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM value = enif_make_atom(env, "undefined");
int ret = enif_get_map_value(env, argv[0], argv[1], &value);
return enif_make_tuple2(env, enif_make_int(env,ret), value);
}
static ERL_NIF_TERM make_map_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
int ret = enif_make_map_update(env, argv[0], argv[1], argv[2], &map_out);
return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
}
static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
int ret = enif_make_map_remove(env, argv[0], argv[1], &map_out);
return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
}
/* maps */
static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM cell = argv[0];
ERL_NIF_TERM map = enif_make_new_map(env);
ERL_NIF_TERM tuple;
const ERL_NIF_TERM *pair;
int arity = -1;
if (argc != 1 && !enif_is_list(env, cell)) return enif_make_badarg(env);
/* assume sorted keys */
while (!enif_is_empty_list(env,cell)) {
if (!enif_get_list_cell(env, cell, &tuple, &cell)) return enif_make_badarg(env);
if (enif_get_tuple(env,tuple,&arity,&pair)) {
enif_make_map_put(env, map, pair[0], pair[1], &map);
}
}
return map;
}
static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
ERL_NIF_TERM map = argv[0];
ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */
ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */
ERL_NIF_TERM key, value, k2, v2;
ErlNifMapIterator iter_f;
ErlNifMapIterator iter_b;
int cnt, next_ret, prev_ret;
if (argc != 1 && !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))
return enif_make_int(env, __LINE__);
cnt = 0;
next_ret = 1;
while(enif_map_iterator_get_pair(env,&iter_f,&key,&value)) {
if (!next_ret)
return enif_make_int(env, __LINE__);
list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f);
next_ret = enif_map_iterator_next(env,&iter_f);
cnt++;
}
if (cnt && next_ret)
return enif_make_int(env, __LINE__);
if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_LAST))
return enif_make_int(env, __LINE__);
cnt = 0;
prev_ret = 1;
while(enif_map_iterator_get_pair(env,&iter_b,&key,&value)) {
if (!prev_ret)
return enif_make_int(env, __LINE__);
/* Test that iter_f can step "backwards" */
if (!enif_map_iterator_prev(env,&iter_f)
|| !enif_map_iterator_get_pair(env,&iter_f,&k2,&v2)
|| k2 != key || v2 != value) {
return enif_make_int(env, __LINE__);
}
list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b);
prev_ret = enif_map_iterator_prev(env,&iter_b);
cnt++;
}
if (cnt) {
if (prev_ret || enif_map_iterator_prev(env,&iter_f))
return enif_make_int(env, __LINE__);
/* Test that iter_b can step "backwards" one step */
if (!enif_map_iterator_next(env, &iter_b)
|| !enif_map_iterator_get_pair(env,&iter_b,&k2,&v2)
|| k2 != key || v2 != value)
return enif_make_int(env, __LINE__);
}
enif_map_iterator_destroy(env, &iter_f);
enif_map_iterator_destroy(env, &iter_b);
return enif_make_tuple2(env, list_f, list_b);
}
static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifTimeUnit time_unit;
if (argc != 1)
return atom_false;
if (enif_compare(argv[0], atom_second) == 0)
time_unit = ERL_NIF_SEC;
else if (enif_compare(argv[0], atom_millisecond) == 0)
time_unit = ERL_NIF_MSEC;
else if (enif_compare(argv[0], atom_microsecond) == 0)
time_unit = ERL_NIF_USEC;
else if (enif_compare(argv[0], atom_nanosecond) == 0)
time_unit = ERL_NIF_NSEC;
else
time_unit = 4711; /* invalid time unit */
return enif_make_int64(env, enif_monotonic_time(time_unit));
}
static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifTimeUnit time_unit;
if (argc != 1)
return atom_false;
if (enif_compare(argv[0], atom_second) == 0)
time_unit = ERL_NIF_SEC;
else if (enif_compare(argv[0], atom_millisecond) == 0)
time_unit = ERL_NIF_MSEC;
else if (enif_compare(argv[0], atom_microsecond) == 0)
time_unit = ERL_NIF_USEC;
else if (enif_compare(argv[0], atom_nanosecond) == 0)
time_unit = ERL_NIF_NSEC;
else
time_unit = 4711; /* invalid time unit */
return enif_make_int64(env, enif_time_offset(time_unit));
}
static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifSInt64 i64;
ErlNifTime val;
ErlNifTimeUnit from, to;
if (argc != 3)
return atom_false;
if (!enif_get_int64(env, argv[0], &i64))
return enif_make_badarg(env);
val = (ErlNifTime) i64;
if (enif_compare(argv[1], atom_second) == 0)
from = ERL_NIF_SEC;
else if (enif_compare(argv[1], atom_millisecond) == 0)
from = ERL_NIF_MSEC;
else if (enif_compare(argv[1], atom_microsecond) == 0)
from = ERL_NIF_USEC;
else if (enif_compare(argv[1], atom_nanosecond) == 0)
from = ERL_NIF_NSEC;
else
from = 4711; /* invalid time unit */
if (enif_compare(argv[2], atom_second) == 0)
to = ERL_NIF_SEC;
else if (enif_compare(argv[2], atom_millisecond) == 0)
to = ERL_NIF_MSEC;
else if (enif_compare(argv[2], atom_microsecond) == 0)
to = ERL_NIF_USEC;
else if (enif_compare(argv[2], atom_nanosecond) == 0)
to = ERL_NIF_NSEC;
else
to = 4711; /* invalid time unit */
return enif_make_int64(env, enif_convert_time_unit(val, from, to));
}
static ERL_NIF_TERM now_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return enif_now_time(env);
}
static ERL_NIF_TERM cpu_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return enif_cpu_time(env);
}
static ERL_NIF_TERM unique_integer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM atom_pos = enif_make_atom(env,"positive"),
atom_mon = enif_make_atom(env,"monotonic");
ERL_NIF_TERM opts = argv[0], opt;
ErlNifUniqueInteger properties = 0;
while (!enif_is_empty_list(env, opts)) {
if (!enif_get_list_cell(env, opts, &opt, &opts))
return enif_make_badarg(env);
if (enif_compare(opt, atom_pos) == 0)
properties |= ERL_NIF_UNIQUE_POSITIVE;
if (enif_compare(opt, atom_mon) == 0)
properties |= ERL_NIF_UNIQUE_MONOTONIC;
}
return enif_make_unique_integer(env, properties);
}
static ERL_NIF_TERM is_process_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifPid pid;
if (!enif_get_local_pid(env, argv[0], &pid))
return enif_make_badarg(env);
if (enif_is_process_alive(env, &pid))
return atom_true;
return atom_false;
}
static ERL_NIF_TERM is_port_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifPort port;
if (!enif_get_local_port(env, argv[0], &port))
return enif_make_badarg(env);
if (enif_is_port_alive(env, &port))
return atom_true;
return atom_false;
}
static ERL_NIF_TERM term_to_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary bin;
ErlNifPid pid;
ErlNifEnv *msg_env = env;
ERL_NIF_TERM term;
if (enif_get_local_pid(env, argv[1], &pid))
msg_env = enif_alloc_env();
if (!enif_term_to_binary(msg_env, argv[0], &bin))
return enif_make_badarg(env);
term = enif_make_binary(msg_env, &bin);
if (msg_env != env) {
enif_send(env, &pid, msg_env, term);
enif_free_env(msg_env);
return atom_true;
} else {
return term;
}
}
static ERL_NIF_TERM binary_to_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary bin;
ERL_NIF_TERM term, ret_term;
ErlNifPid pid;
ErlNifEnv *msg_env = env;
unsigned int opts;
ErlNifUInt64 ret;
if (enif_get_local_pid(env, argv[1], &pid))
msg_env = enif_alloc_env();
if (!enif_inspect_binary(env, argv[0], &bin)
|| !enif_get_uint(env, argv[2], &opts))
return enif_make_badarg(env);
ret = enif_binary_to_term(msg_env, bin.data, bin.size, &term,
(ErlNifBinaryToTerm)opts);
if (!ret)
return atom_false;
ret_term = enif_make_uint64(env, ret);
if (msg_env != env) {
enif_send(env, &pid, msg_env, term);
enif_free_env(msg_env);
return ret_term;
} else {
return enif_make_tuple2(env, ret_term, term);
}
}
static ERL_NIF_TERM port_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifPort port;
if (!enif_get_local_port(env, argv[0], &port))
return enif_make_badarg(env);
if (!enif_port_command(env, &port, NULL, argv[1]))
return enif_make_badarg(env);
return atom_true;
}
static ERL_NIF_TERM format_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary obin;
unsigned int size;
if (!enif_get_uint(env, argv[0], &size))
return enif_make_badarg(env);
if (!enif_alloc_binary(size,&obin))
return enif_make_badarg(env);
if (enif_snprintf((char*)obin.data, (size_t)size, "%T", argv[1]) < 0)
return atom_false;
return enif_make_binary(env,&obin);
}
static int get_fd(ErlNifEnv* env, ERL_NIF_TERM term, struct fd_resource** rsrc)
{
if (!enif_get_resource(env, term, fd_resource_type, (void**)rsrc)) {
return 0;
}
return 1;
}
static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
struct fd_resource* fdr;
enum ErlNifSelectFlags mode;
void* obj;
ErlNifPid nifpid, *pid = NULL;
ERL_NIF_TERM ref;
int retval;
if (!get_fd(env, argv[0], &fdr)
|| !enif_get_uint(env, argv[1], (unsigned int*)&mode)
|| !enif_get_resource(env, argv[2], fd_resource_type, &obj))
{
return enif_make_badarg(env);
}
if (argv[3] != atom_null) {
if (!enif_get_local_pid(env, argv[3], &nifpid))
return enif_make_badarg(env);
pid = &nifpid;
}
ref = argv[4];
fdr->was_selected = 1;
enif_self(env, &fdr->pid);
retval = enif_select(env, fdr->fd, mode, obj, pid, ref);
return enif_make_int(env, retval);
}
#ifndef __WIN32__
static ERL_NIF_TERM pipe_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
struct fd_resource* read_rsrc;
struct fd_resource* write_rsrc;
ERL_NIF_TERM read_fd, write_fd;
int fds[2], flags;
if (pipe(fds) < 0)
return enif_make_string(env, "pipe failed", ERL_NIF_LATIN1);
if ((flags = fcntl(fds[0], F_GETFL, 0)) < 0
|| fcntl(fds[0], F_SETFL, flags|O_NONBLOCK) < 0
|| (flags = fcntl(fds[1], F_GETFL, 0)) < 0
|| fcntl(fds[1], F_SETFL, flags|O_NONBLOCK) < 0) {
close(fds[0]);
close(fds[1]);
return enif_make_string(env, "fcntl failed on pipe", ERL_NIF_LATIN1);
}
read_rsrc = enif_alloc_resource(fd_resource_type, sizeof(struct fd_resource));
write_rsrc = enif_alloc_resource(fd_resource_type, sizeof(struct fd_resource));
read_rsrc->fd = fds[0];
read_rsrc->was_selected = 0;
write_rsrc->fd = fds[1];
write_rsrc->was_selected = 0;
read_fd = enif_make_resource(env, read_rsrc);
write_fd = enif_make_resource(env, write_rsrc);
enif_release_resource(read_rsrc);
enif_release_resource(write_rsrc);
return enif_make_tuple2(env,
enif_make_tuple2(env, read_fd, make_pointer(env, read_rsrc)),
enif_make_tuple2(env, write_fd, make_pointer(env, write_rsrc)));
}
static ERL_NIF_TERM write_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
struct fd_resource* fdr;
ErlNifBinary bin;
int n, written = 0;
if (!get_fd(env, argv[0], &fdr)
|| !enif_inspect_binary(env, argv[1], &bin))
return enif_make_badarg(env);
for (;;) {
n = write(fdr->fd, bin.data + written, bin.size - written);
if (n >= 0) {
written += n;
if (written == bin.size) {
return atom_ok;
}
}
else if (errno == EAGAIN) {
return enif_make_tuple2(env, atom_eagain, enif_make_int(env, written));
}
else if (errno == EINTR) {
continue;
}
else {
return enif_make_tuple2(env, atom_error, enif_make_int(env, errno));
}
}
}
static ERL_NIF_TERM read_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
struct fd_resource* fdr;
unsigned char* buf;
int n, count;
ERL_NIF_TERM res;
if (!get_fd(env, argv[0], &fdr)
|| !enif_get_int(env, argv[1], &count) || count < 1)
return enif_make_badarg(env);
buf = enif_make_new_binary(env, count, &res);
for (;;) {
n = read(fdr->fd, buf, count);
if (n > 0) {
if (n < count) {
res = enif_make_sub_binary(env, res, 0, n);
}
return res;
}
else if (n == 0) {
return atom_eof;
}
else if (errno == EAGAIN) {
return atom_eagain;
}
else if (errno == EINTR) {
continue;
}
else {
return enif_make_tuple2(env, atom_error, enif_make_int(env, errno));
}
}
}
static ERL_NIF_TERM is_closed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
struct fd_resource* fdr;
if (!get_fd(env, argv[0], &fdr))
return enif_make_badarg(env);
return fdr->fd < 0 ? atom_true : atom_false;
}
#endif /* !__WIN32__ */
static void fd_resource_dtor(ErlNifEnv* env, void* obj)
{
struct fd_resource* fdr = (struct fd_resource*)obj;
resource_dtor(env, obj);
#ifdef __WIN32__
abort();
#else
if (fdr->fd >= 0) {
assert(!fdr->was_selected);
close(fdr->fd);
}
#endif
}
static struct {
void* obj;
int was_direct_call;
}last_fd_stop;
int fd_stop_cnt = 0;
static void fd_resource_stop(ErlNifEnv* env, void* obj, ErlNifEvent fd,
int is_direct_call)
{
struct fd_resource* fdr = (struct fd_resource*)obj;
assert(fd == fdr->fd);
assert(fd >= 0);
last_fd_stop.obj = obj;
last_fd_stop.was_direct_call = is_direct_call;
fd_stop_cnt++;
close(fd);
fdr->fd = -1; /* thread safety ? */
fdr->was_selected = 0;
{
ErlNifEnv* msg_env = enif_alloc_env();
ERL_NIF_TERM msg;
msg = enif_make_tuple3(msg_env,
atom_fd_resource_stop,
make_pointer(msg_env, obj),
enif_make_int(msg_env, is_direct_call));
enif_send(env, &fdr->pid, msg_env, msg);
enif_free_env(msg_env);
}
}
static ERL_NIF_TERM last_fd_stop_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM last, ret;
last = enif_make_tuple2(env, make_pointer(env, last_fd_stop.obj),
enif_make_int(env, last_fd_stop.was_direct_call));
ret = enif_make_tuple2(env, enif_make_int(env, fd_stop_cnt), last);
fd_stop_cnt = 0;
return ret;
}
static void monitor_resource_dtor(ErlNifEnv* env, void* obj)
{
resource_dtor(env, obj);
}
static ERL_NIF_TERM make_monitor(ErlNifEnv* env, const ErlNifMonitor* mon)
{
ERL_NIF_TERM mon_bin;
memcpy(enif_make_new_binary(env, sizeof(ErlNifMonitor), &mon_bin),
mon, sizeof(ErlNifMonitor));
return mon_bin;
}
static int get_monitor(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifMonitor* mon)
{
ErlNifBinary bin;
if (!enif_inspect_binary(env, term, &bin)
|| bin.size != sizeof(ErlNifMonitor))
return 0;
memcpy(mon, bin.data, bin.size);
return 1;
}
static void monitor_resource_down(ErlNifEnv* env, void* obj, ErlNifPid* pid,
ErlNifMonitor* mon)
{
struct monitor_resource* rsrc = (struct monitor_resource*)obj;
ErlNifEnv* build_env;
ErlNifEnv* msg_env;
ERL_NIF_TERM msg;
if (rsrc->use_msgenv) {
msg_env = enif_alloc_env();
build_env = msg_env;
}
else {
msg_env = NULL;
build_env = env;
}
msg = enif_make_tuple4(build_env,
atom_monitor_resource_down,
make_pointer(build_env, obj),
enif_make_pid(build_env, pid),
make_monitor(build_env, mon));
enif_send(env, &rsrc->receiver, msg_env, msg);
if (msg_env)
enif_free_env(msg_env);
}
static ERL_NIF_TERM alloc_monitor_resource_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
struct monitor_resource* rsrc;
rsrc = enif_alloc_resource(monitor_resource_type, sizeof(struct monitor_resource));
return make_pointer(env,rsrc);
}
static ERL_NIF_TERM monitor_process_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
struct monitor_resource* rsrc;
ErlNifPid target;
ErlNifMonitor mon;
int res;
if (!get_pointer(env, argv[0], (void**)&rsrc)
|| !enif_get_local_pid(env, argv[1], &target)
|| !enif_get_local_pid(env, argv[3], &rsrc->receiver)) {
return enif_make_badarg(env);
}
rsrc->use_msgenv = (argv[2] == atom_true);
res = enif_monitor_process(env, rsrc, &target, &mon);
return enif_make_tuple2(env, enif_make_int(env, res), make_monitor(env, &mon));
}
static ERL_NIF_TERM demonitor_process_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
struct monitor_resource* rsrc;
ErlNifMonitor mon;
int res;
if (!get_pointer(env, argv[0], (void**)&rsrc)
|| !get_monitor(env, argv[1], &mon)) {
return enif_make_badarg(env);
}
res = enif_demonitor_process(env, rsrc, &mon);
return enif_make_int(env, res);
}
static ERL_NIF_TERM compare_monitors_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ErlNifMonitor m1, m2;
if (!get_monitor(env, argv[0], &m1)
|| !get_monitor(env, argv[1], &m2)) {
return enif_make_badarg(env);
}
return enif_make_int(env, enif_compare_monitors(&m1, &m2));
}
/*********** monitor_frenzy ************/
struct frenzy_rand_bits
{
unsigned int source;
unsigned int bits_consumed;
};
static unsigned int frenzy_rand_bits_max;
unsigned rand_bits(struct frenzy_rand_bits* rnd, unsigned int nbits)
{
unsigned int res;
rnd->bits_consumed += nbits;
assert(rnd->bits_consumed <= frenzy_rand_bits_max);
res = rnd->source & ((1 << nbits)-1);
rnd->source >>= nbits;
return res;
}
#define FRENZY_PROCS_MAX_BITS 4
#define FRENZY_PROCS_MAX (1 << FRENZY_PROCS_MAX_BITS)
#define FRENZY_RESOURCES_MAX_BITS 4
#define FRENZY_RESOURCES_MAX (1 << FRENZY_RESOURCES_MAX_BITS)
#define FRENZY_MONITORS_MAX_BITS 4
#define FRENZY_MONITORS_MAX (1 << FRENZY_MONITORS_MAX_BITS)
struct frenzy_monitor {
ErlNifMutex* lock;
enum {
MON_FREE, MON_FREE_DOWN, MON_FREE_DEMONITOR,
MON_TRYING, MON_ACTIVE, MON_PENDING
} state;
ErlNifMonitor mon;
ErlNifPid pid;
unsigned int use_cnt;
};
struct frenzy_resource {
unsigned int rix;
struct frenzy_monitor monv[FRENZY_MONITORS_MAX];
};
struct frenzy_reslot {
ErlNifMutex* lock;
int stopped;
struct frenzy_resource* obj;
unsigned long alloc_cnt;
unsigned long release_cnt;
unsigned long dtor_cnt;
};
static struct frenzy_reslot resv[FRENZY_RESOURCES_MAX];
static ERL_NIF_TERM monitor_frenzy_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
struct frenzy_proc {
ErlNifPid pid;
int is_free;
};
static struct frenzy_proc procs[FRENZY_PROCS_MAX];
static struct frenzy_proc* proc_refs[FRENZY_PROCS_MAX];
static unsigned int nprocs, old_nprocs;
static ErlNifMutex* procs_lock;
static unsigned long spawn_cnt = 0;
static unsigned long kill_cnt = 0;
static unsigned long proc_histogram[FRENZY_PROCS_MAX];
static const unsigned int primes[] = {7, 13, 17, 19};
struct frenzy_resource* r;
struct frenzy_rand_bits rnd;
unsigned int op, inc, my_nprocs;
unsigned int mix; /* r->monv[] index */
unsigned int rix; /* resv[] index */
unsigned int pix; /* procs[] index */
unsigned int ref_ix; /* proc_refs[] index */
int self_pix, rv;
ERL_NIF_TERM retval = atom_error;
const ERL_NIF_TERM Op = argv[0];
const ERL_NIF_TERM Rnd = argv[1];
const ERL_NIF_TERM SelfPix = argv[2];
const ERL_NIF_TERM NewPid = argv[3];
if (enif_is_atom(env, Op)) {
if (Op == atom_init) {
if (procs_lock || !enif_get_uint(env, Rnd, &frenzy_rand_bits_max))
return enif_make_badarg(env);
procs_lock = enif_mutex_create("nif_SUITE:monitor_frenzy.procs");
nprocs = 0;
old_nprocs = 0;
for (pix = 0; pix < FRENZY_PROCS_MAX; pix++) {
proc_refs[pix] = &procs[pix];
procs[pix].is_free = 1;
proc_histogram[pix] = 0;
}
for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
resv[rix].lock = enif_mutex_create("nif_SUITE:monitor_frenzy.resv.lock");
resv[rix].obj = NULL;
resv[rix].stopped = 0;
resv[rix].alloc_cnt = 0;
resv[rix].release_cnt = 0;
resv[rix].dtor_cnt = 0;
}
/* Add self as first process */
enif_self(env, &procs[0].pid);
procs[0].is_free = 0;
old_nprocs = ++nprocs;
spawn_cnt = 1;
kill_cnt = 0;
return enif_make_uint(env, 0); /* SelfPix */
}
else if (Op == atom_stats) {
ERL_NIF_TERM hist[FRENZY_PROCS_MAX];
unsigned long res_alloc_cnt = 0;
unsigned long res_release_cnt = 0;
unsigned long res_dtor_cnt = 0;
for (ref_ix = 0; ref_ix < FRENZY_PROCS_MAX; ref_ix++) {
hist[ref_ix] = enif_make_ulong(env, proc_histogram[ref_ix]);
}
for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
res_alloc_cnt += resv[rix].alloc_cnt;
res_release_cnt += resv[rix].release_cnt;
res_dtor_cnt += resv[rix].dtor_cnt;
}
return
enif_make_list4(env,
enif_make_tuple2(env, enif_make_string(env, "proc_histogram", ERL_NIF_LATIN1),
enif_make_list_from_array(env, hist, FRENZY_PROCS_MAX)),
enif_make_tuple2(env, enif_make_string(env, "spawn_cnt", ERL_NIF_LATIN1),
enif_make_ulong(env, spawn_cnt)),
enif_make_tuple2(env, enif_make_string(env, "kill_cnt", ERL_NIF_LATIN1),
enif_make_ulong(env, kill_cnt)),
enif_make_tuple4(env, enif_make_string(env, "resource_alloc", ERL_NIF_LATIN1),
enif_make_ulong(env, res_alloc_cnt),
enif_make_ulong(env, res_release_cnt),
enif_make_ulong(env, res_dtor_cnt)));
}
else if (Op == atom_stop && procs_lock) { /* stop all */
/* Release all resources */
for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
enif_mutex_lock(resv[rix].lock);
r = resv[rix].obj;
if (r) {
resv[rix].obj = NULL;
resv[rix].release_cnt++;
}
resv[rix].stopped = 1;
enif_mutex_unlock(resv[rix].lock);
if (r)
enif_release_resource(r);
}
/* Remove and return all pids */
retval = enif_make_list(env, 0);
enif_mutex_lock(procs_lock);
for (ref_ix = 0; ref_ix < nprocs; ref_ix++) {
assert(!proc_refs[ref_ix]->is_free);
retval = enif_make_list_cell(env, enif_make_pid(env, &proc_refs[ref_ix]->pid),
retval);
proc_refs[ref_ix]->is_free = 1;
}
kill_cnt += nprocs;
nprocs = 0;
old_nprocs = 0;
enif_mutex_unlock(procs_lock);
return retval;
}
return enif_make_badarg(env);
}
if (!enif_get_int(env, SelfPix, &self_pix) ||
!enif_get_uint(env, Op, &op) ||
!enif_get_uint(env, Rnd, &rnd.source))
return enif_make_badarg(env);
rnd.bits_consumed = 0;
switch (op) {
case 0: { /* add/remove process */
ErlNifPid self;
enif_self(env, &self);
ref_ix = rand_bits(&rnd, FRENZY_PROCS_MAX_BITS) % FRENZY_PROCS_MAX;
enif_mutex_lock(procs_lock);
if (procs[self_pix].is_free || procs[self_pix].pid.pid != self.pid) {
/* Some one already removed me */
enif_mutex_unlock(procs_lock);
return atom_done;
}
if (ref_ix >= nprocs || nprocs < 2) { /* add process */
ref_ix = nprocs++;
pix = proc_refs[ref_ix] - procs;
assert(procs[pix].is_free);
if (!enif_get_local_pid(env, NewPid, &procs[pix].pid))
abort();
procs[pix].is_free = 0;
spawn_cnt++;
proc_histogram[ref_ix]++;
old_nprocs = nprocs;
enif_mutex_unlock(procs_lock);
DBG_TRACE2("Add pid %T, nprocs = %u\n", NewPid, nprocs);
retval = enif_make_uint(env, pix);
}
else { /* remove process */
pix = proc_refs[ref_ix] - procs;
if (pix == self_pix) {
ref_ix = (ref_ix + 1) % nprocs;
pix = proc_refs[ref_ix] - procs;
}
assert(procs[pix].pid.pid != self.pid);
assert(!procs[pix].is_free);
retval = enif_make_pid(env, &procs[pix].pid);
--nprocs;
assert(!proc_refs[nprocs]->is_free);
if (ref_ix != nprocs) {
struct frenzy_proc* tmp = proc_refs[ref_ix];
proc_refs[ref_ix] = proc_refs[nprocs];
proc_refs[nprocs] = tmp;
}
procs[pix].is_free = 1;
proc_histogram[nprocs]++;
kill_cnt++;
enif_mutex_unlock(procs_lock);
DBG_TRACE2("Removed pid %T, nprocs = %u\n", retval, nprocs);
}
break;
}
case 1:
case 2: /* create/delete/lookup resource */
rix = rand_bits(&rnd, FRENZY_RESOURCES_MAX_BITS) % FRENZY_RESOURCES_MAX;
inc = primes[rand_bits(&rnd, 2)];
while (enif_mutex_trylock(resv[rix].lock) == EBUSY) {
rix = (rix + inc) % FRENZY_RESOURCES_MAX;
}
if (resv[rix].stopped) {
retval = atom_done;
enif_mutex_unlock(resv[rix].lock);
break;
}
else if (resv[rix].obj == NULL) {
r = enif_alloc_resource(frenzy_resource_type,
sizeof(struct frenzy_resource));
resv[rix].obj = r;
resv[rix].alloc_cnt++;
r->rix = rix;
for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
r->monv[mix].lock = enif_mutex_create("nif_SUITE:monitor_frenzy.monv.lock");
r->monv[mix].state = MON_FREE;
r->monv[mix].use_cnt = 0;
r->monv[mix].pid.pid = 0; /* null-pid */
}
DBG_TRACE2("New resource at r=%p rix=%u\n", r, rix);
}
else {
unsigned int resource_op = rand_bits(&rnd, 3);
r = resv[rix].obj;
if (resource_op == 0) { /* delete resource */
resv[rix].obj = NULL;
resv[rix].release_cnt++;
enif_mutex_unlock(resv[rix].lock);
DBG_TRACE2("Delete resource at r=%p rix=%u\n", r, rix);
enif_release_resource(r);
retval = atom_ok;
break;
}
else if (resource_op == 1) { /* return resource */
retval = enif_make_resource(env, r);
enif_mutex_unlock(resv[rix].lock);
break;
}
}
enif_keep_resource(r);
enif_mutex_unlock(resv[rix].lock);
/* monitor/demonitor */
mix = rand_bits(&rnd, FRENZY_MONITORS_MAX_BITS) % FRENZY_MONITORS_MAX;
inc = primes[rand_bits(&rnd, 2)];
while (enif_mutex_trylock(r->monv[mix].lock) == EBUSY) {
mix = (mix + inc) % FRENZY_MONITORS_MAX;
}
switch (r->monv[mix].state) {
case MON_FREE:
case MON_FREE_DOWN:
case MON_FREE_DEMONITOR: { /* do monitor */
/*
* Use an old possibly larger value of 'nprocs', to increase
* probability of monitoring an already terminated process
*/
my_nprocs = old_nprocs;
if (my_nprocs > 0) {
int save_state = r->monv[mix].state;
ref_ix = rand_bits(&rnd, FRENZY_PROCS_MAX_BITS) % my_nprocs;
pix = proc_refs[ref_ix] - procs;
r->monv[mix].pid.pid = procs[pix].pid.pid; /* "atomic" */
r->monv[mix].state = MON_TRYING;
rv = enif_monitor_process(env, r, &r->monv[mix].pid, &r->monv[mix].mon);
if (rv == 0) {
r->monv[mix].state = MON_ACTIVE;
r->monv[mix].use_cnt++;
DBG_TRACE3("Monitor from r=%p rix=%u to %T\n",
r, r->rix, r->monv[mix].pid.pid);
}
else {
r->monv[mix].state = save_state;
DBG_TRACE4("Monitor from r=%p rix=%u to %T FAILED with %d\n",
r, r->rix, r->monv[mix].pid.pid, rv);
}
retval = enif_make_int(env,rv);
}
else {
DBG_TRACE0("No pids to monitor\n");
retval = atom_ok;
}
break;
}
case MON_ACTIVE: /* do demonitor */
rv = enif_demonitor_process(env, r, &r->monv[mix].mon);
if (rv == 0) {
DBG_TRACE3("Demonitor from r=%p rix=%u to %T\n",
r, r->rix, r->monv[mix].pid.pid);
r->monv[mix].state = MON_FREE_DEMONITOR;
}
else {
DBG_TRACE4("Demonitor from r=%p rix=%u to %T FAILED with %d\n",
r, r->rix, r->monv[mix].pid.pid, rv);
r->monv[mix].state = MON_PENDING;
}
retval = enif_make_int(env,rv);
break;
case MON_PENDING: /* waiting for 'down' callback, do nothing */
retval = atom_ok;
break;
default:
abort();
break;
}
enif_mutex_unlock(r->monv[mix].lock);
enif_release_resource(r);
break;
case 3: /* no-op */
retval = atom_ok;
break;
}
{
int percent = (rand_bits(&rnd, 6) + 1) * 2; /* 2 to 128 */
if (percent <= 100)
enif_consume_timeslice(env, percent);
}
return retval;
}
static void frenzy_resource_dtor(ErlNifEnv* env, void* obj)
{
struct frenzy_resource* r = (struct frenzy_resource*) obj;
unsigned int mix;
DBG_TRACE2("DTOR r=%p rix=%u\n", r, r->rix);
enif_mutex_lock(resv[r->rix].lock);
resv[r->rix].dtor_cnt++;
enif_mutex_unlock(resv[r->rix].lock);
for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
assert(r->monv[mix].state != MON_PENDING);
enif_mutex_destroy(r->monv[mix].lock);
r->monv[mix].lock = NULL;
}
}
static void frenzy_resource_down(ErlNifEnv* env, void* obj, ErlNifPid* pid,
ErlNifMonitor* mon)
{
struct frenzy_resource* r = (struct frenzy_resource*) obj;
unsigned int mix;
DBG_TRACE3("DOWN pid=%T, r=%p rix=%u\n", pid->pid, r, r->rix);
for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
if (r->monv[mix].pid.pid == pid->pid && r->monv[mix].state >= MON_TRYING) {
enif_mutex_lock(r->monv[mix].lock);
if (enif_compare_monitors(mon, &r->monv[mix].mon) == 0) {
assert(r->monv[mix].state >= MON_ACTIVE);
r->monv[mix].state = MON_FREE_DOWN;
enif_mutex_unlock(r->monv[mix].lock);
return;
}
enif_mutex_unlock(r->monv[mix].lock);
}
}
enif_fprintf(stderr, "DOWN called for unknown monitor\n");
abort();
}
static ErlNifFunc nif_funcs[] =
{
{"lib_version", 0, lib_version},
{"call_history", 0, call_history},
{"hold_nif_mod_priv_data", 1, hold_nif_mod_priv_data},
{"nif_mod_call_history", 0, nif_mod_call_history},
{"list_seq", 1, list_seq},
{"type_test", 0, type_test},
{"tuple_2_list", 1, tuple_2_list},
{"is_identical",2,is_identical},
{"compare",2,compare},
{"phash2_nif",1,phash2_nif},
{"phash2_ranged_nif",2,phash2_ranged_nif},
{"many_args_100", 100, many_args_100},
{"clone_bin", 1, clone_bin},
{"make_sub_bin", 3, make_sub_bin},
{"string_to_bin", 2, string_to_bin},
{"atom_to_bin", 2, atom_to_bin},
{"macros", 1, macros},
{"tuple_2_list_and_tuple",1,tuple_2_list_and_tuple},
{"iolist_2_bin", 1, iolist_2_bin},
{"get_resource_type", 1, get_resource_type},
{"alloc_resource", 2, alloc_resource},
{"make_resource", 1, make_resource},
{"get_resource", 2, get_resource},
{"release_resource", 1, release_resource},
{"last_resource_dtor_call", 0, last_resource_dtor_call},
{"make_new_resource", 2, make_new_resource},
{"check_is", 11, check_is},
{"check_is_exception", 0, check_is_exception},
{"length_test", 6, length_test},
{"make_atoms", 0, make_atoms},
{"make_strings", 0, make_strings},
{"make_new_resource", 2, make_new_resource},
{"make_new_resource_binary", 1, make_new_resource_binary},
{"send_list_seq", 2, send_list_seq},
{"send_new_blob", 2, send_new_blob},
{"alloc_msgenv", 0, alloc_msgenv},
{"clear_msgenv", 1, clear_msgenv},
{"grow_blob", 2, grow_blob},
{"grow_blob", 3, grow_blob},
{"send_blob", 2, send_blob},
{"send3_blob", 3, send3_blob},
{"send_blob_thread", 3, send_blob_thread},
{"join_send_thread", 1, join_send_thread},
{"copy_blob", 1, copy_blob},
{"send_term", 2, send_term},
{"send_copy_term", 2, send_copy_term},
{"reverse_list",1, reverse_list},
{"echo_int", 1, echo_int},
{"type_sizes", 0, type_sizes},
{"otp_9668_nif", 1, otp_9668_nif},
{"otp_9828_nif", 1, otp_9828_nif},
{"consume_timeslice_nif", 2, consume_timeslice_nif},
{"call_nif_schedule", 2, call_nif_schedule},
{"call_nif_exception", 1, call_nif_exception},
{"call_nif_nan_or_inf", 1, call_nif_nan_or_inf},
{"call_nif_atom_too_long", 1, call_nif_atom_too_long},
{"is_map_nif", 1, is_map_nif},
{"get_map_size_nif", 1, get_map_size_nif},
{"make_new_map_nif", 0, make_new_map_nif},
{"make_map_put_nif", 3, make_map_put_nif},
{"get_map_value_nif", 2, get_map_value_nif},
{"make_map_update_nif", 3, make_map_update_nif},
{"make_map_remove_nif", 2, make_map_remove_nif},
{"maps_from_list_nif", 1, maps_from_list_nif},
{"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif},
{"monotonic_time", 1, monotonic_time},
{"time_offset", 1, time_offset},
{"convert_time_unit", 3, convert_time_unit},
{"now_time", 0, now_time},
{"cpu_time", 0, cpu_time},
{"unique_integer_nif", 1, unique_integer},
{"is_process_alive_nif", 1, is_process_alive},
{"is_port_alive_nif", 1, is_port_alive},
{"term_to_binary_nif", 2, term_to_binary},
{"binary_to_term_nif", 3, binary_to_term},
{"port_command_nif", 2, port_command},
{"format_term_nif", 2, format_term},
{"select_nif", 5, select_nif},
#ifndef __WIN32__
{"pipe_nif", 0, pipe_nif},
{"write_nif", 2, write_nif},
{"read_nif", 2, read_nif},
{"is_closed_nif", 1, is_closed_nif},
#endif
{"last_fd_stop_call", 0, last_fd_stop_call},
{"alloc_monitor_resource_nif", 0, alloc_monitor_resource_nif},
{"monitor_process_nif", 4, monitor_process_nif},
{"demonitor_process_nif", 2, demonitor_process_nif},
{"compare_monitors_nif", 2, compare_monitors_nif},
{"monitor_frenzy_nif", 4, monitor_frenzy_nif}
};
ERL_NIF_INIT(nif_SUITE,nif_funcs,load,NULL,upgrade,unload)