1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2009-2020. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 #include <erl_nif.h>
21 
22 #include <stdio.h>
23 #include <string.h>
24 #include <assert.h>
25 #include <limits.h>
26 #include <errno.h>
27 #ifndef __WIN32__
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <sys/uio.h>
31 #endif
32 
33 #include "nif_mod.h"
34 
35 #if 0
36 static ErlNifMutex* dbg_trace_lock;
37 #define DBG_TRACE_INIT dbg_trace_lock = enif_mutex_create("nif_SUITE.DBG_TRACE")
38 #define DBG_TRACE_FINI enif_mutex_destroy(dbg_trace_lock)
39 #define DBG_TRACE_LOCK enif_mutex_lock(dbg_trace_lock)
40 #define DBG_TRACE_UNLOCK enif_mutex_unlock(dbg_trace_lock)
41 #define DBG_TRACE0(FMT) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT); DBG_TRACE_UNLOCK; }while(0)
42 #define DBG_TRACE1(FMT, A) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A); DBG_TRACE_UNLOCK; }while(0)
43 #define DBG_TRACE2(FMT, A, B) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A, B); DBG_TRACE_UNLOCK; }while(0)
44 #define DBG_TRACE3(FMT, A, B, C) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A, B, C); DBG_TRACE_UNLOCK; }while(0)
45 #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)
46 #else
47 #define DBG_TRACE_INIT
48 #define DBG_TRACE_FINI
49 #define DBG_TRACE0(FMT)
50 #define DBG_TRACE1(FMT, A)
51 #define DBG_TRACE2(FMT, A, B)
52 #define DBG_TRACE3(FMT, A, B, C)
53 #define DBG_TRACE4(FMT, A, B, C, D)
54 #endif
55 
56 /*
57  * Hack to get around this function missing from the NIF API.
58  * TODO: Add this function/macro in the appropriate place, probably with
59  *       enif_make_pid() in erl_nif_api_funcs.h
60  */
61 #ifndef enif_make_port
62 #define enif_make_port(ENV, PORT) ((void)(ENV),(const ERL_NIF_TERM)((PORT)->port_id))
63 #endif
64 
65 static int static_cntA; /* zero by default */
66 static int static_cntB = NIF_SUITE_LIB_VER * 100;
67 
68 static ERL_NIF_TERM atom_false;
69 static ERL_NIF_TERM atom_true;
70 static ERL_NIF_TERM atom_self;
71 static ERL_NIF_TERM atom_ok;
72 static ERL_NIF_TERM atom_join;
73 static ERL_NIF_TERM atom_binary_resource_type;
74 static ERL_NIF_TERM atom_second;
75 static ERL_NIF_TERM atom_millisecond;
76 static ERL_NIF_TERM atom_microsecond;
77 static ERL_NIF_TERM atom_nanosecond;
78 static ERL_NIF_TERM atom_eagain;
79 static ERL_NIF_TERM atom_eof;
80 static ERL_NIF_TERM atom_error;
81 static ERL_NIF_TERM atom_fd_resource_stop;
82 static ERL_NIF_TERM atom_monitor_resource_type;
83 static ERL_NIF_TERM atom_monitor_resource_down;
84 static ERL_NIF_TERM atom_init;
85 static ERL_NIF_TERM atom_stats;
86 static ERL_NIF_TERM atom_done;
87 static ERL_NIF_TERM atom_stop;
88 static ERL_NIF_TERM atom_null;
89 static ERL_NIF_TERM atom_pid;
90 static ERL_NIF_TERM atom_port;
91 static ERL_NIF_TERM atom_send;
92 static ERL_NIF_TERM atom_lookup;
93 static ERL_NIF_TERM atom_badarg;
94 
95 typedef struct
96 {
97     int ref_cnt;
98     CallInfo* call_history;
99     NifModPrivData* nif_mod;
100     union { ErlNifResourceType* t; void* vp; } rt_arr[2];
101 } PrivData;
102 
103 /*
104  * Use a union for pointer type conversion to avoid compiler warnings
105  * about strict-aliasing violations with gcc-4.1. gcc >= 4.2 does not
106  * emit the warning.
107  * TODO: Reconsider use of union once gcc-4.1 is obsolete?
108  */
109 typedef union {
110     void* vp;
111     struct make_term_info* p;
112 } mti_t;
113 
add_call(ErlNifEnv * env,PrivData * data,const char * func_name)114 void add_call(ErlNifEnv* env, PrivData* data, const char* func_name)
115 {
116     CallInfo* call = enif_alloc(sizeof(CallInfo)+strlen(func_name));
117     strcpy(call->func_name, func_name);
118     call->lib_ver = NIF_SUITE_LIB_VER;
119     call->next = data->call_history;
120     call->static_cntA = ++static_cntA;
121     call->static_cntB = ++static_cntB;
122     data->call_history = call;
123     call->arg = NULL;
124     call->arg_sz = 0;
125 }
126 
127 #define ADD_CALL(FUNC_NAME) add_call(env, enif_priv_data(env),FUNC_NAME)
128 
129 static void*    resource_dtor_last = NULL;
130 static unsigned resource_dtor_last_sz = 0;
131 static char     resource_dtor_last_data[20];
132 static int resource_dtor_cnt = 0;
133 
resource_dtor(ErlNifEnv * env,void * obj)134 static void resource_dtor(ErlNifEnv* env, void* obj)
135 {
136     resource_dtor_last = obj;
137     resource_dtor_cnt++;
138     resource_dtor_last_sz = enif_sizeof_resource(obj);
139     assert(resource_dtor_last_sz <= sizeof(resource_dtor_last_data));
140     memcpy(resource_dtor_last_data, obj, resource_dtor_last_sz);
141 }
142 
143 static ErlNifResourceType* msgenv_resource_type;
144 static void msgenv_dtor(ErlNifEnv* env, void* obj);
145 
146 static ErlNifResourceType* binary_resource_type;
147 static void binary_resource_dtor(ErlNifEnv* env, void* obj);
148 struct binary_resource {
149     unsigned char* data;
150     unsigned size;
151 };
152 
153 static ErlNifResourceType* fd_resource_type;
154 static void fd_resource_dtor(ErlNifEnv* env, void* obj);
155 static void fd_resource_stop(ErlNifEnv* env, void* obj, ErlNifEvent, int);
156 static ErlNifResourceTypeInit fd_rt_init = {
157     fd_resource_dtor,
158     fd_resource_stop
159 };
160 struct fd_resource {
161     ErlNifEvent fd;
162     int was_selected;
163     ErlNifPid pid;
164 };
165 
166 static ErlNifResourceType* monitor_resource_type;
167 static void monitor_resource_dtor(ErlNifEnv* env, void* obj);
168 static void monitor_resource_down(ErlNifEnv*, void* obj, ErlNifPid*, ErlNifMonitor*);
169 static ErlNifResourceTypeInit monitor_rt_init = {
170     monitor_resource_dtor,
171     NULL,
172     monitor_resource_down
173 };
174 struct monitor_resource {
175     ErlNifPid receiver;
176     int use_msgenv;
177 };
178 
179 static ErlNifResourceType* frenzy_resource_type;
180 static void frenzy_resource_dtor(ErlNifEnv* env, void* obj);
181 static void frenzy_resource_down(ErlNifEnv*, void* obj, ErlNifPid*, ErlNifMonitor*);
182 static ErlNifResourceTypeInit frenzy_rt_init = {
183     frenzy_resource_dtor,
184     NULL,
185     frenzy_resource_down
186 };
187 
188 static ErlNifResourceType* whereis_resource_type;
189 static void whereis_thread_resource_dtor(ErlNifEnv* env, void* obj);
190 static ErlNifResourceType* ioq_resource_type;
191 
192 static void ioq_resource_dtor(ErlNifEnv* env, void* obj);
193 struct ioq_resource {
194     ErlNifIOQueue *q;
195 };
196 
get_pointer(ErlNifEnv * env,ERL_NIF_TERM term,void ** pp)197 static int get_pointer(ErlNifEnv* env, ERL_NIF_TERM term, void** pp)
198 {
199     ErlNifBinary bin;
200     int r = enif_inspect_binary(env, term, &bin);
201     if (r) {
202 	*pp = *(void**)bin.data;
203     }
204     return r;
205 }
206 
make_pointer(ErlNifEnv * env,void * p)207 static ERL_NIF_TERM make_pointer(ErlNifEnv* env, void* p)
208 {
209     void** bin_data;
210     ERL_NIF_TERM res;
211     bin_data = (void**)enif_make_new_binary(env, sizeof(void*), &res);
212     *bin_data = p;
213     return res;
214 }
215 
load(ErlNifEnv * env,void ** priv_data,ERL_NIF_TERM load_info)216 static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
217 {
218     PrivData* data = enif_alloc(sizeof(PrivData));
219     assert(data != NULL);
220     data->ref_cnt = 1;
221     data->call_history = NULL;
222     data->nif_mod = NULL;
223 
224     DBG_TRACE_INIT;
225 
226     add_call(env, data, "load");
227 
228     data->rt_arr[0].t = enif_open_resource_type(env,NULL,"Gold",resource_dtor,
229 						ERL_NIF_RT_CREATE,NULL);
230     data->rt_arr[1].t = enif_open_resource_type(env,NULL,"Silver",resource_dtor,
231 						ERL_NIF_RT_CREATE,NULL);
232 
233     binary_resource_type =  enif_open_resource_type(env,NULL,"nif_SUITE.binary",
234 						    binary_resource_dtor,
235 						    ERL_NIF_RT_CREATE, NULL);
236 
237     msgenv_resource_type =  enif_open_resource_type(env,NULL,"nif_SUITE.msgenv",
238 						    msgenv_dtor,
239 						    ERL_NIF_RT_CREATE, NULL);
240     fd_resource_type =  enif_open_resource_type_x(env, "nif_SUITE.fd",
241                                                   &fd_rt_init,
242                                                   ERL_NIF_RT_CREATE, NULL);
243     monitor_resource_type = enif_open_resource_type_x(env, "nif_SUITE.monitor",
244                                                       &monitor_rt_init,
245                                                       ERL_NIF_RT_CREATE, NULL);
246     frenzy_resource_type = enif_open_resource_type_x(env, "nif_SUITE.monitor_frenzy",
247 						      &frenzy_rt_init,
248 						      ERL_NIF_RT_CREATE, NULL);
249 
250     whereis_resource_type = enif_open_resource_type(env, NULL, "nif_SUITE.whereis",
251                             whereis_thread_resource_dtor, ERL_NIF_RT_CREATE, NULL);
252 
253     ioq_resource_type = enif_open_resource_type(env,NULL,"ioq",
254                                                 ioq_resource_dtor,
255                                                 ERL_NIF_RT_CREATE, NULL);
256 
257     atom_false = enif_make_atom(env,"false");
258     atom_true = enif_make_atom(env,"true");
259     atom_self = enif_make_atom(env,"self");
260     atom_ok = enif_make_atom(env,"ok");
261     atom_join = enif_make_atom(env,"join");
262     atom_binary_resource_type = enif_make_atom(env,"binary_resource_type");
263     atom_second = enif_make_atom(env,"second");
264     atom_millisecond = enif_make_atom(env,"millisecond");
265     atom_microsecond = enif_make_atom(env,"microsecond");
266     atom_nanosecond = enif_make_atom(env,"nanosecond");
267     atom_eagain = enif_make_atom(env, "eagain");
268     atom_eof = enif_make_atom(env, "eof");
269     atom_error = enif_make_atom(env, "error");
270     atom_fd_resource_stop = enif_make_atom(env, "fd_resource_stop");
271     atom_monitor_resource_type = enif_make_atom(env, "monitor_resource_type");
272     atom_monitor_resource_down = enif_make_atom(env, "monitor_resource_down");
273     atom_init = enif_make_atom(env,"init");
274     atom_stats = enif_make_atom(env,"stats");
275     atom_done = enif_make_atom(env,"done");
276     atom_stop = enif_make_atom(env,"stop");
277     atom_null = enif_make_atom(env,"null");
278     atom_pid = enif_make_atom(env, "pid");
279     atom_port = enif_make_atom(env, "port");
280     atom_send = enif_make_atom(env, "send");
281     atom_lookup = enif_make_atom(env, "lookup");
282     atom_badarg = enif_make_atom(env, "badarg");
283 
284     *priv_data = data;
285     return 0;
286 }
287 
resource_takeover(ErlNifEnv * env,PrivData * priv)288 static void resource_takeover(ErlNifEnv* env, PrivData* priv)
289 {
290     ErlNifResourceFlags tried;
291     ErlNifResourceType* rt;
292     rt = enif_open_resource_type(env, NULL, "Gold", resource_dtor,
293 				 ERL_NIF_RT_TAKEOVER, &tried);
294     assert(rt == priv->rt_arr[0].t);
295     assert(tried == ERL_NIF_RT_TAKEOVER);
296     rt = enif_open_resource_type(env, NULL, "Silver", resource_dtor,
297 				 ERL_NIF_RT_TAKEOVER, &tried);
298     assert(rt == priv->rt_arr[1].t);
299     assert(tried == ERL_NIF_RT_TAKEOVER);
300 
301     rt =  enif_open_resource_type(env, NULL, "nif_SUITE.binary", binary_resource_dtor,
302 				  ERL_NIF_RT_TAKEOVER, &tried);
303     assert(rt != NULL);
304     assert(tried == ERL_NIF_RT_TAKEOVER);
305     assert(binary_resource_type==NULL || binary_resource_type == rt);
306     binary_resource_type = rt;
307 
308     rt = enif_open_resource_type(env, NULL, "nif_SUITE.msgenv", msgenv_dtor,
309 				  ERL_NIF_RT_TAKEOVER, &tried);
310     assert(rt != NULL);
311     assert(tried == ERL_NIF_RT_TAKEOVER);
312     assert(msgenv_resource_type==NULL || msgenv_resource_type == rt);
313     msgenv_resource_type = rt;
314 }
315 
upgrade(ErlNifEnv * env,void ** priv_data,void ** old_priv_data,ERL_NIF_TERM load_info)316 static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
317 {
318     PrivData* priv = (PrivData*) *old_priv_data;
319     add_call(env, priv, "upgrade");
320     priv->ref_cnt++;
321     *priv_data = *old_priv_data;
322     resource_takeover(env,priv);
323     return 0;
324 }
325 
unload(ErlNifEnv * env,void * priv_data)326 static void unload(ErlNifEnv* env, void* priv_data)
327 {
328     PrivData* data = priv_data;
329     add_call(env, data, "unload");
330     if (--data->ref_cnt == 0) {
331 	if (data->nif_mod != NULL) {
332 	    NifModPrivData_release(data->nif_mod);
333 	}
334 	enif_free(priv_data);
335     }
336     DBG_TRACE_FINI;
337 }
338 
lib_version(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])339 static ERL_NIF_TERM lib_version(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
340 {
341     ADD_CALL("lib_version");
342     return enif_make_int(env, NIF_SUITE_LIB_VER);
343 }
344 
make_call_history(ErlNifEnv * env,CallInfo ** headp)345 static ERL_NIF_TERM make_call_history(ErlNifEnv* env, CallInfo** headp)
346 {
347     ERL_NIF_TERM list = enif_make_list(env, 0); /* NIL */
348 
349     while (*headp != NULL) {
350 	CallInfo* call = *headp;
351 	ERL_NIF_TERM func_term = enif_make_atom(env,call->func_name);
352 	ERL_NIF_TERM tpl;
353 	if (call->arg != NULL) {
354 	    ERL_NIF_TERM arg_bin;
355 	    memcpy(enif_make_new_binary(env, call->arg_sz, &arg_bin),
356 		   call->arg, call->arg_sz);
357 	    func_term = enif_make_tuple2(env, func_term, arg_bin);
358 	}
359 	tpl = enif_make_tuple4(env, func_term,
360 			       enif_make_int(env,call->lib_ver),
361 			       enif_make_int(env,call->static_cntA),
362 			       enif_make_int(env,call->static_cntB));
363 	list = enif_make_list_cell(env, tpl, list);
364 	*headp = call->next;
365 	enif_free(call);
366     }
367     return list;
368 }
369 
call_history(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])370 static ERL_NIF_TERM call_history(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
371 {
372     PrivData* data = (PrivData*) enif_priv_data(env);
373 
374     return make_call_history(env,&data->call_history);
375 }
376 
hold_nif_mod_priv_data(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])377 static ERL_NIF_TERM hold_nif_mod_priv_data(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
378 {
379     PrivData* data = (PrivData*) enif_priv_data(env);
380     void* ptr;
381 
382     if (!get_pointer(env,argv[0],&ptr)) {
383 	return enif_make_badarg(env);
384     }
385     if (data->nif_mod != NULL) {
386 	NifModPrivData_release(data->nif_mod);
387     }
388     data->nif_mod = (NifModPrivData*) ptr;
389     return enif_make_int(env,++(data->nif_mod->ref_cnt));
390 }
391 
nif_mod_call_history(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])392 static ERL_NIF_TERM nif_mod_call_history(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
393 {
394     PrivData* data = (PrivData*) enif_priv_data(env);
395     ERL_NIF_TERM ret;
396     if (data->nif_mod == NULL) {
397 	return enif_make_string(env,"nif_mod pointer is NULL", ERL_NIF_LATIN1);
398     }
399     enif_mutex_lock(data->nif_mod->mtx);
400     ret = make_call_history(env, &data->nif_mod->call_history);
401     enif_mutex_unlock(data->nif_mod->mtx);
402     return ret;
403 }
404 
list_seq(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])405 static ERL_NIF_TERM list_seq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
406 {
407     ERL_NIF_TERM list;
408     int n;
409     if (!enif_get_int(env, argv[0], &n)) {
410 	return enif_make_badarg(env);
411     }
412     list = enif_make_list(env, 0); /* NIL */
413     while (n > 0) {
414 	list = enif_make_list_cell(env, enif_make_int(env,n), list);
415 	n--;
416     }
417     return list;
418 }
419 
test_int(ErlNifEnv * env,int i1)420 static int test_int(ErlNifEnv* env, int i1)
421 {
422     int i2 = 0;
423     ERL_NIF_TERM int_term = enif_make_int(env, i1);
424     if (!enif_get_int(env,int_term, &i2) || i1 != i2) {
425 	fprintf(stderr, "test_int(%d) ...FAILED i2=%d\r\n", i1, i2);
426 	return 0;
427     }
428     return 1;
429 }
430 
test_uint(ErlNifEnv * env,unsigned i1)431 static int test_uint(ErlNifEnv* env, unsigned i1)
432 {
433     unsigned i2 = 0;
434     ERL_NIF_TERM int_term = enif_make_uint(env, i1);
435     if (!enif_get_uint(env,int_term, &i2) || i1 != i2) {
436 	fprintf(stderr, "test_uint(%u) ...FAILED i2=%u\r\n", i1, i2);
437 	return 0;
438     }
439     return 1;
440 }
441 
test_long(ErlNifEnv * env,long i1)442 static int test_long(ErlNifEnv* env, long i1)
443 {
444     long i2 = 0;
445     ERL_NIF_TERM int_term = enif_make_long(env, i1);
446     if (!enif_get_long(env,int_term, &i2) || i1 != i2) {
447 	fprintf(stderr, "test_long(%ld) ...FAILED i2=%ld\r\n", i1, i2);
448 	return 0;
449     }
450     return 1;
451 }
452 
test_ulong(ErlNifEnv * env,unsigned long i1)453 static int test_ulong(ErlNifEnv* env, unsigned long i1)
454 {
455     unsigned long i2 = 0;
456     ERL_NIF_TERM int_term = enif_make_ulong(env, i1);
457     if (!enif_get_ulong(env,int_term, &i2) || i1 != i2) {
458 	fprintf(stderr, "test_ulong(%lu) ...FAILED i2=%lu\r\n", i1, i2);
459 	return 0;
460     }
461     return 1;
462 }
463 
test_int64(ErlNifEnv * env,ErlNifSInt64 i1)464 static int test_int64(ErlNifEnv* env, ErlNifSInt64 i1)
465 {
466     ErlNifSInt64 i2 = 0;
467     ERL_NIF_TERM int_term = enif_make_int64(env, i1);
468     if (!enif_get_int64(env,int_term, &i2) || i1 != i2) {
469 	fprintf(stderr, "test_int64(%ld) ...FAILED i2=%ld\r\n",
470 		(long)i1, (long)i2);
471 	return 0;
472     }
473     return 1;
474 }
475 
test_uint64(ErlNifEnv * env,ErlNifUInt64 i1)476 static int test_uint64(ErlNifEnv* env, ErlNifUInt64 i1)
477 {
478     ErlNifUInt64 i2 = 0;
479     ERL_NIF_TERM int_term = enif_make_uint64(env, i1);
480     if (!enif_get_uint64(env,int_term, &i2) || i1 != i2) {
481 	fprintf(stderr, "test_ulong(%lu) ...FAILED i2=%lu\r\n",
482 		(unsigned long)i1, (unsigned long)i2);
483 	return 0;
484     }
485     return 1;
486 }
487 
test_double(ErlNifEnv * env,double d1)488 static int test_double(ErlNifEnv* env, double d1)
489 {
490     double d2 = 0;
491     ERL_NIF_TERM term = enif_make_double(env, d1);
492     if (!enif_get_double(env,term, &d2) || d1 != d2) {
493 	fprintf(stderr, "test_double(%e) ...FAILED i2=%e\r\n", d1, d2);
494 	return 0;
495     }
496     return 1;
497 }
498 
499 #define TAG_BITS        4
500 #define SMALL_BITS	(sizeof(void*)*8 - TAG_BITS)
501 #ifdef _WIN64
502 #define MAX_SMALL	((1LL << (SMALL_BITS-1))-1)
503 #define MIN_SMALL	(-(1LL << (SMALL_BITS-1)))
504 #else
505 #define MAX_SMALL	((1L << (SMALL_BITS-1))-1)
506 #define MIN_SMALL	(-(1L << (SMALL_BITS-1)))
507 #endif
508 
type_test(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])509 static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
510 {
511     int i;
512     int sint;
513     unsigned uint;
514     long slong;
515     unsigned long ulong;
516     ErlNifSInt64 sint64;
517     ErlNifUInt64 uint64;
518     double d;
519     ERL_NIF_TERM atom, ref1, ref2;
520 
521     sint = INT_MIN;
522     do {
523 	if (!test_int(env,sint)) {
524 	    goto error;
525 	}
526 	sint += ~sint / 3 + 1;
527     } while (sint < 0);
528     sint = INT_MAX;
529     do {
530 	if (!test_int(env,sint)) {
531 	    goto error;
532 	}
533 	sint -= sint / 3 + 1;
534     } while (sint >= 0);
535 
536     slong = LONG_MIN;
537     do {
538 	if (!test_long(env,slong)) {
539 	    goto error;
540 	}
541 	slong += ~slong / 3 + 1;
542     } while (slong < 0);
543     slong = LONG_MAX;
544     do {
545 	if (!test_long(env,slong)) {
546 	    goto error;
547 	}
548 	slong -= slong / 3 + 1;
549     } while (slong >= 0);
550 
551     sint64 = ((ErlNifSInt64)1 << 63); /* INT64_MIN */
552     do {
553 	if (!test_int64(env,sint64)) {
554 	    goto error;
555 	}
556 	sint64 += ~sint64 / 3 + 1;
557     } while (sint64 < 0);
558     sint64 = ((ErlNifUInt64)1 << 63) - 1; /* INT64_MAX */
559     do {
560 	if (!test_int64(env,sint64)) {
561 	    goto error;
562 	}
563 	sint64 -= sint64 / 3 + 1;
564     } while (sint64 >= 0);
565 
566     uint = UINT_MAX;
567     for (;;) {
568 	if (!test_uint(env,uint)) {
569 	    goto error;
570 	}
571 	if (uint == 0) break;
572 	uint -= uint / 3 + 1;
573     }
574     ulong = ULONG_MAX;
575     for (;;) {
576 	if (!test_ulong(env,ulong)) {
577 	    goto error;
578 	}
579 	if (ulong == 0) break;
580 	ulong -= ulong / 3 + 1;
581     }
582     uint64 = (ErlNifUInt64)-1; /* UINT64_MAX */
583     for (;;) {
584 	if (!test_uint64(env,uint64)) {
585 	    goto error;
586 	}
587 	if (uint64 == 0) break;
588 	uint64 -= uint64 / 3 + 1;
589     }
590 
591     if (MAX_SMALL < INT_MAX) { /* 32-bit */
592 	for (i=-10 ; i <= 10; i++) {
593 	    if (!test_int(env,MAX_SMALL+i)) {
594 		goto error;
595 	    }
596 	}
597 	for (i=-10 ; i <= 10; i++) {
598 	    if (!test_int(env,MIN_SMALL+i)) {
599 		goto error;
600 	    }
601 	}
602 	for (i=-10 ; i <= 10; i++) {
603 	    if (!test_uint(env,MAX_SMALL+i)) {
604 		goto error;
605 	    }
606 	}
607     }
608     assert((MAX_SMALL < INT_MAX) == (MIN_SMALL > INT_MIN));
609 
610     for (i=-10 ; i < 10; i++) {
611 	if (!test_long(env,MAX_SMALL+i) || !test_ulong(env,MAX_SMALL+i) ||
612 	    !test_long(env,MIN_SMALL+i) ||
613 	    !test_int64(env,MAX_SMALL+i) || !test_uint64(env,MAX_SMALL+i) ||
614 	    !test_int64(env,MIN_SMALL+i)) {
615 	    goto error;
616 	}
617 	if (MAX_SMALL < INT_MAX) {
618 	    if (!test_int(env,MAX_SMALL+i) || !test_uint(env,MAX_SMALL+i) ||
619 		!test_int(env,MIN_SMALL+i)) {
620 		goto error;
621 	    }
622 	}
623     }
624     for (d=3.141592e-100 ; d < 1e100 ; d *= 9.97) {
625 	if (!test_double(env,d) || !test_double(env,-d)) {
626 	    goto error;
627 	}
628     }
629 
630     if (!enif_make_existing_atom(env,"nif_SUITE", &atom, ERL_NIF_LATIN1)
631 	|| !enif_is_identical(atom,enif_make_atom(env,"nif_SUITE"))) {
632 	fprintf(stderr, "nif_SUITE not an atom?\r\n");
633 	goto error;
634     }
635     for (i=2; i; i--) {
636 	if (enif_make_existing_atom(env,"nif_SUITE_pink_unicorn", &atom, ERL_NIF_LATIN1)) {
637 	    fprintf(stderr, "pink unicorn exist?\r\n");
638 	    goto error;
639 	}
640     }
641 
642     ref1 = enif_make_ref(env);
643     ref2 = enif_make_ref(env);
644     if (!enif_is_ref(env,ref1) || !enif_is_ref(env,ref2)
645 	|| enif_is_identical(ref1,ref2) || enif_compare(ref1,ref2)==0) {
646 	fprintf(stderr, "strange refs?\r\n");
647 	goto error;
648     }
649     return enif_make_atom(env,"ok");
650 
651 error:
652     return enif_make_atom(env,"error");
653 }
654 
echo_int(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])655 static ERL_NIF_TERM echo_int(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
656 {
657     int sint;
658     unsigned uint;
659     long slong;
660     unsigned long ulong;
661     ErlNifSInt64 sint64;
662     ErlNifUInt64 uint64;
663     ERL_NIF_TERM sint_term = atom_false,   uint_term = atom_false;
664     ERL_NIF_TERM slong_term = atom_false,  ulong_term = atom_false;
665     ERL_NIF_TERM sint64_term = atom_false, uint64_term = atom_false;
666 
667     if (enif_get_int(env, argv[0], &sint)) {
668 	sint_term = enif_make_int(env, sint);
669     }
670     if (enif_get_uint(env, argv[0], &uint)) {
671 	uint_term = enif_make_uint(env, uint);
672     }
673     if (enif_get_long(env, argv[0], &slong)) {
674 	slong_term = enif_make_long(env, slong);
675     }
676     if (enif_get_ulong(env, argv[0], &ulong)) {
677 	ulong_term = enif_make_ulong(env, ulong);
678     }
679     if (enif_get_int64(env, argv[0], &sint64)) {
680 	sint64_term = enif_make_int64(env, sint64);
681     }
682     if (enif_get_uint64(env, argv[0], &uint64)) {
683 	uint64_term = enif_make_uint64(env, uint64);
684     }
685     return enif_make_list6(env, sint_term, uint_term, slong_term, ulong_term, sint64_term, uint64_term);
686 }
687 
type_sizes(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])688 static ERL_NIF_TERM type_sizes(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
689 {
690     return enif_make_tuple2(env, enif_make_int(env, sizeof(int)),
691 			    enif_make_int(env, sizeof(long)));
692 }
693 
tuple_2_list(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])694 static ERL_NIF_TERM tuple_2_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
695 {
696     int arity = -1;
697     const ERL_NIF_TERM* ptr;
698     ERL_NIF_TERM list = enif_make_list(env,0);
699 
700     if (argc!=1 || !enif_get_tuple(env,argv[0],&arity,&ptr)) {
701 	return enif_make_badarg(env);
702     }
703     while (--arity >= 0) {
704 	list = enif_make_list_cell(env,ptr[arity],list);
705     }
706     return list;
707 }
708 
is_identical(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])709 static ERL_NIF_TERM is_identical(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
710 {
711     assert(argc == 2);
712     return enif_make_atom(env, (enif_is_identical(argv[0],argv[1]) ?
713 				"true" : "false"));
714 }
715 
compare(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])716 static ERL_NIF_TERM compare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
717 {
718     assert(argc == 2);
719     return enif_make_int(env, enif_compare(argv[0],argv[1]));
720 }
721 
hash_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])722 static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
723 {
724     ErlNifHash type;
725     ErlNifUInt64 salt;
726 
727     assert(argc == 3);
728     if (enif_is_identical(argv[0], enif_make_atom(env, "internal"))) {
729         type = ERL_NIF_INTERNAL_HASH;
730     }
731     else if (enif_is_identical(argv[0], enif_make_atom(env, "phash2"))) {
732         type = ERL_NIF_PHASH2;
733     }
734     else {
735         return enif_make_badarg(env);
736     }
737 
738     if (! enif_get_uint64(env, argv[2], &salt)) {
739         return enif_make_badarg(env);
740     }
741 
742     return enif_make_uint64(env, enif_hash(type, argv[1], salt));
743 }
744 
many_args_100(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])745 static ERL_NIF_TERM many_args_100(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
746 {
747     int i, k;
748     if (argc == 100) {
749 	for (i=1; i<=100; i++) {
750 	    if (!enif_get_int(env,argv[i-1],&k) || k!=i) {
751 		goto badarg;
752 	    }
753 	}
754 	return enif_make_atom(env,"ok");
755     }
756 badarg:
757     return enif_make_badarg(env);
758 }
759 
clone_bin(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])760 static ERL_NIF_TERM clone_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
761 {
762     ErlNifBinary ibin;
763     if (enif_inspect_binary(env,argv[0],&ibin)) {
764 	ERL_NIF_TERM obin;
765 	memcpy(enif_make_new_binary(env, ibin.size, &obin),
766 	       ibin.data, ibin.size);
767 	return obin;
768     }
769     else {
770 	return enif_make_badarg(env);
771     }
772 }
773 
make_sub_bin(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])774 static ERL_NIF_TERM make_sub_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
775 {
776     int pos, size;
777     if (!enif_get_int(env,argv[1],&pos) || !enif_get_int(env,argv[2],&size)) {
778 	return enif_make_badarg(env);
779     }
780     return enif_make_sub_binary(env,argv[0],pos,size);
781 }
782 
string_to_bin(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])783 static ERL_NIF_TERM string_to_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
784 {
785     ErlNifBinary obin;
786     unsigned size;
787     int n;
788     if (!enif_get_int(env,argv[1],(int*)&size)
789 	|| !enif_alloc_binary(size,&obin)) {
790 	return enif_make_badarg(env);
791     }
792     n = enif_get_string(env, argv[0], (char*)obin.data, size, ERL_NIF_LATIN1);
793     return enif_make_tuple(env, 2, enif_make_int(env,n),
794 			   enif_make_binary(env,&obin));
795 }
796 
atom_to_bin(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])797 static ERL_NIF_TERM atom_to_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
798 {
799     ErlNifBinary obin;
800     unsigned size;
801     int n;
802     if (!enif_get_int(env,argv[1],(int*)&size)
803 	|| !enif_alloc_binary(size,&obin)) {
804 	return enif_make_badarg(env);
805     }
806     n = enif_get_atom(env, argv[0], (char*)obin.data, size, ERL_NIF_LATIN1);
807     return enif_make_tuple(env, 2, enif_make_int(env,n),
808 			   enif_make_binary(env,&obin));
809 }
810 
macros(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])811 static ERL_NIF_TERM macros(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
812 {
813     const ERL_NIF_TERM* a;
814     ERL_NIF_TERM lists, tuples;
815     int arity;
816     if (!enif_get_tuple(env, argv[0], &arity, &a) || arity != 9) {
817 	return enif_make_badarg(env);
818     }
819 
820     lists = enif_make_list(env,9,
821 			   enif_make_list1(env,a[0]),
822 			   enif_make_list2(env,a[0],a[1]),
823 			   enif_make_list3(env,a[0],a[1],a[2]),
824 			   enif_make_list4(env,a[0],a[1],a[2],a[3]),
825 			   enif_make_list5(env,a[0],a[1],a[2],a[3],a[4]),
826 			   enif_make_list6(env,a[0],a[1],a[2],a[3],a[4],a[5]),
827 			   enif_make_list7(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6]),
828 			   enif_make_list8(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),
829 			   enif_make_list9(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]));
830     tuples = enif_make_list(env,9,
831 			    enif_make_tuple1(env,a[0]),
832 			    enif_make_tuple2(env,a[0],a[1]),
833 			    enif_make_tuple3(env,a[0],a[1],a[2]),
834 			    enif_make_tuple4(env,a[0],a[1],a[2],a[3]),
835 			    enif_make_tuple5(env,a[0],a[1],a[2],a[3],a[4]),
836 			    enif_make_tuple6(env,a[0],a[1],a[2],a[3],a[4],a[5]),
837 			    enif_make_tuple7(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6]),
838 			    enif_make_tuple8(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),
839 			    enif_make_tuple9(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]));
840     return enif_make_tuple2(env,lists,tuples);
841 }
842 
tuple_2_list_and_tuple(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])843 static ERL_NIF_TERM tuple_2_list_and_tuple(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
844 {
845     const ERL_NIF_TERM* arr;
846     int arity;
847     if (!enif_get_tuple(env,argv[0],&arity,&arr)) {
848 	return enif_make_badarg(env);
849     }
850     return enif_make_tuple2(env,
851 			    enif_make_list_from_array(env, arr, arity),
852 			    enif_make_tuple_from_array(env, arr, arity));
853 }
854 
iolist_2_bin(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])855 static ERL_NIF_TERM iolist_2_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
856 {
857     ErlNifBinary obin;
858     if (!enif_inspect_iolist_as_binary(env, argv[0], &obin)) {
859 	return enif_make_badarg(env);
860     }
861     return enif_make_binary(env,&obin);
862 }
863 
last_resource_dtor_call_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])864 static ERL_NIF_TERM last_resource_dtor_call_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
865 {
866     ERL_NIF_TERM ret;
867     if (resource_dtor_last != NULL) {
868 	ERL_NIF_TERM bin;
869 	memcpy(enif_make_new_binary(env, resource_dtor_last_sz, &bin),
870 	       resource_dtor_last_data, resource_dtor_last_sz);
871 	ret = enif_make_tuple3(env,
872 				make_pointer(env, resource_dtor_last),
873 				bin,
874 				enif_make_int(env, resource_dtor_cnt));
875     }
876     else {
877 	ret = enif_make_list(env,0);
878     }
879     resource_dtor_last = NULL;
880     resource_dtor_last_sz = 0;
881     resource_dtor_cnt = 0;
882     return ret;
883 }
884 
get_resource_type(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])885 static ERL_NIF_TERM get_resource_type(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
886 {
887     PrivData* data = (PrivData*) enif_priv_data(env);
888     int ix;
889 
890     if (!enif_get_int(env, argv[0], &ix) || ix >= 2) {
891 	return enif_make_badarg(env);
892     }
893     return make_pointer(env, data->rt_arr[ix].vp);
894 }
895 
alloc_resource(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])896 static ERL_NIF_TERM alloc_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
897 {
898     ErlNifBinary data_bin;
899     union { ErlNifResourceType* t; void* vp; } type;
900     void* data;
901     if (!get_pointer(env, argv[0], &type.vp)
902 	|| !enif_inspect_binary(env, argv[1], &data_bin)
903 	|| (data = enif_alloc_resource(type.t, data_bin.size))==NULL) {
904 
905 	return enif_make_badarg(env);
906     }
907     memcpy(data, data_bin.data, data_bin.size);
908     return make_pointer(env, data);
909 }
910 
make_resource(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])911 static ERL_NIF_TERM make_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
912 {
913     void* data;
914     if (!get_pointer(env, argv[0], &data)) {
915 	return enif_make_badarg(env);
916     }
917     return enif_make_resource(env, data);
918 }
919 
make_new_resource(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])920 static ERL_NIF_TERM make_new_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
921 {
922     ErlNifBinary data_bin;
923     union { ErlNifResourceType* t; void* vp; } type;
924     void* data;
925     ERL_NIF_TERM ret;
926     if (!get_pointer(env, argv[0], &type.vp)
927 	|| !enif_inspect_binary(env, argv[1], &data_bin)
928 	|| (data = enif_alloc_resource(type.t, data_bin.size))==NULL) {
929 
930 	return enif_make_badarg(env);
931     }
932     ret = enif_make_resource(env, data);
933     memcpy(data, data_bin.data, data_bin.size);
934     enif_release_resource(data);
935     return ret;
936 }
937 
make_new_resource_binary(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])938 static ERL_NIF_TERM make_new_resource_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
939 {
940     ErlNifBinary data_bin;
941     union { struct binary_resource* p; void* vp; } br;
942     void* buf;
943     ERL_NIF_TERM ret;
944     if (!enif_inspect_binary(env, argv[0], &data_bin)
945 	|| (br.vp = enif_alloc_resource(binary_resource_type,
946 					sizeof(struct binary_resource)))==NULL
947 	|| (buf = enif_alloc(data_bin.size)) == NULL) {
948 
949 	return enif_make_badarg(env);
950     }
951     memset(br.vp,0xba,sizeof(struct binary_resource)); /* avoid valgrind warning */
952     br.p->data = buf;
953     br.p->size = data_bin.size;
954     memcpy(br.p->data, data_bin.data, data_bin.size);
955     ret = enif_make_resource_binary(env, br.vp, br.p->data, br.p->size);
956     enif_release_resource(br.p);
957     return enif_make_tuple2(env, make_pointer(env,br.vp), ret);
958 }
959 
binary_resource_dtor(ErlNifEnv * env,void * obj)960 static void binary_resource_dtor(ErlNifEnv* env, void* obj)
961 {
962     struct binary_resource* br = (struct binary_resource*) obj;
963     resource_dtor(env,obj);
964     assert(br->data != NULL);
965     enif_free(br->data);
966     br->data = NULL;
967 }
968 
get_resource(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])969 static ERL_NIF_TERM get_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
970 {
971     ErlNifBinary data_bin;
972     union { ErlNifResourceType* t; void* vp; } type;
973     void* data;
974 
975     type.t = NULL;
976     if (enif_is_identical(argv[0], atom_binary_resource_type)) {
977 	type.t = binary_resource_type;
978     }
979     else if (enif_is_identical(argv[0], atom_monitor_resource_type)) {
980 	type.t = monitor_resource_type;
981     }
982     else {
983 	get_pointer(env, argv[0], &type.vp);
984     }
985     if (type.t == NULL
986 	|| !enif_get_resource(env, argv[1], type.t, &data)) {
987 	return enif_make_badarg(env);
988     }
989     enif_alloc_binary(enif_sizeof_resource(data), &data_bin);
990     memcpy(data_bin.data, data, data_bin.size);
991     return enif_make_tuple2(env, make_pointer(env,data),
992 			    enif_make_binary(env, &data_bin));
993 }
994 
release_resource(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])995 static ERL_NIF_TERM release_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
996 {
997     void* data;
998     if (!get_pointer(env, argv[0], &data)) {
999 	return enif_make_badarg(env);
1000     }
1001     enif_release_resource(data);
1002     return enif_make_atom(env,"ok");
1003 }
1004 
threaded_release_resource(void * resource)1005 static void* threaded_release_resource(void* resource)
1006 {
1007     enif_release_resource(resource);
1008     return NULL;
1009 }
1010 
release_resource_from_thread(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1011 static ERL_NIF_TERM release_resource_from_thread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1012 {
1013     void* resource;
1014     ErlNifTid tid;
1015     int err;
1016 
1017     if (!get_pointer(env, argv[0], &resource)) {
1018         return enif_make_badarg(env);
1019     }
1020     if (enif_thread_create("nif_SUITE:release_resource_from_thread", &tid,
1021                            threaded_release_resource, resource, NULL) != 0) {
1022         return enif_make_badarg(env);
1023     }
1024     err = enif_thread_join(tid, NULL);
1025     assert(err == 0);
1026     return atom_ok;
1027 }
1028 
1029 
1030 /*
1031  * argv[0] an atom
1032  * argv[1] a binary
1033  * argv[2] a ref
1034  * argv[3] 'ok'
1035  * argv[4] a fun
1036  * argv[5] a pid
1037  * argv[6] a port
1038  * argv[7] an empty list
1039  * argv[8] a non-empty list
1040  * argv[9] a tuple
1041  * argv[10] a number (small, big integer or float)
1042  */
check_is(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1043 static ERL_NIF_TERM check_is(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1044 {
1045     ERL_NIF_TERM ok_atom = enif_make_atom(env, "ok");
1046 
1047     if (!enif_is_atom(env, argv[0])) return enif_make_badarg(env);
1048     if (!enif_is_binary(env, argv[1])) return enif_make_badarg(env);
1049     if (!enif_is_ref(env, argv[2])) return enif_make_badarg(env);
1050     if (!enif_is_identical(argv[3], ok_atom)) return enif_make_badarg(env);
1051     if (!enif_is_fun(env, argv[4])) return enif_make_badarg(env);
1052     if (!enif_is_pid(env, argv[5])) return enif_make_badarg(env);
1053     if (!enif_is_port(env, argv[6])) return enif_make_badarg(env);
1054     if (!enif_is_empty_list(env, argv[7])) return enif_make_badarg(env);
1055     if (!enif_is_list(env, argv[7])) return enif_make_badarg(env);
1056     if (!enif_is_list(env, argv[8])) return enif_make_badarg(env);
1057     if (!enif_is_tuple(env, argv[9])) return enif_make_badarg(env);
1058     if (!enif_is_number(env, argv[10])) return enif_make_badarg(env);
1059 
1060     return ok_atom;
1061 }
1062 
1063 /*
1064  * no arguments
1065  *
1066  * This function is separate from check_is because it calls enif_make_badarg
1067  * and so it must return the badarg exception as its return value. Thus, the
1068  * badarg exception indicates success.
1069  */
check_is_exception(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1070 static ERL_NIF_TERM check_is_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1071 {
1072     ERL_NIF_TERM badarg, exc_term;
1073     ERL_NIF_TERM error_atom = enif_make_atom(env, "error");
1074     ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg");
1075     assert(!enif_is_exception(env, error_atom));
1076     badarg = enif_make_badarg(env);
1077     assert(enif_is_exception(env, badarg));
1078     assert(enif_has_pending_exception(env, NULL));
1079     assert(enif_has_pending_exception(env, &exc_term));
1080     assert(enif_is_identical(exc_term, badarg_atom));
1081     return badarg;
1082 }
1083 
1084 /*
1085  * argv[0] atom with length of 6
1086  * argv[1] list with length of 6
1087  * argv[2] empty list
1088  * argv[3] not an atom
1089  * argv[4] not a list
1090  * argv[5] improper list
1091  */
length_test(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1092 static ERL_NIF_TERM length_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1093 {
1094     unsigned len;
1095 
1096     if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1) || len != 6)
1097 	return enif_make_badarg(env);
1098 
1099     if (!enif_get_list_length(env, argv[1], &len) || len != 6)
1100 	return enif_make_badarg(env);
1101 
1102     if (!enif_get_list_length(env, argv[2], &len) || len != 0)
1103 	return enif_make_badarg(env);
1104 
1105     if (enif_get_atom_length(env, argv[3], &len, ERL_NIF_LATIN1))
1106 	return enif_make_badarg(env);
1107 
1108     if (enif_get_list_length(env, argv[4], &len))
1109 	return enif_make_badarg(env);
1110 
1111     if (enif_get_list_length(env, argv[5], &len))
1112 	return enif_make_badarg(env);
1113 
1114     return enif_make_atom(env, "ok");
1115 }
1116 
make_atoms(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1117 static ERL_NIF_TERM make_atoms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1118 {
1119     ERL_NIF_TERM arr[7];
1120     ERL_NIF_TERM existingatom0a, existingatom0b;
1121     ERL_NIF_TERM existing0atom0;
1122     const char * const an0atom = "an0atom";
1123     const char an0atom0[8] = {'a','n','\0','a','t','o','m',0};
1124 
1125     arr[0] = enif_make_atom(env, "an0atom");
1126     arr[1] = enif_make_atom_len(env, "an0atom", 7);
1127     arr[2] = enif_make_atom_len(env, an0atom, 7);
1128     arr[3] = enif_make_atom_len(env, an0atom0, 8);
1129 
1130     if (!enif_make_existing_atom(env, "an0atom", &existingatom0a, ERL_NIF_LATIN1))
1131 	return enif_make_atom(env, "error");
1132     arr[4] = existingatom0a;
1133 
1134     if (!enif_make_existing_atom_len(env, an0atom, 7, &existingatom0b, ERL_NIF_LATIN1))
1135 	return enif_make_atom(env, "error");
1136     arr[5] = existingatom0b;
1137 
1138     if (!enif_make_existing_atom_len(env, an0atom0, 8, &existing0atom0, ERL_NIF_LATIN1))
1139 	return enif_make_atom(env, "error");
1140     arr[6] = existing0atom0;
1141 
1142     return enif_make_tuple7(env,
1143 			    arr[0],arr[1],arr[2],arr[3],arr[4],arr[5],arr[6]);
1144 }
1145 
make_strings(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1146 static ERL_NIF_TERM make_strings(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1147 {
1148     const char a0string[8] = {'a','0','s','t','r','i','n','g'};
1149     const char a0string0[9] = {'a','\0','s','t','r','i','n','g',0};
1150     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};
1151 
1152     return enif_make_tuple5(env,
1153 			    enif_make_string(env, "a0string", ERL_NIF_LATIN1),
1154 			    enif_make_string_len(env, "a0string", 8, ERL_NIF_LATIN1),
1155 			    enif_make_string_len(env, a0string, 8, ERL_NIF_LATIN1),
1156 			    enif_make_string_len(env, a0string0, 9, ERL_NIF_LATIN1),
1157 			    enif_make_string(env, astringwith8bits, ERL_NIF_LATIN1));
1158 }
send_list_seq(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1159 static ERL_NIF_TERM send_list_seq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1160 {
1161     ErlNifPid to;
1162     ERL_NIF_TERM msg;
1163     ErlNifEnv* msg_env;
1164     int i, res;
1165 
1166     if (!enif_get_int(env, argv[0], &i)) {
1167 	return enif_make_badarg(env);
1168     }
1169     if (argv[1] == atom_self) {
1170 	enif_self(env, &to);
1171     }
1172     else if (!enif_get_local_pid(env, argv[1], &to)) {
1173 	return enif_make_badarg(env);
1174     }
1175     msg_env = enif_alloc_env();
1176     msg = enif_make_list(msg_env,0);
1177     for ( ; i>0 ; i--) {
1178 	msg = enif_make_list_cell(msg_env, enif_make_int(msg_env, i), msg);
1179     }
1180     res = enif_send(env, &to, msg_env, msg);
1181     enif_free_env(msg_env);
1182     return enif_make_tuple2(env, atom_ok, enif_make_int(env,res));
1183 }
1184 
fill(void * dst,unsigned bytes,int seed)1185 static void fill(void* dst, unsigned bytes, int seed)
1186 {
1187     unsigned char* ptr = dst;
1188     int i;
1189     for (i=bytes; i>0; i--) {
1190 	*ptr++ = seed;
1191 	seed += 7;
1192     }
1193 }
1194 
1195 /* enif_whereis_... tests */
1196 
1197 enum {
1198     /* results */
1199     WHEREIS_SUCCESS,
1200     WHEREIS_ERROR_LOOKUP,
1201     WHEREIS_ERROR_SEND,
1202     /* types */
1203     WHEREIS_LOOKUP_PID,     /* enif_whereis_pid() */
1204     WHEREIS_LOOKUP_PORT     /* enif_whereis_port() */
1205 };
1206 
1207 typedef union {
1208     ErlNifPid   pid;
1209     ErlNifPort  port;
1210 } whereis_term_data_t;
1211 
1212 /* single use, no cross-thread access/serialization */
1213 typedef struct {
1214     ErlNifEnv* env;
1215     ERL_NIF_TERM name;
1216     whereis_term_data_t res;
1217     ErlNifTid tid;
1218     int type;
1219     int rc;
1220     ERL_NIF_TERM dtor_msg;
1221 } whereis_thread_resource_t;
1222 
whereis_thread_resource_create(void)1223 static whereis_thread_resource_t* whereis_thread_resource_create(void)
1224 {
1225     whereis_thread_resource_t* rp = (whereis_thread_resource_t*)
1226         enif_alloc_resource(whereis_resource_type, sizeof(*rp));
1227     memset(rp, 0, sizeof(*rp));
1228     rp->env = enif_alloc_env();
1229 
1230     return rp;
1231 }
1232 
1233 static int whereis_lookup_internal(ErlNifEnv*, int type, ERL_NIF_TERM name,
1234                                    whereis_term_data_t* out);
1235 static int whereis_send_internal(ErlNifEnv*, int type, whereis_term_data_t* to,
1236                                  ERL_NIF_TERM msg);
1237 
1238 
whereis_thread_resource_dtor(ErlNifEnv * env,void * obj)1239 static void whereis_thread_resource_dtor(ErlNifEnv* env, void* obj)
1240 {
1241     whereis_thread_resource_t* rp = (whereis_thread_resource_t*) obj;
1242     whereis_term_data_t to;
1243 
1244     if (whereis_lookup_internal(env, rp->type, rp->name, &to)
1245         == WHEREIS_SUCCESS) {
1246         whereis_send_internal(env, rp->type, &to, rp->dtor_msg);
1247     }
1248     enif_free_env(rp->env);
1249 }
1250 
whereis_type(ERL_NIF_TERM type_term,int * type_p)1251 static int whereis_type(ERL_NIF_TERM type_term, int* type_p)
1252 {
1253     if (enif_is_identical(type_term, atom_pid)) {
1254         *type_p = WHEREIS_LOOKUP_PID;
1255         return 1;
1256     }
1257     if (enif_is_identical(type_term, atom_port)) {
1258         *type_p = WHEREIS_LOOKUP_PORT;
1259         return 1;
1260     }
1261     return 0;
1262 }
1263 
whereis_lookup_internal(ErlNifEnv * env,int type,ERL_NIF_TERM name,whereis_term_data_t * out)1264 static int whereis_lookup_internal(
1265     ErlNifEnv* env, int type, ERL_NIF_TERM name, whereis_term_data_t* out)
1266 {
1267     if (type == WHEREIS_LOOKUP_PID)
1268         return enif_whereis_pid(env, name, & out->pid)
1269             ? WHEREIS_SUCCESS : WHEREIS_ERROR_LOOKUP;
1270 
1271     if (type == WHEREIS_LOOKUP_PORT)
1272         return enif_whereis_port(env, name, & out->port)
1273             ? WHEREIS_SUCCESS : WHEREIS_ERROR_LOOKUP;
1274 
1275     abort();
1276 }
1277 
whereis_send_internal(ErlNifEnv * env,int type,whereis_term_data_t * to,ERL_NIF_TERM msg)1278 static int whereis_send_internal(
1279     ErlNifEnv* env, int type, whereis_term_data_t* to, ERL_NIF_TERM msg)
1280 {
1281     if (type == WHEREIS_LOOKUP_PID)
1282         return enif_send(env, & to->pid, NULL, msg)
1283             ? WHEREIS_SUCCESS : WHEREIS_ERROR_SEND;
1284 
1285     if (type == WHEREIS_LOOKUP_PORT)
1286         return enif_port_command(env, & to->port, NULL, msg)
1287             ? WHEREIS_SUCCESS : WHEREIS_ERROR_SEND;
1288 
1289     abort();
1290 }
1291 
whereis_resolved_term(ErlNifEnv * env,int type,whereis_term_data_t * res)1292 static ERL_NIF_TERM whereis_resolved_term(
1293     ErlNifEnv* env, int type, whereis_term_data_t* res)
1294 {
1295     switch (type) {
1296         case WHEREIS_LOOKUP_PID:
1297             return enif_make_pid(env, &res->pid);
1298         case WHEREIS_LOOKUP_PORT:
1299             return enif_make_port(env, &res->port);
1300         default:
1301             abort();
1302     }
1303 }
1304 
whereis_result_term(ErlNifEnv * env,int result)1305 static ERL_NIF_TERM whereis_result_term(ErlNifEnv* env, int result)
1306 {
1307     ERL_NIF_TERM err;
1308     switch (result)
1309     {
1310         case WHEREIS_SUCCESS:
1311             return atom_ok;
1312         case WHEREIS_ERROR_LOOKUP:
1313             err = atom_lookup;
1314             break;
1315         case WHEREIS_ERROR_SEND:
1316             err = atom_send;
1317             break;
1318         default:
1319             err = enif_make_int(env, -result);
1320             break;
1321     }
1322     return enif_make_tuple2(env, atom_error, err);
1323 }
1324 
whereis_lookup_thread(void * arg)1325 static void* whereis_lookup_thread(void* arg)
1326 {
1327     whereis_thread_resource_t* rp = (whereis_thread_resource_t*) arg;
1328 
1329     rp->rc = whereis_lookup_internal(NULL, rp->type, rp->name, &rp->res);
1330 
1331     return NULL;
1332 }
1333 
1334 /* whereis_term(Type, Name) -> pid() | port() | false */
1335 static ERL_NIF_TERM
whereis_term(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1336 whereis_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1337 {
1338     whereis_term_data_t res;
1339     int type, rc;
1340 
1341     assert(argc == 2);
1342     if (!whereis_type(argv[0], &type))
1343         return enif_make_badarg(env);
1344 
1345     rc = whereis_lookup_internal(env, type, argv[1], & res);
1346     return (rc == WHEREIS_SUCCESS ?
1347             whereis_resolved_term(env, type, &res) :
1348             atom_false);
1349 }
1350 
1351 /* whereis_send(Type, Name, Message) -> ok | {error, Reason} */
1352 static ERL_NIF_TERM
whereis_send(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1353 whereis_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1354 {
1355     whereis_term_data_t to;
1356     int type, rc;
1357 
1358     assert(argc == 3);
1359     if (!enif_is_atom(env, argv[1]))
1360         return enif_make_badarg(env);
1361 
1362     if (!whereis_type(argv[0], &type))
1363         return enif_make_badarg(env);
1364 
1365     rc = whereis_lookup_internal(env, type, argv[1], & to);
1366     if (rc == WHEREIS_SUCCESS)
1367         rc = whereis_send_internal(env, type, & to, argv[2]);
1368 
1369     return whereis_result_term(env, rc);
1370 }
1371 
1372 /* whereis_thd_lookup(Type, Name, DtorMsg) -> {ok, Resource} | {error, SysErrno} */
1373 static ERL_NIF_TERM
whereis_thd_lookup(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1374 whereis_thd_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1375 {
1376     whereis_thread_resource_t* rp;
1377     int type, rc;
1378     ERL_NIF_TERM ret;
1379 
1380     assert(argc == 3);
1381     if (!enif_is_atom(env, argv[1]))
1382         return enif_make_badarg(env);
1383 
1384     if (!whereis_type(argv[0], &type))
1385         return enif_make_badarg(env);
1386 
1387     rp = whereis_thread_resource_create();
1388     rp->type = type;
1389     rp->name = enif_make_copy(rp->env, argv[1]);
1390     rp->dtor_msg = enif_make_copy(rp->env, argv[2]);
1391 
1392     rc = enif_thread_create(
1393         "nif_SUITE:whereis_thd", & rp->tid, whereis_lookup_thread, rp, NULL);
1394 
1395     if (rc == 0)
1396         ret = enif_make_tuple2(env, atom_ok, enif_make_resource(env, rp));
1397     else
1398         ret = enif_make_tuple2(env, atom_error, enif_make_int(env, rc));
1399     enif_release_resource(rp);
1400     return ret;
1401 }
1402 
1403 /* whereis_thd_result(Resource) -> {ok, pid() | port()} | {error, ErrNum} */
1404 static ERL_NIF_TERM
whereis_thd_result(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1405 whereis_thd_result(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1406 {
1407     whereis_thread_resource_t* rp;
1408     ERL_NIF_TERM ret;
1409     int join_rc;
1410 
1411     assert(argc == 1);
1412     if (!enif_get_resource(env, argv[0], whereis_resource_type, (void**) & rp))
1413         return enif_make_badarg(env);
1414 
1415     if ((join_rc = enif_thread_join(rp->tid, NULL)) != 0)
1416         return enif_make_tuple2(env, atom_error, enif_make_int(env, join_rc));
1417 
1418     if (rp->rc == WHEREIS_SUCCESS) {
1419         ret = enif_make_tuple2(env, atom_ok,
1420                                whereis_resolved_term(env, rp->type, &rp->res));
1421     }
1422     else
1423         ret = whereis_result_term(env, rp->rc);
1424 
1425     return ret;
1426 }
1427 
1428 #define MAKE_TERM_REUSE_LEN 16
1429 struct make_term_info
1430 {
1431     ErlNifEnv* caller_env;
1432     ErlNifEnv* dst_env;
1433     int dst_env_valid;
1434     ERL_NIF_TERM reuse[MAKE_TERM_REUSE_LEN];
1435     unsigned reuse_push;
1436     unsigned reuse_pull;
1437     ErlNifResourceType* resource_type;
1438     void *resource;
1439     ERL_NIF_TERM other_term;
1440     ERL_NIF_TERM blob;
1441     ErlNifPid to_pid;
1442     ErlNifTid tid;
1443     ErlNifCond* cond;
1444     ErlNifMutex* mtx;
1445     int send_it;
1446     int send_res;
1447     unsigned n;
1448 };
1449 
1450 
push_term(struct make_term_info * mti,ERL_NIF_TERM term)1451 static void push_term(struct make_term_info* mti, ERL_NIF_TERM term)
1452 {
1453     unsigned ix = (mti->reuse_push++) % MAKE_TERM_REUSE_LEN;
1454     mti->reuse[ix] = term;
1455     //enif_fprintf(stderr, "push at %u: %T\r\n", ix, term);
1456 }
pull_term(struct make_term_info * mti)1457 static ERL_NIF_TERM pull_term(struct make_term_info* mti)
1458 {
1459     unsigned ix;
1460     if (mti->reuse_pull >= mti->reuse_push &&
1461 	mti->reuse_push < MAKE_TERM_REUSE_LEN) {
1462 	mti->reuse_pull = 0;
1463 	if (mti->reuse_push == 0) {
1464             assert(mti->dst_env_valid);
1465 	    mti->reuse[0] = enif_make_list(mti->dst_env, 0);
1466 	}
1467     }
1468     ix = (mti->reuse_pull++) % MAKE_TERM_REUSE_LEN;
1469     //enif_fprintf(stderr, "pull from %u: %T\r\n", ix, mti->reuse[ix]);
1470     return mti->reuse[ix];
1471 }
1472 
1473 static int make_term_n(struct make_term_info* mti, int n, ERL_NIF_TERM* res);
1474 
make_term_binary(struct make_term_info * mti,int n)1475 static ERL_NIF_TERM make_term_binary(struct make_term_info* mti, int n)
1476 {
1477     ErlNifBinary bin;
1478     enif_alloc_binary(100, &bin);
1479     fill(bin.data, bin.size, n);
1480     return enif_make_binary(mti->dst_env, &bin);
1481 }
1482 
make_term_int(struct make_term_info * mti,int n)1483 static ERL_NIF_TERM make_term_int(struct make_term_info* mti, int n)
1484 {
1485     int i;
1486     fill(&i, sizeof(i), n);
1487     return enif_make_int(mti->dst_env, i);
1488 }
1489 
make_term_ulong(struct make_term_info * mti,int n)1490 static ERL_NIF_TERM make_term_ulong(struct make_term_info* mti, int n)
1491 {
1492     unsigned long ul;
1493     fill(&ul, sizeof(ul), n);
1494     return enif_make_ulong(mti->dst_env, ul);
1495 }
1496 
make_term_double(struct make_term_info * mti,int n)1497 static ERL_NIF_TERM make_term_double(struct make_term_info* mti, int n)
1498 {
1499     double d = 3.141592;
1500     return enif_make_double(mti->dst_env, d);
1501 }
make_term_atom(struct make_term_info * mti,int n)1502 static ERL_NIF_TERM make_term_atom(struct make_term_info* mti, int n)
1503 {
1504     return enif_make_atom(mti->dst_env, "make_term_n");
1505 }
make_term_existing_atom(struct make_term_info * mti,int n)1506 static ERL_NIF_TERM make_term_existing_atom(struct make_term_info* mti, int n)
1507 {
1508     ERL_NIF_TERM res;
1509     int exist = enif_make_existing_atom(mti->dst_env, "nif_SUITE", &res,
1510 					ERL_NIF_LATIN1);
1511     assert(exist);
1512     return res;
1513 }
make_term_string(struct make_term_info * mti,int n)1514 static ERL_NIF_TERM make_term_string(struct make_term_info* mti, int n)
1515 {
1516     return enif_make_string(mti->dst_env, "Hello!", ERL_NIF_LATIN1);
1517 }
make_term_sub_binary(struct make_term_info * mti,int n)1518 static ERL_NIF_TERM make_term_sub_binary(struct make_term_info* mti, int n)
1519 {
1520     ERL_NIF_TERM orig;
1521     unsigned char* ptr = enif_make_new_binary(mti->dst_env, 10, &orig);
1522     fill(ptr, 10, n);
1523     return enif_make_sub_binary(mti->dst_env, orig, 3, 5);
1524 }
make_term_uint(struct make_term_info * mti,int n)1525 static ERL_NIF_TERM make_term_uint(struct make_term_info* mti, int n)
1526 {
1527     unsigned int ui;
1528     fill(&ui, sizeof(ui), n);
1529     return enif_make_uint(mti->dst_env, ui);
1530 }
make_term_long(struct make_term_info * mti,int n)1531 static ERL_NIF_TERM make_term_long(struct make_term_info* mti, int n)
1532 {
1533     long l;
1534     fill(&l, sizeof(l), n);
1535     return enif_make_long(mti->dst_env, l);
1536 }
make_term_tuple0(struct make_term_info * mti,int n)1537 static ERL_NIF_TERM make_term_tuple0(struct make_term_info* mti, int n)
1538 {
1539     return enif_make_tuple(mti->dst_env, 0);
1540 }
make_term_list0(struct make_term_info * mti,int n)1541 static ERL_NIF_TERM make_term_list0(struct make_term_info* mti, int n)
1542 {
1543     return enif_make_list(mti->dst_env, 0);
1544 }
make_term_resource(struct make_term_info * mti,int n)1545 static ERL_NIF_TERM make_term_resource(struct make_term_info* mti, int n)
1546 {
1547     return enif_make_resource(mti->dst_env, mti->resource);
1548 }
make_term_new_binary(struct make_term_info * mti,int n)1549 static ERL_NIF_TERM make_term_new_binary(struct make_term_info* mti, int n)
1550 {
1551     ERL_NIF_TERM res;
1552     unsigned char* ptr = enif_make_new_binary(mti->dst_env,20,&res);
1553     fill(ptr, 20, n);
1554     return res;
1555 }
make_term_caller_pid(struct make_term_info * mti,int n)1556 static ERL_NIF_TERM make_term_caller_pid(struct make_term_info* mti, int n)
1557 {
1558     ErlNifPid pid;
1559     return enif_make_pid(mti->dst_env, enif_self(mti->caller_env, &pid));
1560 }
1561 
make_term_tuple(struct make_term_info * mti,int n)1562 static ERL_NIF_TERM make_term_tuple(struct make_term_info* mti, int n)
1563 {
1564     ERL_NIF_TERM t[3];
1565     t[0] = pull_term(mti);
1566     t[1] = pull_term(mti);
1567     t[2] = pull_term(mti);
1568     return enif_make_tuple3(mti->dst_env, t[0], t[1], t[2]);
1569 }
make_term_list(struct make_term_info * mti,int n)1570 static ERL_NIF_TERM make_term_list(struct make_term_info* mti, int n)
1571 {
1572     ERL_NIF_TERM t[3];
1573     t[0] = pull_term(mti);
1574     t[1] = pull_term(mti);
1575     t[2] = pull_term(mti);
1576     return enif_make_list3(mti->dst_env, t[0], t[1], t[2]);
1577 }
make_term_list_cell(struct make_term_info * mti,int n)1578 static ERL_NIF_TERM make_term_list_cell(struct make_term_info* mti, int n)
1579 {
1580     ERL_NIF_TERM t[2];
1581     t[0] = pull_term(mti);
1582     t[1] = pull_term(mti);
1583     return enif_make_list_cell(mti->dst_env, t[0], t[1]);
1584 }
make_term_tuple_from_array(struct make_term_info * mti,int n)1585 static ERL_NIF_TERM make_term_tuple_from_array(struct make_term_info* mti, int n)
1586 {
1587     ERL_NIF_TERM t[3];
1588     t[0] = pull_term(mti);
1589     t[1] = pull_term(mti);
1590     t[2] = pull_term(mti);
1591     return enif_make_tuple_from_array(mti->dst_env, t, 3);
1592 }
make_term_list_from_array(struct make_term_info * mti,int n)1593 static ERL_NIF_TERM make_term_list_from_array(struct make_term_info* mti, int n)
1594 {
1595     ERL_NIF_TERM t[3];
1596     t[0] = pull_term(mti);
1597     t[1] = pull_term(mti);
1598     t[2] = pull_term(mti);
1599     return enif_make_list_from_array(mti->dst_env, t, 3);
1600 }
make_term_garbage(struct make_term_info * mti,int n)1601 static ERL_NIF_TERM make_term_garbage(struct make_term_info* mti, int n)
1602 {
1603     (void) enif_make_string(mti->dst_env, "garbage string", ERL_NIF_LATIN1);
1604     return pull_term(mti);
1605 }
make_term_copy(struct make_term_info * mti,int n)1606 static ERL_NIF_TERM make_term_copy(struct make_term_info* mti, int n)
1607 {
1608     return enif_make_copy(mti->dst_env, mti->other_term);
1609 }
1610 
1611 typedef ERL_NIF_TERM Make_term_Func(struct make_term_info*, int);
1612 static Make_term_Func* make_funcs[] = {
1613     make_term_binary,
1614     make_term_int,
1615     make_term_ulong,
1616     make_term_double,
1617     make_term_atom,
1618     make_term_existing_atom,
1619     make_term_string,
1620     make_term_sub_binary,
1621     make_term_uint,
1622     make_term_long,
1623     make_term_tuple0,
1624     make_term_list0,
1625     make_term_resource,
1626     make_term_new_binary,
1627     make_term_caller_pid,
1628     make_term_tuple,
1629     make_term_list,
1630     make_term_list_cell,
1631     make_term_tuple_from_array,
1632     make_term_list_from_array,
1633     make_term_garbage,
1634     make_term_copy
1635 };
num_of_make_funcs()1636 static unsigned num_of_make_funcs()
1637 {
1638     return sizeof(make_funcs)/sizeof(*make_funcs);
1639 }
make_term_n(struct make_term_info * mti,int n,ERL_NIF_TERM * res)1640 static int make_term_n(struct make_term_info* mti, int n, ERL_NIF_TERM* res)
1641 {
1642     if (n < num_of_make_funcs()) {
1643         assert(mti->dst_env_valid);
1644 	*res = make_funcs[n](mti, n);
1645 	push_term(mti, *res);
1646 	return 1;
1647     }
1648     return 0;
1649 }
1650 
1651 
1652 static void
init_make_blob(struct make_term_info * mti,ErlNifEnv * caller_env,ERL_NIF_TERM other_term)1653 init_make_blob(struct make_term_info *mti,
1654 	       ErlNifEnv* caller_env,
1655 	       ERL_NIF_TERM other_term)
1656 {
1657     PrivData* priv = (PrivData*) enif_priv_data(caller_env);
1658     mti->caller_env = caller_env;
1659     mti->resource_type = priv->rt_arr[0].t;
1660     mti->resource = enif_alloc_resource(mti->resource_type, 10);
1661     fill(mti->resource, 10, 17);
1662     mti->other_term = other_term;
1663 }
1664 
1665 static void
fini_make_blob(struct make_term_info * mti)1666 fini_make_blob(struct make_term_info *mti)
1667 {
1668     enif_release_resource(mti->resource);
1669 }
1670 
make_blob(struct make_term_info * mti,ErlNifEnv * dst_env)1671 static ERL_NIF_TERM make_blob(struct make_term_info *mti,
1672 			      ErlNifEnv* dst_env)
1673 {
1674     ERL_NIF_TERM term, list;
1675     int n = 0;
1676 
1677     mti->reuse_push = 0;
1678     mti->reuse_pull = 0;
1679     mti->dst_env = dst_env;
1680     mti->dst_env_valid = 1;
1681 
1682     list = enif_make_list(dst_env, 0);
1683     while (make_term_n(mti, n++, &term)) {
1684 	list = enif_make_list_cell(dst_env, term, list);
1685     }
1686     return list;
1687 }
1688 
send_new_blob(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1689 static ERL_NIF_TERM send_new_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1690 {
1691     ErlNifPid to;
1692     ERL_NIF_TERM msg, copy;
1693     ErlNifEnv* msg_env;
1694     int res;
1695     struct make_term_info mti;
1696 
1697     if (!enif_get_local_pid(env, argv[0], &to)) {
1698 	return enif_make_badarg(env);
1699     }
1700     msg_env = enif_alloc_env();
1701     init_make_blob(&mti, env, argv[1]);
1702     msg = make_blob(&mti,msg_env);
1703     copy = make_blob(&mti,env);
1704     fini_make_blob(&mti);
1705     res = enif_send(env, &to, msg_env, msg);
1706     enif_free_env(msg_env);
1707     return enif_make_tuple3(env, atom_ok, enif_make_int(env,res), copy);
1708 }
1709 
alloc_msgenv(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1710 static ERL_NIF_TERM alloc_msgenv(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1711 {
1712     PrivData* priv = (PrivData*) enif_priv_data(env);
1713     struct make_term_info* mti;
1714     ERL_NIF_TERM ret;
1715 
1716     mti = (struct make_term_info*) enif_alloc_resource(msgenv_resource_type,
1717 						       sizeof(*mti));
1718     mti->caller_env = NULL;
1719     mti->dst_env = enif_alloc_env();
1720     mti->dst_env_valid = 1;
1721     mti->reuse_push = 0;
1722     mti->reuse_pull = 0;
1723     mti->resource_type = priv->rt_arr[0].t;
1724     mti->resource = enif_alloc_resource(mti->resource_type, 10);
1725     fill(mti->resource, 10, 17);
1726     mti->other_term = enif_make_list(mti->dst_env, 0);
1727     mti->blob = enif_make_list(mti->dst_env, 0);
1728     mti->mtx = enif_mutex_create("nif_SUITE:mtx");
1729     mti->cond = enif_cond_create("nif_SUITE:cond");
1730     mti->send_res = 0xcafebabe;
1731     mti->n = 0;
1732     ret = enif_make_resource(env, mti);
1733     enif_release_resource(mti);
1734     return ret;
1735 }
1736 
msgenv_dtor(ErlNifEnv * env,void * obj)1737 static void msgenv_dtor(ErlNifEnv* env, void* obj)
1738 {
1739     struct make_term_info* mti = (struct make_term_info*) obj;
1740     if (mti->dst_env != NULL) {
1741 	enif_free_env(mti->dst_env);
1742     }
1743     enif_release_resource(mti->resource);
1744     enif_mutex_destroy(mti->mtx);
1745     enif_cond_destroy(mti->cond);
1746 }
1747 
clear_msgenv(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1748 static ERL_NIF_TERM clear_msgenv(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1749 {
1750     mti_t mti;
1751     if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)) {
1752 	return enif_make_badarg(env);
1753     }
1754     enif_clear_env(mti.p->dst_env);
1755     mti.p->dst_env_valid = 1;
1756     mti.p->reuse_pull = 0;
1757     mti.p->reuse_push = 0;
1758     mti.p->blob = enif_make_list(mti.p->dst_env, 0);
1759     return atom_ok;
1760 }
1761 
grow_blob(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1762 static ERL_NIF_TERM grow_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1763 {
1764     mti_t mti;
1765     ERL_NIF_TERM term;
1766     if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
1767 	|| (argc>2 && !enif_get_uint(env,argv[2], &mti.p->n))) {
1768 	return enif_make_badarg(env);
1769     }
1770     mti.p->caller_env = env;
1771     mti.p->other_term = argv[1];
1772     mti.p->n %= num_of_make_funcs();
1773     make_term_n(mti.p, mti.p->n++, &term);
1774     mti.p->blob = enif_make_list_cell(mti.p->dst_env, term, mti.p->blob);
1775     return atom_ok;
1776 }
1777 
send_blob(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1778 static ERL_NIF_TERM send_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1779 {
1780     mti_t mti;
1781     ErlNifPid to;
1782     ERL_NIF_TERM copy;
1783     int res;
1784     if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
1785 	|| !enif_get_local_pid(env, argv[1], &to)) {
1786 	return enif_make_badarg(env);
1787     }
1788     copy = enif_make_copy(env, mti.p->blob);
1789     res = enif_send(env, &to, mti.p->dst_env, mti.p->blob);
1790     if (res)
1791         mti.p->dst_env_valid = 0;
1792     return enif_make_tuple3(env, atom_ok, enif_make_int(env,res), copy);
1793 }
1794 
send3_blob(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1795 static ERL_NIF_TERM send3_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1796 {
1797     mti_t mti;
1798     ErlNifPid to;
1799     int res;
1800     if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
1801 	|| !enif_get_local_pid(env, argv[1], &to)) {
1802 	return enif_make_badarg(env);
1803     }
1804     mti.p->blob = enif_make_tuple2(mti.p->dst_env,
1805 				   enif_make_copy(mti.p->dst_env, argv[2]),
1806 				   mti.p->blob);
1807     res = enif_send(env, &to, mti.p->dst_env, mti.p->blob);
1808     if (res)
1809         mti.p->dst_env_valid = 0;
1810     return enif_make_int(env,res);
1811 }
1812 
threaded_sender(void * arg)1813 void* threaded_sender(void *arg)
1814 {
1815 
1816     mti_t mti;
1817     mti.vp = arg;
1818 
1819     enif_mutex_lock(mti.p->mtx);
1820     while (!mti.p->send_it) {
1821 	enif_cond_wait(mti.p->cond, mti.p->mtx);
1822     }
1823     mti.p->send_it = 0;
1824     enif_mutex_unlock(mti.p->mtx);
1825     mti.p->send_res = enif_send(NULL, &mti.p->to_pid, mti.p->dst_env, mti.p->blob);
1826     if (mti.p->send_res)
1827         mti.p->dst_env_valid = 0;
1828     return NULL;
1829 }
1830 
send_blob_thread(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1831 static ERL_NIF_TERM send_blob_thread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1832 {
1833     mti_t mti;
1834     ERL_NIF_TERM copy;
1835     if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
1836 	|| !enif_get_local_pid(env,argv[1], &mti.p->to_pid)) {
1837 	return enif_make_badarg(env);
1838     }
1839     copy = enif_make_copy(env, mti.p->blob);
1840 
1841     mti.p->send_it = enif_is_identical(argv[2],atom_join);
1842     if (enif_thread_create("nif_SUITE:send_from_thread", &mti.p->tid,
1843 			   threaded_sender, mti.p, NULL) != 0) {
1844 	return enif_make_badarg(env);
1845     }
1846     if (enif_is_identical(argv[2],atom_join)) {
1847 	int err = enif_thread_join(mti.p->tid, NULL);
1848 	assert(err == 0);
1849 	return enif_make_tuple3(env, atom_ok, enif_make_int(env, mti.p->send_res), copy);
1850     }
1851     else {
1852 	enif_keep_resource(mti.vp);
1853 	return enif_make_tuple2(env, atom_ok, copy);
1854     }
1855 }
1856 
join_send_thread(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1857 static ERL_NIF_TERM join_send_thread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1858 {
1859     mti_t mti;
1860     int err;
1861     if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)) {
1862 	return enif_make_badarg(env);
1863     }
1864     enif_mutex_lock(mti.p->mtx);
1865     mti.p->send_it = 1;
1866     enif_cond_signal(mti.p->cond);
1867     enif_mutex_unlock(mti.p->mtx);
1868     err = enif_thread_join(mti.p->tid, NULL);
1869     assert(err == 0);
1870     enif_release_resource(mti.vp);
1871     return enif_make_tuple2(env, atom_ok, enif_make_int(env, mti.p->send_res));
1872 }
1873 
copy_blob(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1874 static ERL_NIF_TERM copy_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1875 {
1876     mti_t mti;
1877     if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)) {
1878 	return enif_make_badarg(env);
1879     }
1880     return enif_make_copy(env, mti.p->blob);
1881 }
1882 
get_pidbin(ErlNifEnv * env,ERL_NIF_TERM pidbin,ErlNifPid * pid)1883 static int get_pidbin(ErlNifEnv* env, ERL_NIF_TERM pidbin, ErlNifPid* pid)
1884 {
1885     ErlNifBinary bin;
1886 
1887     if (!enif_inspect_binary(env, pidbin, &bin) || bin.size != sizeof(ErlNifPid))
1888         return 0;
1889 
1890     memcpy(pid, bin.data, bin.size);
1891     return 1;
1892 }
1893 
send_term(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1894 static ERL_NIF_TERM send_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1895 {
1896     ErlNifEnv* menv;
1897     ErlNifPid pid;
1898     int ret;
1899     if (!enif_get_local_pid(env, argv[0], &pid) && !get_pidbin(env, argv[0], &pid)) {
1900 	return enif_make_badarg(env);
1901     }
1902     menv = enif_alloc_env();
1903     ret = enif_send(env, &pid, menv, enif_make_copy(menv, argv[1]));
1904     enif_free_env(menv);
1905     return enif_make_int(env, ret);
1906 }
1907 
send_copy_term(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1908 static ERL_NIF_TERM send_copy_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1909 {
1910     ErlNifPid pid;
1911     int ret;
1912     if (!enif_get_local_pid(env, argv[0], &pid)) {
1913 	return enif_make_badarg(env);
1914     }
1915     ret = enif_send(env, &pid, NULL, argv[1]);
1916     return enif_make_int(env, ret);
1917 }
1918 
reverse_list(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1919 static ERL_NIF_TERM reverse_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
1920     ERL_NIF_TERM rev_list;
1921 
1922     if(!enif_make_reverse_list(env, argv[0], &rev_list))
1923 	return enif_make_atom(env, "badarg");
1924     return rev_list;
1925 }
1926 
otp_9668_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1927 static ERL_NIF_TERM otp_9668_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1928 {
1929     /* Inspect in process independent env */
1930     ErlNifEnv* myenv = enif_alloc_env();
1931     ERL_NIF_TERM mycopy = enif_make_copy(myenv, argv[0]);
1932     ErlNifBinary obin, cbin;
1933 
1934     if ((enif_inspect_binary(env, argv[0], &obin)
1935 	 && enif_inspect_binary(myenv, mycopy, &cbin))
1936 	||
1937 	(enif_inspect_iolist_as_binary(env, argv[0], &obin)
1938 	 && enif_inspect_iolist_as_binary(myenv, mycopy, &cbin)))
1939     {
1940 	assert(obin.size == cbin.size);
1941 	assert(memcmp(obin.data, cbin.data, obin.size) == 0);
1942     }
1943     enif_free_env(myenv);
1944     return atom_ok;
1945 }
1946 
otp_9828_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1947 static ERL_NIF_TERM otp_9828_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1948 {
1949     /* copy a writable binary could reallocate it due to "emasculation"
1950        and thereby render a previous inspection invalid.
1951      */
1952     ErlNifBinary bin1;
1953     ErlNifEnv* myenv;
1954 
1955     if (!enif_inspect_binary(env, argv[0], &bin1)) {
1956 	return enif_make_badarg(env);
1957     }
1958 
1959     myenv = enif_alloc_env();
1960     enif_make_copy(myenv, argv[0]);
1961     enif_free_env(myenv);
1962 
1963     return memcmp(bin1.data, "I'm alive!", 10)==0 ? atom_ok : atom_false;
1964 }
1965 
1966 
consume_timeslice_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1967 static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1968 {
1969     int percent;
1970     char atom[10];
1971 
1972     if (!enif_get_int(env, argv[0], &percent) ||
1973 	!enif_get_atom(env, argv[1], atom, sizeof(atom), ERL_NIF_LATIN1)) {
1974 	return enif_make_badarg(env);
1975     }
1976     if (strcmp(atom , "true") == 0) {
1977 	int cnt = 1;
1978 	while (enif_consume_timeslice(env, percent) == 0 && cnt < 200)
1979 	    cnt++;
1980 	return enif_make_int(env, cnt);
1981     }
1982     else {
1983 	return enif_make_int(env, enif_consume_timeslice(env, percent));
1984     }
1985 }
1986 
nif_sched2(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1987 static ERL_NIF_TERM nif_sched2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1988 {
1989     char s[64];
1990     if (!enif_get_string(env, argv[2], s, sizeof s, ERL_NIF_LATIN1))
1991 	return enif_make_badarg(env);
1992     return enif_make_tuple2(env, argv[3], argv[2]);
1993 }
1994 
nif_sched1(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1995 static ERL_NIF_TERM nif_sched1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
1996 {
1997     ERL_NIF_TERM new_argv[4];
1998     new_argv[0] = enif_make_atom(env, "garbage0");
1999     new_argv[1] = enif_make_atom(env, "garbage1");
2000     new_argv[2] = argv[0];
2001     new_argv[3] = argv[1];
2002     return enif_schedule_nif(env, "nif_sched2", 0, nif_sched2, 4, new_argv);
2003 }
2004 
call_nif_schedule(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2005 static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2006 {
2007     ERL_NIF_TERM result;
2008     assert(argc == 2);
2009     result = enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv);
2010     assert(!enif_is_exception(env, result));
2011     return result;
2012 }
2013 
2014 /*
2015  * If argv[0] is the integer 0, call enif_make_badarg, but don't return its
2016  * return value. Instead, return ok.  Result should still be a badarg
2017  * exception for the erlang caller.
2018  *
2019  * For any other value of argv[0], use it as an exception term and return
2020  * the exception.
2021  */
call_nif_exception(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2022 static ERL_NIF_TERM call_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2023 {
2024     ERL_NIF_TERM exc_term;
2025     ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg");
2026     int arg;
2027 
2028     if (enif_get_int(env, argv[0], &arg) && arg == 0) {
2029 	/* ignore return value */ enif_make_badarg(env);
2030 	assert(enif_has_pending_exception(env, NULL));
2031 	assert(enif_has_pending_exception(env, &exc_term));
2032 	assert(enif_is_identical(badarg_atom, exc_term));
2033 	return enif_make_atom(env, "ok");
2034     } else {
2035 	ERL_NIF_TERM exc_retval = enif_raise_exception(env, argv[0]);
2036 	assert(enif_has_pending_exception(env, NULL));
2037 	assert(enif_has_pending_exception(env, &exc_term));
2038 	assert(enif_is_identical(argv[0], exc_term));
2039 	return exc_retval;
2040     }
2041 }
2042 
2043 #if !defined(NAN) || !defined(INFINITY)
zero(void)2044 double zero(void)
2045 {
2046     return 0.0;
2047 }
2048 #endif
2049 
call_nif_nan_or_inf(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2050 static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2051 {
2052     double val;
2053     char arg[6];
2054     ERL_NIF_TERM res;
2055 
2056     assert(argc == 1);
2057     enif_get_atom(env, argv[0], arg, sizeof arg, ERL_NIF_LATIN1);
2058     if (strcmp(arg, "nan") == 0) {
2059         /* Verify that enif_make_double raises a badarg for NaN */
2060 #ifdef NAN
2061         val = NAN;
2062 #else
2063         val = 0.0/zero();
2064 #endif
2065     } else {
2066         /* Verify that enif_make_double raises a badarg for NaN and infinity */
2067 #ifdef INFINITY
2068         val = INFINITY;
2069 #else
2070         val = 1.0/zero();
2071 #endif
2072     }
2073     res = enif_make_double(env, val);
2074     assert(enif_is_exception(env, res));
2075     assert(enif_has_pending_exception(env, NULL));
2076     if (strcmp(arg, "tuple") == 0) {
2077         return enif_make_tuple2(env, argv[0], argv[0]);
2078     } else {
2079         return res;
2080     }
2081 }
2082 
call_nif_atom_too_long(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2083 static ERL_NIF_TERM call_nif_atom_too_long(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2084 {
2085     char str[257];
2086     char arg[4];
2087     size_t len;
2088     int i;
2089     ERL_NIF_TERM res;
2090 
2091     assert(argc == 1);
2092     enif_get_atom(env, argv[0], arg, sizeof arg, ERL_NIF_LATIN1);
2093     /* Verify that creating an atom from a string that's too long results in a badarg */
2094     for (i = 0; i < sizeof str; ++i) {
2095         str[i] = 'a';
2096     }
2097     str[256] = '\0';
2098     if (strcmp(arg, "len") == 0) {
2099         len = strlen(str);
2100         res = enif_make_atom_len(env, str, len);
2101     } else {
2102         res = enif_make_atom(env, str);
2103     }
2104     assert(enif_is_exception(env, res));
2105     return res;
2106 }
2107 
is_map_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2108 static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2109 {
2110     return enif_make_int(env, enif_is_map(env,argv[0]));
2111 }
get_map_size_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2112 static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2113 {
2114     size_t size = (size_t)-123;
2115     int ret = enif_get_map_size(env, argv[0], &size);
2116     return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, (int)size));
2117 }
make_new_map_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2118 static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2119 {
2120     return enif_make_new_map(env);
2121 }
make_map_put_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2122 static ERL_NIF_TERM make_map_put_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2123 {
2124     ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
2125     int ret = enif_make_map_put(env, argv[0], argv[1], argv[2], &map_out);
2126     return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
2127 }
get_map_value_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2128 static ERL_NIF_TERM get_map_value_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2129 {
2130     ERL_NIF_TERM value = enif_make_atom(env, "undefined");
2131     int ret = enif_get_map_value(env, argv[0], argv[1], &value);
2132     return enif_make_tuple2(env, enif_make_int(env,ret), value);
2133 
2134 }
make_map_update_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2135 static ERL_NIF_TERM make_map_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2136 {
2137     ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
2138     int ret = enif_make_map_update(env, argv[0], argv[1], argv[2], &map_out);
2139     return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
2140 }
make_map_remove_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2141 static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2142 {
2143     ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
2144     int ret = enif_make_map_remove(env, argv[0], argv[1], &map_out);
2145     return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
2146 }
2147 
2148 /* maps */
maps_from_list_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2149 static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2150 {
2151     ERL_NIF_TERM *keys, *values;
2152     ERL_NIF_TERM result, cell;
2153     unsigned count;
2154 
2155     assert(argc == 1);
2156     if (!enif_get_list_length(env, argv[0], &count)) {
2157         return enif_make_badarg(env);
2158     }
2159 
2160     keys = enif_alloc(sizeof(ERL_NIF_TERM) * count * 2);
2161     values = keys + count;
2162 
2163     cell = argv[0];
2164     count = 0;
2165 
2166     while (!enif_is_empty_list(env, cell)) {
2167         const ERL_NIF_TERM *pair;
2168         ERL_NIF_TERM tuple;
2169         int arity;
2170 
2171         if (!enif_get_list_cell(env, cell, &tuple, &cell)
2172             || !enif_get_tuple(env, tuple, &arity, &pair)
2173             || arity != 2) {
2174             enif_free(keys);
2175             return enif_make_badarg(env);
2176         }
2177 
2178         keys[count] = pair[0];
2179         values[count] = pair[1];
2180 
2181         count++;
2182     }
2183 
2184     if (!enif_make_map_from_arrays(env, keys, values, count, &result)) {
2185         result = enif_make_atom(env, "has_duplicate_keys");
2186     }
2187 
2188     enif_free(keys);
2189 
2190     return result;
2191 }
2192 
sorted_list_from_maps_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2193 static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
2194 
2195     ERL_NIF_TERM map = argv[0];
2196     ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */
2197     ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */
2198     ERL_NIF_TERM key, value, k2, v2;
2199     ErlNifMapIterator iter_f;
2200     ErlNifMapIterator iter_b;
2201     int cnt, next_ret, prev_ret;
2202 
2203     assert(argc == 1);
2204     if (!enif_is_map(env, map))
2205 	return enif_make_int(env, __LINE__);
2206 
2207     if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_FIRST))
2208 	return enif_make_int(env, __LINE__);
2209 
2210     cnt = 0;
2211     next_ret = 1;
2212     while(enif_map_iterator_get_pair(env,&iter_f,&key,&value)) {
2213 	if (!next_ret)
2214 	    return enif_make_int(env, __LINE__);
2215 	list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f);
2216 	next_ret = enif_map_iterator_next(env,&iter_f);
2217 	cnt++;
2218     }
2219     if (cnt && next_ret)
2220 	return enif_make_int(env, __LINE__);
2221 
2222     if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_LAST))
2223 	return enif_make_int(env, __LINE__);
2224 
2225     cnt = 0;
2226     prev_ret = 1;
2227     while(enif_map_iterator_get_pair(env,&iter_b,&key,&value)) {
2228 	if (!prev_ret)
2229 	    return enif_make_int(env, __LINE__);
2230 
2231 	/* Test that iter_f can step "backwards" */
2232 	if (!enif_map_iterator_prev(env,&iter_f)
2233 	    || !enif_map_iterator_get_pair(env,&iter_f,&k2,&v2)
2234 	    || k2 != key || v2 != value) {
2235 	    return enif_make_int(env, __LINE__);
2236 	}
2237 
2238 	list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b);
2239 	prev_ret = enif_map_iterator_prev(env,&iter_b);
2240 	cnt++;
2241     }
2242 
2243     if (cnt) {
2244 	if (prev_ret || enif_map_iterator_prev(env,&iter_f))
2245 	    return enif_make_int(env, __LINE__);
2246 
2247 	/* Test that iter_b can step "backwards" one step */
2248 	if (!enif_map_iterator_next(env, &iter_b)
2249 	    || !enif_map_iterator_get_pair(env,&iter_b,&k2,&v2)
2250 	    || k2 != key || v2 != value)
2251 	    return enif_make_int(env, __LINE__);
2252     }
2253 
2254     enif_map_iterator_destroy(env, &iter_f);
2255     enif_map_iterator_destroy(env, &iter_b);
2256 
2257     return enif_make_tuple2(env, list_f, list_b);
2258 }
2259 
2260 
monotonic_time(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2261 static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2262 {
2263     ErlNifTimeUnit time_unit;
2264 
2265     assert(argc == 1);
2266     if (enif_compare(argv[0], atom_second) == 0)
2267 	time_unit = ERL_NIF_SEC;
2268     else if (enif_compare(argv[0], atom_millisecond) == 0)
2269 	time_unit = ERL_NIF_MSEC;
2270     else if (enif_compare(argv[0], atom_microsecond) == 0)
2271 	time_unit = ERL_NIF_USEC;
2272     else if (enif_compare(argv[0], atom_nanosecond) == 0)
2273 	time_unit = ERL_NIF_NSEC;
2274     else
2275 	time_unit = 4711; /* invalid time unit */
2276 
2277     return enif_make_int64(env, enif_monotonic_time(time_unit));
2278 }
2279 
time_offset(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2280 static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2281 {
2282     ErlNifTimeUnit time_unit;
2283 
2284     assert(argc == 1);
2285     if (enif_compare(argv[0], atom_second) == 0)
2286 	time_unit = ERL_NIF_SEC;
2287     else if (enif_compare(argv[0], atom_millisecond) == 0)
2288 	time_unit = ERL_NIF_MSEC;
2289     else if (enif_compare(argv[0], atom_microsecond) == 0)
2290 	time_unit = ERL_NIF_USEC;
2291     else if (enif_compare(argv[0], atom_nanosecond) == 0)
2292 	time_unit = ERL_NIF_NSEC;
2293     else
2294 	time_unit = 4711; /* invalid time unit */
2295     return enif_make_int64(env, enif_time_offset(time_unit));
2296 }
2297 
convert_time_unit(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2298 static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2299 {
2300     ErlNifSInt64 i64;
2301     ErlNifTime val;
2302     ErlNifTimeUnit from, to;
2303 
2304     assert(argc == 3);
2305     if (!enif_get_int64(env, argv[0], &i64))
2306 	return enif_make_badarg(env);
2307 
2308     val = (ErlNifTime) i64;
2309 
2310     if (enif_compare(argv[1], atom_second) == 0)
2311 	from = ERL_NIF_SEC;
2312     else if (enif_compare(argv[1], atom_millisecond) == 0)
2313 	from = ERL_NIF_MSEC;
2314     else if (enif_compare(argv[1], atom_microsecond) == 0)
2315 	from = ERL_NIF_USEC;
2316     else if (enif_compare(argv[1], atom_nanosecond) == 0)
2317 	from = ERL_NIF_NSEC;
2318     else
2319 	from = 4711; /* invalid time unit */
2320 
2321     if (enif_compare(argv[2], atom_second) == 0)
2322 	to = ERL_NIF_SEC;
2323     else if (enif_compare(argv[2], atom_millisecond) == 0)
2324 	to = ERL_NIF_MSEC;
2325     else if (enif_compare(argv[2], atom_microsecond) == 0)
2326 	to = ERL_NIF_USEC;
2327     else if (enif_compare(argv[2], atom_nanosecond) == 0)
2328 	to = ERL_NIF_NSEC;
2329     else
2330 	to = 4711; /* invalid time unit */
2331 
2332     return enif_make_int64(env, enif_convert_time_unit(val, from, to));
2333 }
2334 
now_time(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2335 static ERL_NIF_TERM now_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2336 {
2337     return enif_now_time(env);
2338 }
2339 
cpu_time(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2340 static ERL_NIF_TERM cpu_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2341 {
2342     return enif_cpu_time(env);
2343 }
2344 
unique_integer(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2345 static ERL_NIF_TERM unique_integer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2346 {
2347     ERL_NIF_TERM atom_pos = enif_make_atom(env,"positive"),
2348         atom_mon = enif_make_atom(env,"monotonic");
2349     ERL_NIF_TERM opts = argv[0], opt;
2350     ErlNifUniqueInteger properties = 0;
2351 
2352     while (!enif_is_empty_list(env, opts)) {
2353 	if (!enif_get_list_cell(env, opts, &opt, &opts))
2354             return enif_make_badarg(env);
2355 
2356         if (enif_compare(opt, atom_pos) == 0)
2357             properties |= ERL_NIF_UNIQUE_POSITIVE;
2358         if (enif_compare(opt, atom_mon) == 0)
2359             properties |= ERL_NIF_UNIQUE_MONOTONIC;
2360     }
2361 
2362     return enif_make_unique_integer(env, properties);
2363 }
2364 
is_process_alive(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2365 static ERL_NIF_TERM is_process_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2366 {
2367     ErlNifPid pid;
2368     if (!enif_get_local_pid(env, argv[0], &pid))
2369         return enif_make_badarg(env);
2370     if (enif_is_process_alive(env, &pid))
2371         return atom_true;
2372     return atom_false;
2373 }
2374 
is_port_alive(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2375 static ERL_NIF_TERM is_port_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2376 {
2377     ErlNifPort port;
2378     if (!enif_get_local_port(env, argv[0], &port))
2379         return enif_make_badarg(env);
2380     if (enif_is_port_alive(env, &port))
2381         return atom_true;
2382     return atom_false;
2383 }
2384 
term_to_binary(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2385 static ERL_NIF_TERM term_to_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2386 {
2387     ErlNifBinary bin;
2388     ErlNifPid pid;
2389     ErlNifEnv *msg_env = env;
2390     ERL_NIF_TERM term;
2391 
2392     if (enif_get_local_pid(env, argv[1], &pid))
2393         msg_env = enif_alloc_env();
2394 
2395     if (!enif_term_to_binary(msg_env, argv[0], &bin))
2396         return enif_make_badarg(env);
2397 
2398     term = enif_make_binary(msg_env, &bin);
2399 
2400     if (msg_env != env) {
2401         enif_send(env, &pid, msg_env, term);
2402         enif_free_env(msg_env);
2403         return atom_true;
2404     } else {
2405         return term;
2406     }
2407 }
2408 
binary_to_term(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2409 static ERL_NIF_TERM binary_to_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2410 {
2411     ErlNifBinary bin;
2412     ERL_NIF_TERM term, dummy, ret_term;
2413     ErlNifPid pid;
2414     ErlNifEnv *msg_env = env;
2415     unsigned int opts;
2416     ErlNifUInt64 ret;
2417 
2418     if (enif_get_local_pid(env, argv[1], &pid))
2419         msg_env = enif_alloc_env();
2420 
2421     if (!enif_inspect_binary(env, argv[0], &bin)
2422 	|| !enif_get_uint(env, argv[2], &opts))
2423         return enif_make_badarg(env);
2424 
2425     /* build dummy heap term first to provoke OTP-15080 */
2426     dummy = enif_make_list_cell(msg_env, atom_true, atom_false);
2427 
2428     ret = enif_binary_to_term(msg_env, bin.data, bin.size, &term,
2429 			      (ErlNifBinaryToTerm)opts);
2430     if (!ret)
2431 	return atom_false;
2432 
2433     ret_term = enif_make_uint64(env, ret);
2434     if (msg_env != env) {
2435         enif_send(env, &pid, msg_env,
2436                   enif_make_tuple2(msg_env, term, dummy));
2437         enif_free_env(msg_env);
2438         return ret_term;
2439     } else {
2440         return enif_make_tuple3(env, ret_term, term, dummy);
2441     }
2442 }
2443 
port_command(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2444 static ERL_NIF_TERM port_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2445 {
2446     ErlNifPort port;
2447 
2448     if (!enif_get_local_port(env, argv[0], &port))
2449         return enif_make_badarg(env);
2450 
2451     if (!enif_port_command(env, &port, NULL, argv[1]))
2452         return enif_make_badarg(env);
2453     return atom_true;
2454 }
2455 
format_term(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2456 static ERL_NIF_TERM format_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2457 {
2458     ErlNifBinary obin;
2459     unsigned int size;
2460 
2461     if (!enif_get_uint(env, argv[0], &size))
2462 	return enif_make_badarg(env);
2463     if (!enif_alloc_binary(size,&obin))
2464 	return enif_make_badarg(env);
2465 
2466     if (enif_snprintf((char*)obin.data, (size_t)size, "%T", argv[1]) < 0)
2467         return atom_false;
2468 
2469     return enif_make_binary(env,&obin);
2470 }
2471 
get_fd(ErlNifEnv * env,ERL_NIF_TERM term,struct fd_resource ** rsrc)2472 static int get_fd(ErlNifEnv* env, ERL_NIF_TERM term, struct fd_resource** rsrc)
2473 {
2474     if (!enif_get_resource(env, term, fd_resource_type, (void**)rsrc)) {
2475         return 0;
2476     }
2477     return 1;
2478 }
2479 
2480 /* Returns: badarg
2481  *    Or an enif_select result, which is a combination of bits:
2482  *    ERL_NIF_SELECT_STOP_CALLED = 1
2483  *    ERL_NIF_SELECT_STOP_SCHEDULED = 2
2484  *    ERL_NIF_SELECT_INVALID_EVENT = 4
2485  *    ERL_NIF_SELECT_FAILED = 8
2486  */
select_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2487 static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2488 {
2489     struct fd_resource* fdr;
2490     enum ErlNifSelectFlags mode;
2491     void* obj;
2492     ErlNifPid nifpid, *pid = NULL;
2493     ERL_NIF_TERM ref_or_msg;
2494     ErlNifEnv* msg_env = NULL;
2495     int retval;
2496 
2497     if (!get_fd(env, argv[0], &fdr)
2498         || !enif_get_uint(env, argv[1], (unsigned int*)&mode)
2499         || !enif_get_resource(env, argv[2], fd_resource_type, &obj))
2500     {
2501         return enif_make_badarg(env);
2502     }
2503 
2504     if (argv[3] != atom_null) {
2505 	if (!enif_get_local_pid(env, argv[3], &nifpid))
2506 	    return enif_make_badarg(env);
2507 	pid = &nifpid;
2508     }
2509     ref_or_msg = argv[4];
2510     if (argv[5] != atom_null) {
2511         msg_env = enif_alloc_env();
2512         ref_or_msg = enif_make_copy(msg_env, ref_or_msg);
2513     }
2514 
2515     fdr->was_selected = 1;
2516     enif_self(env, &fdr->pid);
2517     switch (mode) {
2518     case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_READ:
2519         retval = enif_select_read(env, fdr->fd, obj, pid, ref_or_msg, msg_env);
2520         break;
2521     case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_WRITE:
2522         retval = enif_select_write(env, fdr->fd, obj, pid, ref_or_msg, msg_env);
2523         break;
2524     default:
2525         retval = enif_select(env, fdr->fd, mode, obj, pid, ref_or_msg);
2526     }
2527 
2528     if (msg_env)
2529         enif_free_env(msg_env);
2530 
2531     return enif_make_int(env, retval);
2532 }
2533 
2534 #ifndef __WIN32__
2535 /*
2536  * Create a read-write pipe with two fds (to read and to write)
2537  */
pipe_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2538 static ERL_NIF_TERM pipe_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2539 {
2540     struct fd_resource* read_rsrc;
2541     struct fd_resource* write_rsrc;
2542     ERL_NIF_TERM read_fd, write_fd;
2543     int fds[2], flags;
2544 
2545     if (pipe(fds) < 0)
2546         return enif_make_string(env, "pipe failed", ERL_NIF_LATIN1);
2547 
2548     if ((flags = fcntl(fds[0], F_GETFL, 0)) < 0
2549         || fcntl(fds[0], F_SETFL, flags|O_NONBLOCK) < 0
2550         || (flags = fcntl(fds[1], F_GETFL, 0)) < 0
2551         || fcntl(fds[1], F_SETFL, flags|O_NONBLOCK) < 0) {
2552         close(fds[0]);
2553         close(fds[1]);
2554         return enif_make_string(env, "fcntl failed on pipe", ERL_NIF_LATIN1);
2555     }
2556 
2557     read_rsrc  = enif_alloc_resource(fd_resource_type, sizeof(struct fd_resource));
2558     write_rsrc = enif_alloc_resource(fd_resource_type, sizeof(struct fd_resource));
2559     read_rsrc->fd  = fds[0];
2560     read_rsrc->was_selected = 0;
2561     write_rsrc->fd = fds[1];
2562     write_rsrc->was_selected = 0;
2563     read_fd  = enif_make_resource(env, read_rsrc);
2564     write_fd = enif_make_resource(env, write_rsrc);
2565     enif_release_resource(read_rsrc);
2566     enif_release_resource(write_rsrc);
2567 
2568     return enif_make_tuple2(env,
2569                enif_make_tuple2(env, read_fd, make_pointer(env, read_rsrc)),
2570                enif_make_tuple2(env, write_fd, make_pointer(env, write_rsrc)));
2571 }
2572 
2573 /*
2574  * Create (dupe) of a resource with the same fd, to test stealing
2575  */
dupe_resource_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2576 static ERL_NIF_TERM dupe_resource_nif(ErlNifEnv* env, int argc,
2577                                       const ERL_NIF_TERM argv[]) {
2578     struct fd_resource* orig_rsrc;
2579 
2580     if (!get_fd(env, argv[0], &orig_rsrc)) {
2581         return enif_make_badarg(env);
2582     } else {
2583         struct fd_resource* new_rsrc;
2584         ERL_NIF_TERM new_fd;
2585 
2586         new_rsrc = enif_alloc_resource(fd_resource_type,
2587                                        sizeof(struct fd_resource));
2588         new_rsrc->fd = orig_rsrc->fd;
2589         new_rsrc->was_selected = 0;
2590         new_fd = enif_make_resource(env, new_rsrc);
2591         enif_release_resource(new_rsrc);
2592 
2593         return enif_make_tuple2(env, new_fd, make_pointer(env, new_rsrc));
2594     }
2595 }
2596 
write_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2597 static ERL_NIF_TERM write_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2598 {
2599     struct fd_resource* fdr;
2600     ErlNifBinary bin;
2601     int n, written = 0;
2602 
2603     if (!get_fd(env, argv[0], &fdr)
2604         || !enif_inspect_binary(env, argv[1], &bin))
2605         return enif_make_badarg(env);
2606 
2607     for (;;) {
2608         n = write(fdr->fd, bin.data + written, bin.size - written);
2609         if (n >= 0) {
2610             written += n;
2611             if (written == bin.size) {
2612                 return atom_ok;
2613             }
2614         }
2615         else if (errno == EAGAIN) {
2616             return enif_make_tuple2(env, atom_eagain, enif_make_int(env, written));
2617         }
2618         else if (errno == EINTR) {
2619             continue;
2620         }
2621         else {
2622             return enif_make_tuple2(env, atom_error, enif_make_int(env, errno));
2623         }
2624     }
2625 }
2626 
read_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2627 static ERL_NIF_TERM read_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2628 {
2629     struct fd_resource* fdr;
2630     unsigned char* buf;
2631     int n, count;
2632     ERL_NIF_TERM res;
2633 
2634     if (!get_fd(env, argv[0], &fdr)
2635         || !enif_get_int(env, argv[1], &count) || count < 1)
2636         return enif_make_badarg(env);
2637 
2638     buf = enif_make_new_binary(env, count, &res);
2639 
2640     for (;;) {
2641         n = read(fdr->fd, buf, count);
2642         if (n > 0) {
2643             if (n < count) {
2644                 res = enif_make_sub_binary(env, res, 0, n);
2645             }
2646             return res;
2647         }
2648         else if (n == 0) {
2649             return atom_eof;
2650         }
2651         else if (errno == EAGAIN) {
2652             return atom_eagain;
2653         }
2654         else if (errno == EINTR) {
2655             continue;
2656         }
2657         else {
2658             return enif_make_tuple2(env, atom_error, enif_make_int(env, errno));
2659         }
2660     }
2661 }
2662 
is_closed_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2663 static ERL_NIF_TERM is_closed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2664 {
2665     struct fd_resource* fdr;
2666 
2667     if (!get_fd(env, argv[0], &fdr))
2668         return enif_make_badarg(env);
2669 
2670     return fdr->fd < 0 ? atom_true : atom_false;
2671 }
2672 
clear_select_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2673 static ERL_NIF_TERM clear_select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2674 {
2675     struct fd_resource* fdr = NULL;
2676 
2677     if (!get_fd(env, argv[0], &fdr))
2678         return enif_make_badarg(env);
2679 
2680     fdr->fd = -1;
2681     fdr->was_selected = 0;
2682 
2683     return atom_ok;
2684 }
2685 
2686 #endif /* !__WIN32__ */
2687 
2688 
fd_resource_dtor(ErlNifEnv * env,void * obj)2689 static void fd_resource_dtor(ErlNifEnv* env, void* obj)
2690 {
2691     struct fd_resource* fdr = (struct fd_resource*)obj;
2692     resource_dtor(env, obj);
2693 #ifdef __WIN32__
2694     abort();
2695 #else
2696     if (fdr->fd >= 0) {
2697         assert(!fdr->was_selected);
2698         close(fdr->fd);
2699     }
2700 #endif
2701 }
2702 
2703 static struct {
2704     void* obj;
2705     int was_direct_call;
2706 }last_fd_stop;
2707 int fd_stop_cnt = 0;
2708 
fd_resource_stop(ErlNifEnv * env,void * obj,ErlNifEvent fd,int is_direct_call)2709 static void fd_resource_stop(ErlNifEnv* env, void* obj, ErlNifEvent fd,
2710                              int is_direct_call)
2711 {
2712     struct fd_resource* fdr = (struct fd_resource*)obj;
2713     assert(fd == fdr->fd);
2714     assert(fd >= 0);
2715 
2716     last_fd_stop.obj = obj;
2717     last_fd_stop.was_direct_call = is_direct_call;
2718     fd_stop_cnt++;
2719 
2720     close(fd);
2721     fdr->fd = -1;   /* thread safety ? */
2722     fdr->was_selected = 0;
2723 
2724     {
2725         ErlNifEnv* msg_env = enif_alloc_env();
2726         ERL_NIF_TERM msg;
2727         msg = enif_make_tuple3(msg_env,
2728                                atom_fd_resource_stop,
2729                                make_pointer(msg_env, obj),
2730                                enif_make_int(msg_env, is_direct_call));
2731 
2732         enif_send(env, &fdr->pid, msg_env, msg);
2733         enif_free_env(msg_env);
2734     }
2735 }
2736 
last_fd_stop_call(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2737 static ERL_NIF_TERM last_fd_stop_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2738 {
2739     ERL_NIF_TERM last, ret;
2740     last = enif_make_tuple2(env, make_pointer(env, last_fd_stop.obj),
2741                             enif_make_int(env, last_fd_stop.was_direct_call));
2742     ret = enif_make_tuple2(env, enif_make_int(env, fd_stop_cnt), last);
2743     fd_stop_cnt = 0;
2744     return ret;
2745 }
2746 
2747 
monitor_resource_dtor(ErlNifEnv * env,void * obj)2748 static void monitor_resource_dtor(ErlNifEnv* env, void* obj)
2749 {
2750     resource_dtor(env, obj);
2751 }
2752 
make_monitor(ErlNifEnv * env,const ErlNifMonitor * mon)2753 static ERL_NIF_TERM make_monitor(ErlNifEnv* env, const ErlNifMonitor* mon)
2754 {
2755     ERL_NIF_TERM mon_bin;
2756     memcpy(enif_make_new_binary(env, sizeof(ErlNifMonitor), &mon_bin),
2757            mon, sizeof(ErlNifMonitor));
2758     return mon_bin;
2759 }
2760 
get_monitor(ErlNifEnv * env,ERL_NIF_TERM term,ErlNifMonitor * mon)2761 static int get_monitor(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifMonitor* mon)
2762 {
2763     ErlNifBinary bin;
2764     if (!enif_inspect_binary(env, term, &bin)
2765         || bin.size != sizeof(ErlNifMonitor))
2766         return 0;
2767     memcpy(mon, bin.data, bin.size);
2768     return 1;
2769 }
2770 
monitor_resource_down(ErlNifEnv * env,void * obj,ErlNifPid * pid,ErlNifMonitor * mon)2771 static void monitor_resource_down(ErlNifEnv* env, void* obj, ErlNifPid* pid,
2772                                   ErlNifMonitor* mon)
2773 {
2774     struct monitor_resource* rsrc = (struct monitor_resource*)obj;
2775     ErlNifEnv* build_env;
2776     ErlNifEnv* msg_env;
2777     ERL_NIF_TERM msg;
2778 
2779     if (rsrc->use_msgenv) {
2780         msg_env = enif_alloc_env();
2781         build_env = msg_env;
2782     }
2783     else {
2784         msg_env = NULL;
2785         build_env = env;
2786     }
2787 
2788     msg = enif_make_tuple4(build_env,
2789                            atom_monitor_resource_down,
2790                            make_pointer(build_env, obj),
2791                            enif_make_pid(build_env, pid),
2792                            make_monitor(build_env, mon));
2793 
2794     enif_send(env, &rsrc->receiver, msg_env, msg);
2795     if (msg_env)
2796         enif_free_env(msg_env);
2797 }
2798 
alloc_monitor_resource_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2799 static ERL_NIF_TERM alloc_monitor_resource_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2800 {
2801     struct monitor_resource* rsrc;
2802 
2803     rsrc  = enif_alloc_resource(monitor_resource_type, sizeof(struct monitor_resource));
2804 
2805     return make_pointer(env,rsrc);
2806 }
2807 
monitor_process_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2808 static ERL_NIF_TERM monitor_process_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2809 {
2810     struct monitor_resource* rsrc;
2811     ErlNifPid target;
2812     ErlNifMonitor mon;
2813     int res;
2814 
2815     if (!get_pointer(env, argv[0], (void**)&rsrc)
2816         || !enif_get_local_pid(env, argv[1], &target)
2817         || !enif_get_local_pid(env, argv[3], &rsrc->receiver)) {
2818         return enif_make_badarg(env);
2819     }
2820 
2821     rsrc->use_msgenv = (argv[2] == atom_true);
2822     res = enif_monitor_process(env, rsrc, &target, &mon);
2823 
2824     return enif_make_tuple2(env, enif_make_int(env, res), make_monitor(env, &mon));
2825 }
2826 
demonitor_process_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2827 static ERL_NIF_TERM demonitor_process_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2828 {
2829     struct monitor_resource* rsrc;
2830     ErlNifMonitor mon;
2831     int res;
2832 
2833     if (!get_pointer(env, argv[0], (void**)&rsrc)
2834         || !get_monitor(env, argv[1], &mon)) {
2835         return enif_make_badarg(env);
2836     }
2837 
2838     res = enif_demonitor_process(env, rsrc, &mon);
2839 
2840     return enif_make_int(env, res);
2841 }
2842 
compare_monitors_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2843 static ERL_NIF_TERM compare_monitors_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2844 {
2845     ErlNifMonitor m1, m2;
2846     if (!get_monitor(env, argv[0], &m1)
2847         || !get_monitor(env, argv[1], &m2)) {
2848         return enif_make_badarg(env);
2849     }
2850 
2851     return enif_make_int(env, enif_compare_monitors(&m1, &m2));
2852 }
2853 
make_monitor_term_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2854 static ERL_NIF_TERM make_monitor_term_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2855 {
2856     ErlNifMonitor m;
2857     if (!get_monitor(env, argv[0], &m)) {
2858         return enif_make_badarg(env);
2859     }
2860 
2861     return enif_make_monitor_term(env, &m);
2862 }
2863 
2864 
2865 /*********** monitor_frenzy ************/
2866 
2867 struct frenzy_rand_bits
2868 {
2869     unsigned int source;
2870     unsigned int bits_consumed;
2871 };
2872 
2873 static unsigned int frenzy_rand_bits_max;
2874 
rand_bits(struct frenzy_rand_bits * rnd,unsigned int nbits)2875 unsigned rand_bits(struct frenzy_rand_bits* rnd, unsigned int nbits)
2876 {
2877     unsigned int res;
2878 
2879     rnd->bits_consumed += nbits;
2880     assert(rnd->bits_consumed <= frenzy_rand_bits_max);
2881     res = rnd->source & ((1 << nbits)-1);
2882     rnd->source >>= nbits;
2883     return res;
2884 }
2885 
2886 #define FRENZY_PROCS_MAX_BITS 4
2887 #define FRENZY_PROCS_MAX (1 << FRENZY_PROCS_MAX_BITS)
2888 
2889 #define FRENZY_RESOURCES_MAX_BITS 4
2890 #define FRENZY_RESOURCES_MAX (1 << FRENZY_RESOURCES_MAX_BITS)
2891 
2892 #define FRENZY_MONITORS_MAX_BITS 4
2893 #define FRENZY_MONITORS_MAX (1 << FRENZY_MONITORS_MAX_BITS)
2894 
2895 struct frenzy_monitor {
2896     ErlNifMutex* lock;
2897     volatile enum {
2898         MON_FREE, MON_FREE_DOWN, MON_FREE_DEMONITOR,
2899         MON_TRYING, MON_ACTIVE, MON_PENDING
2900     } state;
2901     ErlNifMonitor mon;
2902     ErlNifPid pid;
2903     unsigned int use_cnt;
2904 };
2905 
2906 struct frenzy_resource {
2907     unsigned int rix;
2908     struct frenzy_monitor monv[FRENZY_MONITORS_MAX];
2909 };
2910 struct frenzy_reslot {
2911     ErlNifMutex* lock;
2912     int stopped;
2913     struct frenzy_resource* obj;
2914     unsigned long alloc_cnt;
2915     unsigned long release_cnt;
2916     unsigned long dtor_cnt;
2917 };
2918 static struct frenzy_reslot resv[FRENZY_RESOURCES_MAX];
2919 
monitor_frenzy_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2920 static ERL_NIF_TERM monitor_frenzy_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2921 {
2922     struct frenzy_proc {
2923         ErlNifPid pid;
2924         int is_free;
2925     };
2926     static struct frenzy_proc procs[FRENZY_PROCS_MAX];
2927     static struct frenzy_proc* proc_refs[FRENZY_PROCS_MAX];
2928     static unsigned int nprocs, old_nprocs;
2929     static ErlNifMutex* procs_lock;
2930     static unsigned long spawn_cnt = 0;
2931     static unsigned long kill_cnt = 0;
2932     static unsigned long proc_histogram[FRENZY_PROCS_MAX];
2933     static int initialized = 0;
2934 
2935     static const unsigned int primes[] = {7, 13, 17, 19};
2936 
2937     struct frenzy_resource* r;
2938     struct frenzy_rand_bits rnd;
2939     unsigned int op, inc, my_nprocs;
2940     unsigned int mix;  /* r->monv[] index */
2941     unsigned int rix;  /* resv[] index */
2942     unsigned int pix;  /* procs[] index */
2943     unsigned int ref_ix; /* proc_refs[] index */
2944     int self_pix, rv;
2945     ERL_NIF_TERM retval = atom_error;
2946     const ERL_NIF_TERM Op = argv[0];
2947     const ERL_NIF_TERM Rnd = argv[1];
2948     const ERL_NIF_TERM SelfPix = argv[2];
2949     const ERL_NIF_TERM NewPid = argv[3];
2950 
2951     if (enif_is_atom(env, Op)) {
2952         if (Op == atom_init) {
2953             if (initialized || !enif_get_uint(env, Rnd, &frenzy_rand_bits_max))
2954                 return enif_make_badarg(env);
2955 
2956             procs_lock = enif_mutex_create("nif_SUITE:monitor_frenzy.procs");
2957             nprocs = 0;
2958             old_nprocs = 0;
2959             for (pix = 0; pix < FRENZY_PROCS_MAX; pix++) {
2960                 proc_refs[pix] = &procs[pix];
2961                 procs[pix].is_free = 1;
2962                 proc_histogram[pix] = 0;
2963             }
2964             for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
2965                 resv[rix].lock = enif_mutex_create("nif_SUITE:monitor_frenzy.resv.lock");
2966                 resv[rix].obj = NULL;
2967                 resv[rix].stopped = 0;
2968                 resv[rix].alloc_cnt = 0;
2969                 resv[rix].release_cnt = 0;
2970                 resv[rix].dtor_cnt = 0;
2971             }
2972 
2973             /* Add self as first process */
2974             enif_self(env, &procs[0].pid);
2975             procs[0].is_free = 0;
2976             old_nprocs = ++nprocs;
2977 
2978             spawn_cnt = 1;
2979             kill_cnt = 0;
2980             initialized = 1;
2981             return enif_make_uint(env, 0);  /* SelfPix */
2982         }
2983         else if (Op == atom_stats) {
2984             ERL_NIF_TERM hist[FRENZY_PROCS_MAX];
2985             unsigned long res_alloc_cnt = 0;
2986             unsigned long res_release_cnt = 0;
2987             unsigned long res_dtor_cnt = 0;
2988             for (ref_ix = 0; ref_ix < FRENZY_PROCS_MAX; ref_ix++) {
2989                 hist[ref_ix] = enif_make_ulong(env, proc_histogram[ref_ix]);
2990             }
2991             for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
2992                 res_alloc_cnt += resv[rix].alloc_cnt;
2993                 res_release_cnt += resv[rix].release_cnt;
2994                 res_dtor_cnt += resv[rix].dtor_cnt;
2995             }
2996 
2997             return
2998             enif_make_list4(env,
2999                             enif_make_tuple2(env, enif_make_string(env, "proc_histogram", ERL_NIF_LATIN1),
3000                                              enif_make_list_from_array(env, hist, FRENZY_PROCS_MAX)),
3001                             enif_make_tuple2(env, enif_make_string(env, "spawn_cnt", ERL_NIF_LATIN1),
3002                                              enif_make_ulong(env, spawn_cnt)),
3003                             enif_make_tuple2(env, enif_make_string(env, "kill_cnt", ERL_NIF_LATIN1),
3004                                              enif_make_ulong(env, kill_cnt)),
3005                             enif_make_tuple4(env, enif_make_string(env, "resource_alloc", ERL_NIF_LATIN1),
3006                                              enif_make_ulong(env, res_alloc_cnt),
3007                                              enif_make_ulong(env, res_release_cnt),
3008                                              enif_make_ulong(env, res_dtor_cnt)));
3009 
3010         }
3011         else if (Op == atom_stop && initialized) {  /* stop all */
3012 
3013             /* Release all resources */
3014             for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
3015                 enif_mutex_lock(resv[rix].lock);
3016                 r = resv[rix].obj;
3017                 if (r) {
3018                     resv[rix].obj =  NULL;
3019                     resv[rix].release_cnt++;
3020                 }
3021                 resv[rix].stopped = 1;
3022                 enif_mutex_unlock(resv[rix].lock);
3023                 if (r)
3024                     enif_release_resource(r);
3025             }
3026 
3027             /* Remove and return all pids */
3028             retval = enif_make_list(env, 0);
3029             enif_mutex_lock(procs_lock);
3030             for (ref_ix = 0; ref_ix < nprocs; ref_ix++) {
3031                 assert(!proc_refs[ref_ix]->is_free);
3032                 retval = enif_make_list_cell(env, enif_make_pid(env, &proc_refs[ref_ix]->pid),
3033                                              retval);
3034                 proc_refs[ref_ix]->is_free = 1;
3035             }
3036             kill_cnt += nprocs;
3037             nprocs = 0;
3038             old_nprocs = 0;
3039             enif_mutex_unlock(procs_lock);
3040 
3041             return retval;
3042         }
3043         return enif_make_badarg(env);
3044     }
3045 
3046     if (!enif_get_int(env, SelfPix, &self_pix) ||
3047         !enif_get_uint(env, Op, &op) ||
3048         !enif_get_uint(env, Rnd, &rnd.source))
3049         return enif_make_badarg(env);
3050 
3051     rnd.bits_consumed = 0;
3052     switch (op) {
3053     case 0:  { /* add/remove process */
3054         ErlNifPid self;
3055         enif_self(env, &self);
3056 
3057         ref_ix = rand_bits(&rnd, FRENZY_PROCS_MAX_BITS) % FRENZY_PROCS_MAX;
3058         enif_mutex_lock(procs_lock);
3059         if (procs[self_pix].is_free || procs[self_pix].pid.pid != self.pid) {
3060             /* Some one already removed me */
3061             enif_mutex_unlock(procs_lock);
3062             return atom_done;
3063         }
3064         if (ref_ix >= nprocs || nprocs < 2) { /* add process */
3065             ref_ix = nprocs++;
3066             pix = proc_refs[ref_ix] - procs;
3067             assert(procs[pix].is_free);
3068             if (!enif_get_local_pid(env, NewPid, &procs[pix].pid))
3069                 abort();
3070             procs[pix].is_free = 0;
3071             spawn_cnt++;
3072             proc_histogram[ref_ix]++;
3073             old_nprocs = nprocs;
3074             enif_mutex_unlock(procs_lock);
3075             DBG_TRACE2("Add pid %T, nprocs = %u\n", NewPid, nprocs);
3076             retval = enif_make_uint(env, pix);
3077         }
3078         else { /* remove process */
3079             pix = proc_refs[ref_ix] - procs;
3080             if (pix == self_pix) {
3081                 ref_ix = (ref_ix + 1) % nprocs;
3082                 pix = proc_refs[ref_ix] - procs;
3083             }
3084             assert(procs[pix].pid.pid != self.pid);
3085             assert(!procs[pix].is_free);
3086             retval = enif_make_pid(env, &procs[pix].pid);
3087             --nprocs;
3088             assert(!proc_refs[nprocs]->is_free);
3089             if (ref_ix != nprocs) {
3090                 struct frenzy_proc* tmp = proc_refs[ref_ix];
3091                 proc_refs[ref_ix] = proc_refs[nprocs];
3092                 proc_refs[nprocs] = tmp;
3093             }
3094             procs[pix].is_free = 1;
3095             proc_histogram[nprocs]++;
3096             kill_cnt++;
3097             enif_mutex_unlock(procs_lock);
3098             DBG_TRACE2("Removed pid %T, nprocs = %u\n", retval, nprocs);
3099         }
3100 	break;
3101     }
3102     case 1:
3103     case 2: /* create/delete/lookup resource */
3104 	rix = rand_bits(&rnd, FRENZY_RESOURCES_MAX_BITS) % FRENZY_RESOURCES_MAX;
3105 	inc = primes[rand_bits(&rnd, 2)];
3106 	while (enif_mutex_trylock(resv[rix].lock) == EBUSY) {
3107 	    rix = (rix + inc) % FRENZY_RESOURCES_MAX;
3108 	}
3109         if (resv[rix].stopped) {
3110             retval = atom_done;
3111             enif_mutex_unlock(resv[rix].lock);
3112             break;
3113         }
3114         else if (resv[rix].obj == NULL) {
3115 	    r = enif_alloc_resource(frenzy_resource_type,
3116 				    sizeof(struct frenzy_resource));
3117 	    resv[rix].obj = r;
3118             resv[rix].alloc_cnt++;
3119 	    r->rix = rix;
3120             for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
3121                 r->monv[mix].lock = enif_mutex_create("nif_SUITE:monitor_frenzy.monv.lock");
3122                 r->monv[mix].state = MON_FREE;
3123                 r->monv[mix].use_cnt = 0;
3124                 r->monv[mix].pid.pid = 0; /* null-pid */
3125             }
3126             DBG_TRACE2("New resource at r=%p rix=%u\n", r, rix);
3127 	}
3128         else {
3129             unsigned int resource_op = rand_bits(&rnd, 3);
3130             r = resv[rix].obj;
3131             if (resource_op == 0) {      /* delete resource */
3132                 resv[rix].obj = NULL;
3133                 resv[rix].release_cnt++;
3134                 enif_mutex_unlock(resv[rix].lock);
3135                 DBG_TRACE2("Delete resource at r=%p rix=%u\n", r, rix);
3136                 enif_release_resource(r);
3137                 retval = atom_ok;
3138                 break;
3139             }
3140             else if (resource_op == 1) {  /* return resource */
3141                 retval = enif_make_resource(env, r);
3142                 enif_mutex_unlock(resv[rix].lock);
3143                 break;
3144             }
3145         }
3146         enif_keep_resource(r);
3147         enif_mutex_unlock(resv[rix].lock);
3148 
3149         /* monitor/demonitor */
3150 
3151         mix = rand_bits(&rnd, FRENZY_MONITORS_MAX_BITS) % FRENZY_MONITORS_MAX;
3152         inc = primes[rand_bits(&rnd, 2)];
3153         while (enif_mutex_trylock(r->monv[mix].lock) == EBUSY) {
3154             mix = (mix + inc) % FRENZY_MONITORS_MAX;
3155         }
3156         switch (r->monv[mix].state) {
3157         case MON_FREE:
3158         case MON_FREE_DOWN:
3159         case MON_FREE_DEMONITOR: {   /* do monitor */
3160             /*
3161              * Use an old possibly larger value of 'nprocs', to increase
3162              * probability of monitoring an already terminated process
3163              */
3164             my_nprocs = old_nprocs;
3165             if (my_nprocs > 0) {
3166                 int save_state = r->monv[mix].state;
3167                 ref_ix = rand_bits(&rnd, FRENZY_PROCS_MAX_BITS) % my_nprocs;
3168                 pix = proc_refs[ref_ix] - procs;
3169                 r->monv[mix].pid.pid = procs[pix].pid.pid; /* "atomic" */
3170                 r->monv[mix].state = MON_TRYING;
3171                 rv = enif_monitor_process(env, r, &r->monv[mix].pid, &r->monv[mix].mon);
3172                 if (rv == 0) {
3173                     r->monv[mix].state = MON_ACTIVE;
3174                     r->monv[mix].use_cnt++;
3175                     DBG_TRACE3("Monitor from r=%p rix=%u to %T\n",
3176                                  r, r->rix, r->monv[mix].pid.pid);
3177                 }
3178                 else {
3179                     r->monv[mix].state = save_state;
3180                     DBG_TRACE4("Monitor from r=%p rix=%u to %T FAILED with %d\n",
3181                                r, r->rix, r->monv[mix].pid.pid, rv);
3182                 }
3183                 retval = enif_make_int(env,rv);
3184             }
3185             else {
3186                 DBG_TRACE0("No pids to monitor\n");
3187                 retval = atom_ok;
3188             }
3189             break;
3190         }
3191         case MON_ACTIVE: /* do demonitor */
3192             rv = enif_demonitor_process(env, r, &r->monv[mix].mon);
3193             if (rv == 0) {
3194                 DBG_TRACE3("Demonitor from r=%p rix=%u to %T\n",
3195                            r, r->rix, r->monv[mix].pid.pid);
3196                 r->monv[mix].state = MON_FREE_DEMONITOR;
3197             }
3198             else {
3199                 DBG_TRACE4("Demonitor from r=%p rix=%u to %T FAILED with %d\n",
3200                            r, r->rix, r->monv[mix].pid.pid, rv);
3201                 r->monv[mix].state = MON_PENDING;
3202             }
3203             retval = enif_make_int(env,rv);
3204             break;
3205 
3206         case MON_PENDING: /* waiting for 'down' callback, do nothing */
3207             retval = atom_ok;
3208             break;
3209         default:
3210             abort();
3211             break;
3212         }
3213         enif_mutex_unlock(r->monv[mix].lock);
3214         enif_release_resource(r);
3215 	break;
3216 
3217     case 3: /* no-op */
3218         retval = atom_ok;
3219         break;
3220     }
3221 
3222     {
3223         int percent = (rand_bits(&rnd, 6) + 1) * 2;  /* 2 to 128 */
3224         if (percent <= 100)
3225             enif_consume_timeslice(env, percent);
3226     }
3227 
3228     return retval;
3229 }
3230 
frenzy_resource_dtor(ErlNifEnv * env,void * obj)3231 static void frenzy_resource_dtor(ErlNifEnv* env, void* obj)
3232 {
3233     struct frenzy_resource* r = (struct frenzy_resource*) obj;
3234     unsigned int mix;
3235 
3236     DBG_TRACE2("DTOR r=%p rix=%u\n", r, r->rix);
3237 
3238     enif_mutex_lock(resv[r->rix].lock);
3239     resv[r->rix].dtor_cnt++;
3240     enif_mutex_unlock(resv[r->rix].lock);
3241 
3242     for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
3243         assert(r->monv[mix].state != MON_PENDING);
3244         enif_mutex_destroy(r->monv[mix].lock);
3245         r->monv[mix].lock = NULL;
3246     }
3247 
3248 }
3249 
frenzy_resource_down(ErlNifEnv * env,void * obj,ErlNifPid * pid,ErlNifMonitor * mon)3250 static void frenzy_resource_down(ErlNifEnv* env, void* obj, ErlNifPid* pid,
3251                                  ErlNifMonitor* mon)
3252 {
3253     struct frenzy_resource* r = (struct frenzy_resource*) obj;
3254     unsigned int mix;
3255 
3256     DBG_TRACE3("DOWN pid=%T, r=%p rix=%u\n", pid->pid, r, r->rix);
3257 
3258     for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
3259         int state1 = r->monv[mix].state;
3260         /* First do dirty access of pid and state without the lock */
3261         if (r->monv[mix].pid.pid == pid->pid && state1 >= MON_TRYING) {
3262             int state2;
3263             enif_mutex_lock(r->monv[mix].lock);
3264             state2 = r->monv[mix].state;
3265             if (state2 >= MON_ACTIVE) {
3266                 if (enif_compare_monitors(mon, &r->monv[mix].mon) == 0) {
3267                     r->monv[mix].state = MON_FREE_DOWN;
3268                     enif_mutex_unlock(r->monv[mix].lock);
3269                     return;
3270                 }
3271             }
3272             else {
3273                 assert(state2 != MON_TRYING);
3274                 assert(state1 == MON_TRYING ||  /* racing monitor failed */
3275                        state2 == MON_FREE_DEMONITOR || /* racing demonitor */
3276                        state2 == MON_FREE_DOWN);       /* racing down */
3277             }
3278             enif_mutex_unlock(r->monv[mix].lock);
3279         }
3280     }
3281     enif_fprintf(stderr, "DOWN called for unknown monitor\n");
3282     abort();
3283 }
3284 
3285 /*********** testing ioq ************/
3286 
ioq_resource_dtor(ErlNifEnv * env,void * obj)3287 static void ioq_resource_dtor(ErlNifEnv* env, void* obj) {
3288 
3289 }
3290 
3291 #ifndef __WIN32__
writeiovec(ErlNifEnv * env,ERL_NIF_TERM term,ERL_NIF_TERM * tail,ErlNifIOQueue * q,int fd)3292 static int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail, ErlNifIOQueue *q, int fd) {
3293     ErlNifIOVec vec, *iovec = &vec;
3294     SysIOVec *sysiovec;
3295     int saved_errno;
3296     int iovcnt, n;
3297 
3298     if (!enif_inspect_iovec(env, 64, term, tail, &iovec))
3299         return -2;
3300 
3301     if (enif_ioq_size(q) > 0) {
3302         /* If the I/O queue contains data we enqueue the iovec and then
3303            peek the data to write out of the queue. */
3304         if (!enif_ioq_enqv(q, iovec, 0))
3305             return -3;
3306 
3307         sysiovec = enif_ioq_peek(q, &iovcnt);
3308     } else {
3309         /* If the I/O queue is empty we skip the trip through it. */
3310         iovcnt = iovec->iovcnt;
3311         sysiovec = iovec->iov;
3312     }
3313 
3314     /* Attempt to write the data */
3315     n = writev(fd, (struct iovec*)sysiovec, iovcnt);
3316     saved_errno = errno;
3317 
3318     if (enif_ioq_size(q) == 0) {
3319         /* If the I/O queue was initially empty we enqueue any
3320            remaining data into the queue for writing later. */
3321         if (n >= 0 && !enif_ioq_enqv(q, iovec, n))
3322             return -3;
3323     } else {
3324         /* Dequeue any data that was written from the queue. */
3325         if (n > 0 && !enif_ioq_deq(q, n, NULL))
3326             return -4;
3327     }
3328 
3329     /* return n, which is either number of bytes written or -1 if
3330        some error happened */
3331     errno = saved_errno;
3332     return n;
3333 }
3334 #endif
3335 
ioq(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3336 static ERL_NIF_TERM ioq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3337 {
3338     struct ioq_resource *ioq;
3339     ERL_NIF_TERM ret;
3340     if (enif_is_identical(argv[0], enif_make_atom(env, "create"))) {
3341         ErlNifIOQueue *q = enif_ioq_create(ERL_NIF_IOQ_NORMAL);
3342         ioq = (struct ioq_resource *)enif_alloc_resource(ioq_resource_type,
3343                                                          sizeof(*ioq));
3344         ioq->q = q;
3345         ret = enif_make_resource(env, ioq);
3346         enif_release_resource(ioq);
3347         return ret;
3348     } else if (enif_is_identical(argv[0], enif_make_atom(env, "inspect"))) {
3349         ErlNifIOVec vec, *iovec = NULL;
3350         int i, iovcnt;
3351         ERL_NIF_TERM *elems, tail, list;
3352         ErlNifEnv *myenv = NULL;
3353 
3354         if (enif_is_identical(argv[2], enif_make_atom(env, "use_stack")))
3355             iovec = &vec;
3356         if (enif_is_identical(argv[3], enif_make_atom(env, "use_env")))
3357             myenv = env;
3358         if (!enif_inspect_iovec(myenv, ~(size_t)0, argv[1], &tail, &iovec))
3359             return enif_make_badarg(env);
3360 
3361         iovcnt = iovec->iovcnt;
3362         elems = enif_alloc(sizeof(ERL_NIF_TERM) * iovcnt);
3363 
3364         for (i = 0; i < iovcnt; i++) {
3365             ErlNifBinary bin;
3366             if (!enif_alloc_binary(iovec->iov[i].iov_len, &bin)) {
3367                 enif_free_iovec(iovec);
3368                 enif_free(elems);
3369                 return enif_make_badarg(env);
3370             }
3371             memcpy(bin.data, iovec->iov[i].iov_base, iovec->iov[i].iov_len);
3372             elems[i] = enif_make_binary(env, &bin);
3373         }
3374 
3375         if (!myenv)
3376             enif_free_iovec(iovec);
3377 
3378 	list = enif_make_list_from_array(env, elems, iovcnt);
3379 	enif_free(elems);
3380 	return list;
3381     } else {
3382         unsigned skip;
3383         if (!enif_get_resource(env, argv[1], ioq_resource_type, (void**)&ioq)
3384             || !ioq->q)
3385             return enif_make_badarg(env);
3386 
3387         if (enif_is_identical(argv[0], enif_make_atom(env, "example"))) {
3388 #ifndef __WIN32__
3389             int fd[2], res = 0, cnt = 0;
3390             ERL_NIF_TERM tail;
3391             char buff[255];
3392             res = pipe(fd);
3393             assert(res == 0);
3394             fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_NONBLOCK);
3395             fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL) | O_NONBLOCK);
3396 
3397             /* Write until the pipe buffer is full, which should result in data
3398              * being queued up. */
3399             for (res = 0; res >= 0; ) {
3400                 cnt += res;
3401                 res = writeiovec(env, argv[2], &tail, ioq->q, fd[1]);
3402             }
3403 
3404             /* Flush the queue while reading from the other end of the pipe. */
3405             tail = enif_make_list(env, 0);
3406             while (enif_ioq_size(ioq->q) > 0) {
3407                 res = writeiovec(env, tail, &tail, ioq->q, fd[1]);
3408 
3409                 if (res < 0 && errno != EAGAIN) {
3410                     break;
3411                 } else if (res > 0) {
3412                     cnt += res;
3413                 }
3414 
3415                 for (res = 0; res >= 0; ) {
3416                     cnt -= res;
3417                     res = read(fd[0], buff, sizeof(buff));
3418                 }
3419             }
3420 
3421             close(fd[0]);
3422             close(fd[1]);
3423 
3424             /* Check that we read as much as we wrote */
3425             if (cnt == 0 && enif_ioq_size(ioq->q) == 0)
3426                 return enif_make_atom(env, "true");
3427 
3428             return enif_make_int(env, cnt);
3429 #else
3430             return enif_make_atom(env, "true");
3431 #endif
3432         }
3433         if (enif_is_identical(argv[0], enif_make_atom(env, "destroy"))) {
3434             enif_ioq_destroy(ioq->q);
3435             ioq->q = NULL;
3436             return enif_make_atom(env, "false");
3437         } else if (enif_is_identical(argv[0], enif_make_atom(env, "enqv"))) {
3438             ErlNifIOVec vec, *iovec = &vec;
3439             ERL_NIF_TERM tail;
3440 
3441             if (!enif_get_uint(env, argv[3], &skip))
3442                 return enif_make_badarg(env);
3443             if (!enif_inspect_iovec(env, ~0ul, argv[2], &tail, &iovec))
3444                 return enif_make_badarg(env);
3445             if (!enif_ioq_enqv(ioq->q, iovec, skip))
3446                 return enif_make_badarg(env);
3447 
3448             return enif_make_atom(env, "true");
3449         } else if (enif_is_identical(argv[0], enif_make_atom(env, "enqb"))) {
3450             ErlNifBinary bin;
3451             if (!enif_get_uint(env, argv[3], &skip) ||
3452                 !enif_inspect_binary(env, argv[2], &bin))
3453                 return enif_make_badarg(env);
3454 
3455             if (!enif_ioq_enq_binary(ioq->q, &bin, skip))
3456                 return enif_make_badarg(env);
3457 
3458             return enif_make_atom(env, "true");
3459         } else if (enif_is_identical(argv[0], enif_make_atom(env, "enqbraw"))) {
3460             ErlNifBinary bin;
3461             ErlNifBinary localbin;
3462 	    int i;
3463             if (!enif_get_uint(env, argv[3], &skip) ||
3464                 !enif_inspect_binary(env, argv[2], &bin) ||
3465                 !enif_alloc_binary(bin.size, &localbin))
3466                 return enif_make_badarg(env);
3467 
3468             memcpy(localbin.data, bin.data, bin.size);
3469             i = enif_ioq_enq_binary(ioq->q, &localbin, skip);
3470 	    if (!i)
3471 		return enif_make_badarg(env);
3472 	    else
3473 		return enif_make_atom(env, "true");
3474         } else if (enif_is_identical(argv[0], enif_make_atom(env, "peek_head"))) {
3475             ERL_NIF_TERM head_term;
3476 
3477             if(enif_ioq_peek_head(env, ioq->q, NULL, &head_term)) {
3478                 return enif_make_tuple2(env,
3479                     enif_make_atom(env, "true"), head_term);
3480             }
3481 
3482             return enif_make_atom(env, "false");
3483         } else if (enif_is_identical(argv[0], enif_make_atom(env, "peek"))) {
3484             int iovlen, num, i, off = 0;
3485             SysIOVec *iov = enif_ioq_peek(ioq->q, &iovlen);
3486             ErlNifBinary bin;
3487 
3488             if (!enif_get_int(env, argv[2], &num) || !enif_alloc_binary(num, &bin))
3489                 return enif_make_badarg(env);
3490 
3491             for (i = 0; i < iovlen && num > 0; i++) {
3492                 int to_copy = num < iov[i].iov_len ? num : iov[i].iov_len;
3493                 memcpy(bin.data + off, iov[i].iov_base, to_copy);
3494                 num -= to_copy;
3495                 off += to_copy;
3496             }
3497 
3498             return enif_make_binary(env, &bin);
3499         } else if (enif_is_identical(argv[0], enif_make_atom(env, "deq"))) {
3500             int num;
3501             size_t sz;
3502             ErlNifUInt64 sz64;
3503             if (!enif_get_int(env, argv[2], &num))
3504                 return enif_make_badarg(env);
3505 
3506             if (!enif_ioq_deq(ioq->q, num, &sz))
3507                 return enif_make_badarg(env);
3508 
3509             sz64 = sz;
3510 
3511             return enif_make_uint64(env, sz64);
3512         } else if (enif_is_identical(argv[0], enif_make_atom(env, "size"))) {
3513             ErlNifUInt64 size = enif_ioq_size(ioq->q);
3514             return enif_make_uint64(env, size);
3515         }
3516     }
3517 
3518     return enif_make_badarg(env);
3519 }
3520 
make_bool(ErlNifEnv * env,int bool)3521 static ERL_NIF_TERM make_bool(ErlNifEnv* env, int bool)
3522 {
3523     return bool ? atom_true : atom_false;
3524 }
3525 
get_local_pid_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3526 static ERL_NIF_TERM get_local_pid_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3527 {
3528     ErlNifPid pid;
3529     ERL_NIF_TERM pid_bin;
3530     int ret = enif_get_local_pid(env, argv[0], &pid);
3531 
3532     memcpy(enif_make_new_binary(env, sizeof(ErlNifPid), &pid_bin),
3533            &pid, sizeof(ErlNifPid));
3534 
3535     return enif_make_tuple2(env, make_bool(env, ret), pid_bin);
3536 }
3537 
make_pid_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3538 static ERL_NIF_TERM make_pid_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3539 {
3540     ErlNifPid pid;
3541 
3542     if (!get_pidbin(env, argv[0], &pid))
3543         return enif_make_badarg(env);
3544 
3545     return enif_make_pid(env, &pid);
3546 }
3547 
set_pid_undefined_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3548 static ERL_NIF_TERM set_pid_undefined_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3549 {
3550     ErlNifPid pid;
3551     ERL_NIF_TERM pid_bin;
3552 
3553     enif_set_pid_undefined(&pid);
3554     memcpy(enif_make_new_binary(env, sizeof(ErlNifPid), &pid_bin),
3555            &pid, sizeof(ErlNifPid));
3556 
3557     return pid_bin;
3558 }
3559 
is_pid_undefined_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3560 static ERL_NIF_TERM is_pid_undefined_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3561 {
3562     ErlNifPid pid;
3563 
3564     if (!get_pidbin(env, argv[0], &pid))
3565         return enif_make_badarg(env);
3566 
3567     return make_bool(env, enif_is_pid_undefined(&pid));
3568 }
3569 
compare_pids_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3570 static ERL_NIF_TERM compare_pids_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3571 {
3572     ErlNifPid a, b;
3573 
3574     if (!get_pidbin(env, argv[0], &a) || !get_pidbin(env, argv[1], &b))
3575         return enif_make_badarg(env);
3576 
3577     return enif_make_int(env, enif_compare_pids(&a, &b));
3578 }
3579 
term_type_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3580 static ERL_NIF_TERM term_type_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3581 {
3582     switch (enif_term_type(env, argv[0])) {
3583     case ERL_NIF_TERM_TYPE_ATOM:
3584         return enif_make_atom(env, "atom");
3585     case ERL_NIF_TERM_TYPE_BITSTRING:
3586         return enif_make_atom(env, "bitstring");
3587     case ERL_NIF_TERM_TYPE_FLOAT:
3588         return enif_make_atom(env, "float");
3589     case ERL_NIF_TERM_TYPE_FUN:
3590         return enif_make_atom(env, "fun");
3591     case ERL_NIF_TERM_TYPE_INTEGER:
3592         return enif_make_atom(env, "integer");
3593     case ERL_NIF_TERM_TYPE_LIST:
3594         return enif_make_atom(env, "list");
3595     case ERL_NIF_TERM_TYPE_MAP:
3596         return enif_make_atom(env, "map");
3597     case ERL_NIF_TERM_TYPE_PID:
3598         return enif_make_atom(env, "pid");
3599     case ERL_NIF_TERM_TYPE_PORT:
3600         return enif_make_atom(env, "port");
3601     case ERL_NIF_TERM_TYPE_REFERENCE:
3602         return enif_make_atom(env, "reference");
3603     case ERL_NIF_TERM_TYPE_TUPLE:
3604         return enif_make_atom(env, "tuple");
3605     default:
3606         return enif_make_badarg(env);
3607     }
3608 }
3609 
3610 static ErlNifFunc nif_funcs[] =
3611 {
3612     {"lib_version", 0, lib_version},
3613     {"call_history", 0, call_history},
3614     {"hold_nif_mod_priv_data", 1, hold_nif_mod_priv_data},
3615     {"nif_mod_call_history", 0, nif_mod_call_history},
3616     {"list_seq", 1, list_seq},
3617     {"type_test", 0, type_test},
3618     {"tuple_2_list", 1, tuple_2_list},
3619     {"is_identical",2,is_identical},
3620     {"compare",2,compare},
3621     {"hash_nif",3,hash_nif},
3622     {"many_args_100", 100, many_args_100},
3623     {"clone_bin", 1, clone_bin},
3624     {"make_sub_bin", 3, make_sub_bin},
3625     {"string_to_bin", 2, string_to_bin},
3626     {"atom_to_bin", 2, atom_to_bin},
3627     {"macros", 1, macros},
3628     {"tuple_2_list_and_tuple",1,tuple_2_list_and_tuple},
3629     {"iolist_2_bin", 1, iolist_2_bin},
3630     {"get_resource_type", 1, get_resource_type},
3631     {"alloc_resource", 2, alloc_resource},
3632     {"make_resource", 1, make_resource},
3633     {"get_resource", 2, get_resource},
3634     {"release_resource", 1, release_resource},
3635     {"release_resource_from_thread", 1, release_resource_from_thread},
3636     {"last_resource_dtor_call_nif", 0, last_resource_dtor_call_nif},
3637     {"make_new_resource", 2, make_new_resource},
3638     {"check_is", 11, check_is},
3639     {"check_is_exception", 0, check_is_exception},
3640     {"length_test", 6, length_test},
3641     {"make_atoms", 0, make_atoms},
3642     {"make_strings", 0, make_strings},
3643     {"make_new_resource", 2, make_new_resource},
3644     {"make_new_resource_binary", 1, make_new_resource_binary},
3645     {"send_list_seq", 2, send_list_seq},
3646     {"send_new_blob", 2, send_new_blob},
3647     {"alloc_msgenv", 0, alloc_msgenv},
3648     {"clear_msgenv", 1, clear_msgenv},
3649     {"grow_blob", 2, grow_blob},
3650     {"grow_blob", 3, grow_blob},
3651     {"send_blob", 2, send_blob},
3652     {"send3_blob", 3, send3_blob},
3653     {"send_blob_thread", 3, send_blob_thread},
3654     {"join_send_thread", 1, join_send_thread},
3655     {"copy_blob", 1, copy_blob},
3656     {"send_term", 2, send_term},
3657     {"send_copy_term", 2, send_copy_term},
3658     {"reverse_list",1, reverse_list},
3659     {"echo_int", 1, echo_int},
3660     {"type_sizes", 0, type_sizes},
3661     {"otp_9668_nif", 1, otp_9668_nif},
3662     {"otp_9828_nif", 1, otp_9828_nif},
3663     {"consume_timeslice_nif", 2, consume_timeslice_nif},
3664     {"call_nif_schedule", 2, call_nif_schedule},
3665     {"call_nif_exception", 1, call_nif_exception},
3666     {"call_nif_nan_or_inf", 1, call_nif_nan_or_inf},
3667     {"call_nif_atom_too_long", 1, call_nif_atom_too_long},
3668     {"is_map_nif", 1, is_map_nif},
3669     {"get_map_size_nif", 1, get_map_size_nif},
3670     {"make_new_map_nif", 0, make_new_map_nif},
3671     {"make_map_put_nif", 3, make_map_put_nif},
3672     {"get_map_value_nif", 2, get_map_value_nif},
3673     {"make_map_update_nif", 3, make_map_update_nif},
3674     {"make_map_remove_nif", 2, make_map_remove_nif},
3675     {"maps_from_list_nif", 1, maps_from_list_nif},
3676     {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif},
3677     {"monotonic_time", 1, monotonic_time},
3678     {"time_offset", 1, time_offset},
3679     {"convert_time_unit", 3, convert_time_unit},
3680     {"now_time", 0, now_time},
3681     {"cpu_time", 0, cpu_time},
3682     {"unique_integer_nif", 1, unique_integer},
3683     {"is_process_alive_nif", 1, is_process_alive},
3684     {"is_port_alive_nif", 1, is_port_alive},
3685     {"term_to_binary_nif", 2, term_to_binary},
3686     {"binary_to_term_nif", 3, binary_to_term},
3687     {"port_command_nif", 2, port_command},
3688     {"format_term_nif", 2, format_term},
3689     {"select_nif", 6, select_nif},
3690 #ifndef __WIN32__
3691     {"pipe_nif", 0, pipe_nif},
3692     {"write_nif", 2, write_nif},
3693     {"dupe_resource_nif", 1, dupe_resource_nif},
3694     {"read_nif", 2, read_nif},
3695     {"is_closed_nif", 1, is_closed_nif},
3696     {"clear_select_nif", 1, clear_select_nif},
3697 #endif
3698     {"last_fd_stop_call", 0, last_fd_stop_call},
3699     {"alloc_monitor_resource_nif", 0, alloc_monitor_resource_nif},
3700     {"monitor_process_nif", 4, monitor_process_nif},
3701     {"demonitor_process_nif", 2, demonitor_process_nif},
3702     {"compare_monitors_nif", 2, compare_monitors_nif},
3703     {"make_monitor_term_nif", 1, make_monitor_term_nif},
3704     {"monitor_frenzy_nif", 4, monitor_frenzy_nif},
3705     {"whereis_send", 3, whereis_send},
3706     {"whereis_term", 2, whereis_term},
3707     {"whereis_thd_lookup", 3, whereis_thd_lookup},
3708     {"whereis_thd_result", 1, whereis_thd_result},
3709     {"ioq_nif", 1, ioq},
3710     {"ioq_nif", 2, ioq},
3711     {"ioq_nif", 3, ioq},
3712     {"ioq_nif", 4, ioq},
3713     {"get_local_pid_nif", 1, get_local_pid_nif},
3714     {"make_pid_nif", 1, make_pid_nif},
3715     {"set_pid_undefined_nif", 0, set_pid_undefined_nif},
3716     {"is_pid_undefined_nif", 1, is_pid_undefined_nif},
3717     {"compare_pids_nif", 2, compare_pids_nif},
3718     {"term_type_nif", 1, term_type_nif}
3719 };
3720 
3721 ERL_NIF_INIT(nif_SUITE,nif_funcs,load,NULL,upgrade,unload)
3722