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 
2127     /* build same map in dynamic env */
2128     ErlNifEnv* dynenv = enif_alloc_env();
2129     ERL_NIF_TERM map_out2 = enif_make_atom(env, "undefined");
2130     int ret2 = enif_make_map_put(dynenv,
2131                                  enif_make_copy(dynenv, argv[0]),
2132                                  enif_make_copy(dynenv, argv[1]),
2133                                  enif_make_copy(dynenv, argv[2]),
2134                                  &map_out2);
2135     if (ret != ret2 || !enif_is_identical(map_out, map_out2))
2136         map_out = enif_make_string(env, "dynenv failure", ERL_NIF_LATIN1);
2137     enif_free_env(dynenv);
2138 
2139     return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
2140 }
get_map_value_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2141 static ERL_NIF_TERM get_map_value_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2142 {
2143     ERL_NIF_TERM value = enif_make_atom(env, "undefined");
2144     int ret = enif_get_map_value(env, argv[0], argv[1], &value);
2145     return enif_make_tuple2(env, enif_make_int(env,ret), value);
2146 
2147 }
make_map_update_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2148 static ERL_NIF_TERM make_map_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2149 {
2150     ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
2151     int ret = enif_make_map_update(env, argv[0], argv[1], argv[2], &map_out);
2152 
2153     /* build same map in dynamic env */
2154     ErlNifEnv* dynenv = enif_alloc_env();
2155     ERL_NIF_TERM map_out2 = enif_make_atom(env, "undefined");
2156     int ret2 = enif_make_map_update(dynenv,
2157                                     enif_make_copy(dynenv, argv[0]),
2158                                     enif_make_copy(dynenv, argv[1]),
2159                                     enif_make_copy(dynenv, argv[2]),
2160                                     &map_out2);
2161     if (ret != ret2 || !enif_is_identical(map_out, map_out2))
2162         map_out = enif_make_string(env, "dynenv failure", ERL_NIF_LATIN1);
2163     enif_free_env(dynenv);
2164 
2165     return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
2166 }
make_map_remove_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2167 static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2168 {
2169     ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
2170     int ret = enif_make_map_remove(env, argv[0], argv[1], &map_out);
2171 
2172     /* build same map in dynamic env */
2173     ErlNifEnv* dynenv = enif_alloc_env();
2174     ERL_NIF_TERM map_out2 = enif_make_atom(env, "undefined");
2175     int ret2 = enif_make_map_remove(dynenv,
2176                                     enif_make_copy(dynenv, argv[0]),
2177                                     enif_make_copy(dynenv, argv[1]),
2178                                     &map_out2);
2179     if (ret != ret2 || !enif_is_identical(map_out, map_out2))
2180         map_out = enif_make_string(env, "dynenv failure", ERL_NIF_LATIN1);
2181     enif_free_env(dynenv);
2182 
2183     return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
2184 }
2185 
2186 /* maps */
maps_from_list_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2187 static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2188 {
2189     ERL_NIF_TERM *keys, *values;
2190     ERL_NIF_TERM result, cell;
2191     unsigned count;
2192 
2193     assert(argc == 1);
2194     if (!enif_get_list_length(env, argv[0], &count)) {
2195         return enif_make_badarg(env);
2196     }
2197 
2198     keys = enif_alloc(sizeof(ERL_NIF_TERM) * count * 2);
2199     values = keys + count;
2200 
2201     cell = argv[0];
2202     count = 0;
2203 
2204     while (!enif_is_empty_list(env, cell)) {
2205         const ERL_NIF_TERM *pair;
2206         ERL_NIF_TERM tuple;
2207         int arity;
2208 
2209         if (!enif_get_list_cell(env, cell, &tuple, &cell)
2210             || !enif_get_tuple(env, tuple, &arity, &pair)
2211             || arity != 2) {
2212             enif_free(keys);
2213             return enif_make_badarg(env);
2214         }
2215 
2216         keys[count] = pair[0];
2217         values[count] = pair[1];
2218 
2219         count++;
2220     }
2221 
2222     if (!enif_make_map_from_arrays(env, keys, values, count, &result)) {
2223         result = enif_make_atom(env, "has_duplicate_keys");
2224     }
2225 
2226     enif_free(keys);
2227 
2228     return result;
2229 }
2230 
sorted_list_from_maps_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2231 static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
2232 
2233     ERL_NIF_TERM map = argv[0];
2234     ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */
2235     ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */
2236     ERL_NIF_TERM key, value, k2, v2;
2237     ErlNifMapIterator iter_f;
2238     ErlNifMapIterator iter_b;
2239     int cnt, next_ret, prev_ret;
2240 
2241     assert(argc == 1);
2242     if (!enif_is_map(env, map))
2243 	return enif_make_int(env, __LINE__);
2244 
2245     if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_FIRST))
2246 	return enif_make_int(env, __LINE__);
2247 
2248     cnt = 0;
2249     next_ret = 1;
2250     while(enif_map_iterator_get_pair(env,&iter_f,&key,&value)) {
2251 	if (!next_ret)
2252 	    return enif_make_int(env, __LINE__);
2253 	list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f);
2254 	next_ret = enif_map_iterator_next(env,&iter_f);
2255 	cnt++;
2256     }
2257     if (cnt && next_ret)
2258 	return enif_make_int(env, __LINE__);
2259 
2260     if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_LAST))
2261 	return enif_make_int(env, __LINE__);
2262 
2263     cnt = 0;
2264     prev_ret = 1;
2265     while(enif_map_iterator_get_pair(env,&iter_b,&key,&value)) {
2266 	if (!prev_ret)
2267 	    return enif_make_int(env, __LINE__);
2268 
2269 	/* Test that iter_f can step "backwards" */
2270 	if (!enif_map_iterator_prev(env,&iter_f)
2271 	    || !enif_map_iterator_get_pair(env,&iter_f,&k2,&v2)
2272 	    || k2 != key || v2 != value) {
2273 	    return enif_make_int(env, __LINE__);
2274 	}
2275 
2276 	list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b);
2277 	prev_ret = enif_map_iterator_prev(env,&iter_b);
2278 	cnt++;
2279     }
2280 
2281     if (cnt) {
2282 	if (prev_ret || enif_map_iterator_prev(env,&iter_f))
2283 	    return enif_make_int(env, __LINE__);
2284 
2285 	/* Test that iter_b can step "backwards" one step */
2286 	if (!enif_map_iterator_next(env, &iter_b)
2287 	    || !enif_map_iterator_get_pair(env,&iter_b,&k2,&v2)
2288 	    || k2 != key || v2 != value)
2289 	    return enif_make_int(env, __LINE__);
2290     }
2291 
2292     enif_map_iterator_destroy(env, &iter_f);
2293     enif_map_iterator_destroy(env, &iter_b);
2294 
2295     return enif_make_tuple2(env, list_f, list_b);
2296 }
2297 
2298 
monotonic_time(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2299 static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2300 {
2301     ErlNifTimeUnit time_unit;
2302 
2303     assert(argc == 1);
2304     if (enif_compare(argv[0], atom_second) == 0)
2305 	time_unit = ERL_NIF_SEC;
2306     else if (enif_compare(argv[0], atom_millisecond) == 0)
2307 	time_unit = ERL_NIF_MSEC;
2308     else if (enif_compare(argv[0], atom_microsecond) == 0)
2309 	time_unit = ERL_NIF_USEC;
2310     else if (enif_compare(argv[0], atom_nanosecond) == 0)
2311 	time_unit = ERL_NIF_NSEC;
2312     else
2313 	time_unit = 4711; /* invalid time unit */
2314 
2315     return enif_make_int64(env, enif_monotonic_time(time_unit));
2316 }
2317 
time_offset(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2318 static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2319 {
2320     ErlNifTimeUnit time_unit;
2321 
2322     assert(argc == 1);
2323     if (enif_compare(argv[0], atom_second) == 0)
2324 	time_unit = ERL_NIF_SEC;
2325     else if (enif_compare(argv[0], atom_millisecond) == 0)
2326 	time_unit = ERL_NIF_MSEC;
2327     else if (enif_compare(argv[0], atom_microsecond) == 0)
2328 	time_unit = ERL_NIF_USEC;
2329     else if (enif_compare(argv[0], atom_nanosecond) == 0)
2330 	time_unit = ERL_NIF_NSEC;
2331     else
2332 	time_unit = 4711; /* invalid time unit */
2333     return enif_make_int64(env, enif_time_offset(time_unit));
2334 }
2335 
convert_time_unit(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2336 static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2337 {
2338     ErlNifSInt64 i64;
2339     ErlNifTime val;
2340     ErlNifTimeUnit from, to;
2341 
2342     assert(argc == 3);
2343     if (!enif_get_int64(env, argv[0], &i64))
2344 	return enif_make_badarg(env);
2345 
2346     val = (ErlNifTime) i64;
2347 
2348     if (enif_compare(argv[1], atom_second) == 0)
2349 	from = ERL_NIF_SEC;
2350     else if (enif_compare(argv[1], atom_millisecond) == 0)
2351 	from = ERL_NIF_MSEC;
2352     else if (enif_compare(argv[1], atom_microsecond) == 0)
2353 	from = ERL_NIF_USEC;
2354     else if (enif_compare(argv[1], atom_nanosecond) == 0)
2355 	from = ERL_NIF_NSEC;
2356     else
2357 	from = 4711; /* invalid time unit */
2358 
2359     if (enif_compare(argv[2], atom_second) == 0)
2360 	to = ERL_NIF_SEC;
2361     else if (enif_compare(argv[2], atom_millisecond) == 0)
2362 	to = ERL_NIF_MSEC;
2363     else if (enif_compare(argv[2], atom_microsecond) == 0)
2364 	to = ERL_NIF_USEC;
2365     else if (enif_compare(argv[2], atom_nanosecond) == 0)
2366 	to = ERL_NIF_NSEC;
2367     else
2368 	to = 4711; /* invalid time unit */
2369 
2370     return enif_make_int64(env, enif_convert_time_unit(val, from, to));
2371 }
2372 
now_time(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2373 static ERL_NIF_TERM now_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2374 {
2375     return enif_now_time(env);
2376 }
2377 
cpu_time(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2378 static ERL_NIF_TERM cpu_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2379 {
2380     return enif_cpu_time(env);
2381 }
2382 
unique_integer(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2383 static ERL_NIF_TERM unique_integer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2384 {
2385     ERL_NIF_TERM atom_pos = enif_make_atom(env,"positive"),
2386         atom_mon = enif_make_atom(env,"monotonic");
2387     ERL_NIF_TERM opts = argv[0], opt;
2388     ErlNifUniqueInteger properties = 0;
2389 
2390     while (!enif_is_empty_list(env, opts)) {
2391 	if (!enif_get_list_cell(env, opts, &opt, &opts))
2392             return enif_make_badarg(env);
2393 
2394         if (enif_compare(opt, atom_pos) == 0)
2395             properties |= ERL_NIF_UNIQUE_POSITIVE;
2396         if (enif_compare(opt, atom_mon) == 0)
2397             properties |= ERL_NIF_UNIQUE_MONOTONIC;
2398     }
2399 
2400     return enif_make_unique_integer(env, properties);
2401 }
2402 
is_process_alive(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2403 static ERL_NIF_TERM is_process_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2404 {
2405     ErlNifPid pid;
2406     if (!enif_get_local_pid(env, argv[0], &pid))
2407         return enif_make_badarg(env);
2408     if (enif_is_process_alive(env, &pid))
2409         return atom_true;
2410     return atom_false;
2411 }
2412 
is_port_alive(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2413 static ERL_NIF_TERM is_port_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2414 {
2415     ErlNifPort port;
2416     if (!enif_get_local_port(env, argv[0], &port))
2417         return enif_make_badarg(env);
2418     if (enif_is_port_alive(env, &port))
2419         return atom_true;
2420     return atom_false;
2421 }
2422 
term_to_binary(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2423 static ERL_NIF_TERM term_to_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2424 {
2425     ErlNifBinary bin;
2426     ErlNifPid pid;
2427     ErlNifEnv *msg_env = env;
2428     ERL_NIF_TERM term;
2429 
2430     if (enif_get_local_pid(env, argv[1], &pid))
2431         msg_env = enif_alloc_env();
2432 
2433     if (!enif_term_to_binary(msg_env, argv[0], &bin))
2434         return enif_make_badarg(env);
2435 
2436     term = enif_make_binary(msg_env, &bin);
2437 
2438     if (msg_env != env) {
2439         enif_send(env, &pid, msg_env, term);
2440         enif_free_env(msg_env);
2441         return atom_true;
2442     } else {
2443         return term;
2444     }
2445 }
2446 
binary_to_term(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2447 static ERL_NIF_TERM binary_to_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2448 {
2449     ErlNifBinary bin;
2450     ERL_NIF_TERM term, dummy, ret_term;
2451     ErlNifPid pid;
2452     ErlNifEnv *msg_env = env;
2453     unsigned int opts;
2454     ErlNifUInt64 ret;
2455 
2456     if (enif_get_local_pid(env, argv[1], &pid))
2457         msg_env = enif_alloc_env();
2458 
2459     if (!enif_inspect_binary(env, argv[0], &bin)
2460 	|| !enif_get_uint(env, argv[2], &opts))
2461         return enif_make_badarg(env);
2462 
2463     /* build dummy heap term first to provoke OTP-15080 */
2464     dummy = enif_make_list_cell(msg_env, atom_true, atom_false);
2465 
2466     ret = enif_binary_to_term(msg_env, bin.data, bin.size, &term,
2467 			      (ErlNifBinaryToTerm)opts);
2468     if (!ret)
2469 	return atom_false;
2470 
2471     ret_term = enif_make_uint64(env, ret);
2472     if (msg_env != env) {
2473         enif_send(env, &pid, msg_env,
2474                   enif_make_tuple2(msg_env, term, dummy));
2475         enif_free_env(msg_env);
2476         return ret_term;
2477     } else {
2478         return enif_make_tuple3(env, ret_term, term, dummy);
2479     }
2480 }
2481 
port_command(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2482 static ERL_NIF_TERM port_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2483 {
2484     ErlNifPort port;
2485 
2486     if (!enif_get_local_port(env, argv[0], &port))
2487         return enif_make_badarg(env);
2488 
2489     if (!enif_port_command(env, &port, NULL, argv[1]))
2490         return enif_make_badarg(env);
2491     return atom_true;
2492 }
2493 
format_term(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2494 static ERL_NIF_TERM format_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2495 {
2496     ErlNifBinary obin;
2497     unsigned int size;
2498 
2499     if (!enif_get_uint(env, argv[0], &size))
2500 	return enif_make_badarg(env);
2501     if (!enif_alloc_binary(size,&obin))
2502 	return enif_make_badarg(env);
2503 
2504     if (enif_snprintf((char*)obin.data, (size_t)size, "%T", argv[1]) < 0)
2505         return atom_false;
2506 
2507     return enif_make_binary(env,&obin);
2508 }
2509 
get_fd(ErlNifEnv * env,ERL_NIF_TERM term,struct fd_resource ** rsrc)2510 static int get_fd(ErlNifEnv* env, ERL_NIF_TERM term, struct fd_resource** rsrc)
2511 {
2512     if (!enif_get_resource(env, term, fd_resource_type, (void**)rsrc)) {
2513         return 0;
2514     }
2515     return 1;
2516 }
2517 
2518 /* Returns: badarg
2519  *    Or an enif_select result, which is a combination of bits:
2520  *    ERL_NIF_SELECT_STOP_CALLED = 1
2521  *    ERL_NIF_SELECT_STOP_SCHEDULED = 2
2522  *    ERL_NIF_SELECT_INVALID_EVENT = 4
2523  *    ERL_NIF_SELECT_FAILED = 8
2524  */
select_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2525 static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2526 {
2527     struct fd_resource* fdr;
2528     enum ErlNifSelectFlags mode;
2529     void* obj;
2530     ErlNifPid nifpid, *pid = NULL;
2531     ERL_NIF_TERM ref_or_msg;
2532     ErlNifEnv* msg_env = NULL;
2533     int retval;
2534 
2535     if (!get_fd(env, argv[0], &fdr)
2536         || !enif_get_uint(env, argv[1], (unsigned int*)&mode)
2537         || !enif_get_resource(env, argv[2], fd_resource_type, &obj))
2538     {
2539         return enif_make_badarg(env);
2540     }
2541 
2542     if (argv[3] != atom_null) {
2543 	if (!enif_get_local_pid(env, argv[3], &nifpid))
2544 	    return enif_make_badarg(env);
2545 	pid = &nifpid;
2546     }
2547     ref_or_msg = argv[4];
2548     if (argv[5] != atom_null) {
2549         msg_env = enif_alloc_env();
2550         ref_or_msg = enif_make_copy(msg_env, ref_or_msg);
2551     }
2552 
2553     fdr->was_selected = 1;
2554     enif_self(env, &fdr->pid);
2555     switch (mode) {
2556     case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_READ:
2557         retval = enif_select_read(env, fdr->fd, obj, pid, ref_or_msg, msg_env);
2558         break;
2559     case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_WRITE:
2560         retval = enif_select_write(env, fdr->fd, obj, pid, ref_or_msg, msg_env);
2561         break;
2562     default:
2563         retval = enif_select(env, fdr->fd, mode, obj, pid, ref_or_msg);
2564     }
2565 
2566     if (msg_env)
2567         enif_free_env(msg_env);
2568 
2569     return enif_make_int(env, retval);
2570 }
2571 
2572 #ifndef __WIN32__
2573 /*
2574  * Create a read-write pipe with two fds (to read and to write)
2575  */
pipe_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2576 static ERL_NIF_TERM pipe_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2577 {
2578     struct fd_resource* read_rsrc;
2579     struct fd_resource* write_rsrc;
2580     ERL_NIF_TERM read_fd, write_fd;
2581     int fds[2], flags;
2582 
2583     if (pipe(fds) < 0)
2584         return enif_make_string(env, "pipe failed", ERL_NIF_LATIN1);
2585 
2586     if ((flags = fcntl(fds[0], F_GETFL, 0)) < 0
2587         || fcntl(fds[0], F_SETFL, flags|O_NONBLOCK) < 0
2588         || (flags = fcntl(fds[1], F_GETFL, 0)) < 0
2589         || fcntl(fds[1], F_SETFL, flags|O_NONBLOCK) < 0) {
2590         close(fds[0]);
2591         close(fds[1]);
2592         return enif_make_string(env, "fcntl failed on pipe", ERL_NIF_LATIN1);
2593     }
2594 
2595     read_rsrc  = enif_alloc_resource(fd_resource_type, sizeof(struct fd_resource));
2596     write_rsrc = enif_alloc_resource(fd_resource_type, sizeof(struct fd_resource));
2597     read_rsrc->fd  = fds[0];
2598     read_rsrc->was_selected = 0;
2599     write_rsrc->fd = fds[1];
2600     write_rsrc->was_selected = 0;
2601     read_fd  = enif_make_resource(env, read_rsrc);
2602     write_fd = enif_make_resource(env, write_rsrc);
2603     enif_release_resource(read_rsrc);
2604     enif_release_resource(write_rsrc);
2605 
2606     return enif_make_tuple2(env,
2607                enif_make_tuple2(env, read_fd, make_pointer(env, read_rsrc)),
2608                enif_make_tuple2(env, write_fd, make_pointer(env, write_rsrc)));
2609 }
2610 
2611 /*
2612  * Create (dupe) of a resource with the same fd, to test stealing
2613  */
dupe_resource_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2614 static ERL_NIF_TERM dupe_resource_nif(ErlNifEnv* env, int argc,
2615                                       const ERL_NIF_TERM argv[]) {
2616     struct fd_resource* orig_rsrc;
2617 
2618     if (!get_fd(env, argv[0], &orig_rsrc)) {
2619         return enif_make_badarg(env);
2620     } else {
2621         struct fd_resource* new_rsrc;
2622         ERL_NIF_TERM new_fd;
2623 
2624         new_rsrc = enif_alloc_resource(fd_resource_type,
2625                                        sizeof(struct fd_resource));
2626         new_rsrc->fd = orig_rsrc->fd;
2627         new_rsrc->was_selected = 0;
2628         new_fd = enif_make_resource(env, new_rsrc);
2629         enif_release_resource(new_rsrc);
2630 
2631         return enif_make_tuple2(env, new_fd, make_pointer(env, new_rsrc));
2632     }
2633 }
2634 
write_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2635 static ERL_NIF_TERM write_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2636 {
2637     struct fd_resource* fdr;
2638     ErlNifBinary bin;
2639     int n, written = 0;
2640 
2641     if (!get_fd(env, argv[0], &fdr)
2642         || !enif_inspect_binary(env, argv[1], &bin))
2643         return enif_make_badarg(env);
2644 
2645     for (;;) {
2646         n = write(fdr->fd, bin.data + written, bin.size - written);
2647         if (n >= 0) {
2648             written += n;
2649             if (written == bin.size) {
2650                 return atom_ok;
2651             }
2652         }
2653         else if (errno == EAGAIN) {
2654             return enif_make_tuple2(env, atom_eagain, enif_make_int(env, written));
2655         }
2656         else if (errno == EINTR) {
2657             continue;
2658         }
2659         else {
2660             return enif_make_tuple2(env, atom_error, enif_make_int(env, errno));
2661         }
2662     }
2663 }
2664 
read_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2665 static ERL_NIF_TERM read_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2666 {
2667     struct fd_resource* fdr;
2668     unsigned char* buf;
2669     int n, count;
2670     ERL_NIF_TERM res;
2671 
2672     if (!get_fd(env, argv[0], &fdr)
2673         || !enif_get_int(env, argv[1], &count) || count < 1)
2674         return enif_make_badarg(env);
2675 
2676     buf = enif_make_new_binary(env, count, &res);
2677 
2678     for (;;) {
2679         n = read(fdr->fd, buf, count);
2680         if (n > 0) {
2681             if (n < count) {
2682                 res = enif_make_sub_binary(env, res, 0, n);
2683             }
2684             return res;
2685         }
2686         else if (n == 0) {
2687             return atom_eof;
2688         }
2689         else if (errno == EAGAIN) {
2690             return atom_eagain;
2691         }
2692         else if (errno == EINTR) {
2693             continue;
2694         }
2695         else {
2696             return enif_make_tuple2(env, atom_error, enif_make_int(env, errno));
2697         }
2698     }
2699 }
2700 
is_closed_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2701 static ERL_NIF_TERM is_closed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2702 {
2703     struct fd_resource* fdr;
2704 
2705     if (!get_fd(env, argv[0], &fdr))
2706         return enif_make_badarg(env);
2707 
2708     return fdr->fd < 0 ? atom_true : atom_false;
2709 }
2710 
clear_select_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2711 static ERL_NIF_TERM clear_select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2712 {
2713     struct fd_resource* fdr = NULL;
2714 
2715     if (!get_fd(env, argv[0], &fdr))
2716         return enif_make_badarg(env);
2717 
2718     fdr->fd = -1;
2719     fdr->was_selected = 0;
2720 
2721     return atom_ok;
2722 }
2723 
2724 #endif /* !__WIN32__ */
2725 
2726 
fd_resource_dtor(ErlNifEnv * env,void * obj)2727 static void fd_resource_dtor(ErlNifEnv* env, void* obj)
2728 {
2729     struct fd_resource* fdr = (struct fd_resource*)obj;
2730     resource_dtor(env, obj);
2731 #ifdef __WIN32__
2732     abort();
2733 #else
2734     if (fdr->fd >= 0) {
2735         assert(!fdr->was_selected);
2736         close(fdr->fd);
2737     }
2738 #endif
2739 }
2740 
2741 static struct {
2742     void* obj;
2743     int was_direct_call;
2744 }last_fd_stop;
2745 int fd_stop_cnt = 0;
2746 
fd_resource_stop(ErlNifEnv * env,void * obj,ErlNifEvent fd,int is_direct_call)2747 static void fd_resource_stop(ErlNifEnv* env, void* obj, ErlNifEvent fd,
2748                              int is_direct_call)
2749 {
2750     struct fd_resource* fdr = (struct fd_resource*)obj;
2751     assert(fd == fdr->fd);
2752     assert(fd >= 0);
2753 
2754     last_fd_stop.obj = obj;
2755     last_fd_stop.was_direct_call = is_direct_call;
2756     fd_stop_cnt++;
2757 
2758     close(fd);
2759     fdr->fd = -1;   /* thread safety ? */
2760     fdr->was_selected = 0;
2761 
2762     {
2763         ErlNifEnv* msg_env = enif_alloc_env();
2764         ERL_NIF_TERM msg;
2765         msg = enif_make_tuple3(msg_env,
2766                                atom_fd_resource_stop,
2767                                make_pointer(msg_env, obj),
2768                                enif_make_int(msg_env, is_direct_call));
2769 
2770         enif_send(env, &fdr->pid, msg_env, msg);
2771         enif_free_env(msg_env);
2772     }
2773 }
2774 
last_fd_stop_call(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2775 static ERL_NIF_TERM last_fd_stop_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2776 {
2777     ERL_NIF_TERM last, ret;
2778     last = enif_make_tuple2(env, make_pointer(env, last_fd_stop.obj),
2779                             enif_make_int(env, last_fd_stop.was_direct_call));
2780     ret = enif_make_tuple2(env, enif_make_int(env, fd_stop_cnt), last);
2781     fd_stop_cnt = 0;
2782     return ret;
2783 }
2784 
2785 
monitor_resource_dtor(ErlNifEnv * env,void * obj)2786 static void monitor_resource_dtor(ErlNifEnv* env, void* obj)
2787 {
2788     resource_dtor(env, obj);
2789 }
2790 
make_monitor(ErlNifEnv * env,const ErlNifMonitor * mon)2791 static ERL_NIF_TERM make_monitor(ErlNifEnv* env, const ErlNifMonitor* mon)
2792 {
2793     ERL_NIF_TERM mon_bin;
2794     memcpy(enif_make_new_binary(env, sizeof(ErlNifMonitor), &mon_bin),
2795            mon, sizeof(ErlNifMonitor));
2796     return mon_bin;
2797 }
2798 
get_monitor(ErlNifEnv * env,ERL_NIF_TERM term,ErlNifMonitor * mon)2799 static int get_monitor(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifMonitor* mon)
2800 {
2801     ErlNifBinary bin;
2802     if (!enif_inspect_binary(env, term, &bin)
2803         || bin.size != sizeof(ErlNifMonitor))
2804         return 0;
2805     memcpy(mon, bin.data, bin.size);
2806     return 1;
2807 }
2808 
monitor_resource_down(ErlNifEnv * env,void * obj,ErlNifPid * pid,ErlNifMonitor * mon)2809 static void monitor_resource_down(ErlNifEnv* env, void* obj, ErlNifPid* pid,
2810                                   ErlNifMonitor* mon)
2811 {
2812     struct monitor_resource* rsrc = (struct monitor_resource*)obj;
2813     ErlNifEnv* build_env;
2814     ErlNifEnv* msg_env;
2815     ERL_NIF_TERM msg;
2816 
2817     if (rsrc->use_msgenv) {
2818         msg_env = enif_alloc_env();
2819         build_env = msg_env;
2820     }
2821     else {
2822         msg_env = NULL;
2823         build_env = env;
2824     }
2825 
2826     msg = enif_make_tuple4(build_env,
2827                            atom_monitor_resource_down,
2828                            make_pointer(build_env, obj),
2829                            enif_make_pid(build_env, pid),
2830                            make_monitor(build_env, mon));
2831 
2832     enif_send(env, &rsrc->receiver, msg_env, msg);
2833     if (msg_env)
2834         enif_free_env(msg_env);
2835 }
2836 
alloc_monitor_resource_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2837 static ERL_NIF_TERM alloc_monitor_resource_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2838 {
2839     struct monitor_resource* rsrc;
2840 
2841     rsrc  = enif_alloc_resource(monitor_resource_type, sizeof(struct monitor_resource));
2842 
2843     return make_pointer(env,rsrc);
2844 }
2845 
monitor_process_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2846 static ERL_NIF_TERM monitor_process_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2847 {
2848     struct monitor_resource* rsrc;
2849     ErlNifPid target;
2850     ErlNifMonitor mon;
2851     int res;
2852 
2853     if (!get_pointer(env, argv[0], (void**)&rsrc)
2854         || !enif_get_local_pid(env, argv[1], &target)
2855         || !enif_get_local_pid(env, argv[3], &rsrc->receiver)) {
2856         return enif_make_badarg(env);
2857     }
2858 
2859     rsrc->use_msgenv = (argv[2] == atom_true);
2860     res = enif_monitor_process(env, rsrc, &target, &mon);
2861 
2862     return enif_make_tuple2(env, enif_make_int(env, res), make_monitor(env, &mon));
2863 }
2864 
demonitor_process_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2865 static ERL_NIF_TERM demonitor_process_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2866 {
2867     struct monitor_resource* rsrc;
2868     ErlNifMonitor mon;
2869     int res;
2870 
2871     if (!get_pointer(env, argv[0], (void**)&rsrc)
2872         || !get_monitor(env, argv[1], &mon)) {
2873         return enif_make_badarg(env);
2874     }
2875 
2876     res = enif_demonitor_process(env, rsrc, &mon);
2877 
2878     return enif_make_int(env, res);
2879 }
2880 
compare_monitors_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2881 static ERL_NIF_TERM compare_monitors_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2882 {
2883     ErlNifMonitor m1, m2;
2884     if (!get_monitor(env, argv[0], &m1)
2885         || !get_monitor(env, argv[1], &m2)) {
2886         return enif_make_badarg(env);
2887     }
2888 
2889     return enif_make_int(env, enif_compare_monitors(&m1, &m2));
2890 }
2891 
make_monitor_term_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2892 static ERL_NIF_TERM make_monitor_term_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2893 {
2894     ErlNifMonitor m;
2895     if (!get_monitor(env, argv[0], &m)) {
2896         return enif_make_badarg(env);
2897     }
2898 
2899     return enif_make_monitor_term(env, &m);
2900 }
2901 
2902 
2903 /*********** monitor_frenzy ************/
2904 
2905 struct frenzy_rand_bits
2906 {
2907     unsigned int source;
2908     unsigned int bits_consumed;
2909 };
2910 
2911 static unsigned int frenzy_rand_bits_max;
2912 
rand_bits(struct frenzy_rand_bits * rnd,unsigned int nbits)2913 unsigned rand_bits(struct frenzy_rand_bits* rnd, unsigned int nbits)
2914 {
2915     unsigned int res;
2916 
2917     rnd->bits_consumed += nbits;
2918     assert(rnd->bits_consumed <= frenzy_rand_bits_max);
2919     res = rnd->source & ((1 << nbits)-1);
2920     rnd->source >>= nbits;
2921     return res;
2922 }
2923 
2924 #define FRENZY_PROCS_MAX_BITS 4
2925 #define FRENZY_PROCS_MAX (1 << FRENZY_PROCS_MAX_BITS)
2926 
2927 #define FRENZY_RESOURCES_MAX_BITS 4
2928 #define FRENZY_RESOURCES_MAX (1 << FRENZY_RESOURCES_MAX_BITS)
2929 
2930 #define FRENZY_MONITORS_MAX_BITS 4
2931 #define FRENZY_MONITORS_MAX (1 << FRENZY_MONITORS_MAX_BITS)
2932 
2933 struct frenzy_monitor {
2934     ErlNifMutex* lock;
2935     volatile enum {
2936         MON_FREE, MON_FREE_DOWN, MON_FREE_DEMONITOR,
2937         MON_TRYING, MON_ACTIVE, MON_PENDING
2938     } state;
2939     ErlNifMonitor mon;
2940     ErlNifPid pid;
2941     unsigned int use_cnt;
2942 };
2943 
2944 struct frenzy_resource {
2945     unsigned int rix;
2946     struct frenzy_monitor monv[FRENZY_MONITORS_MAX];
2947 };
2948 struct frenzy_reslot {
2949     ErlNifMutex* lock;
2950     int stopped;
2951     struct frenzy_resource* obj;
2952     unsigned long alloc_cnt;
2953     unsigned long release_cnt;
2954     unsigned long dtor_cnt;
2955 };
2956 static struct frenzy_reslot resv[FRENZY_RESOURCES_MAX];
2957 
monitor_frenzy_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])2958 static ERL_NIF_TERM monitor_frenzy_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
2959 {
2960     struct frenzy_proc {
2961         ErlNifPid pid;
2962         int is_free;
2963     };
2964     static struct frenzy_proc procs[FRENZY_PROCS_MAX];
2965     static struct frenzy_proc* proc_refs[FRENZY_PROCS_MAX];
2966     static unsigned int nprocs, old_nprocs;
2967     static ErlNifMutex* procs_lock;
2968     static unsigned long spawn_cnt = 0;
2969     static unsigned long kill_cnt = 0;
2970     static unsigned long proc_histogram[FRENZY_PROCS_MAX];
2971     static int initialized = 0;
2972 
2973     static const unsigned int primes[] = {7, 13, 17, 19};
2974 
2975     struct frenzy_resource* r;
2976     struct frenzy_rand_bits rnd;
2977     unsigned int op, inc, my_nprocs;
2978     unsigned int mix;  /* r->monv[] index */
2979     unsigned int rix;  /* resv[] index */
2980     unsigned int pix;  /* procs[] index */
2981     unsigned int ref_ix; /* proc_refs[] index */
2982     int self_pix, rv;
2983     ERL_NIF_TERM retval = atom_error;
2984     const ERL_NIF_TERM Op = argv[0];
2985     const ERL_NIF_TERM Rnd = argv[1];
2986     const ERL_NIF_TERM SelfPix = argv[2];
2987     const ERL_NIF_TERM NewPid = argv[3];
2988 
2989     if (enif_is_atom(env, Op)) {
2990         if (Op == atom_init) {
2991             if (initialized || !enif_get_uint(env, Rnd, &frenzy_rand_bits_max))
2992                 return enif_make_badarg(env);
2993 
2994             procs_lock = enif_mutex_create("nif_SUITE:monitor_frenzy.procs");
2995             nprocs = 0;
2996             old_nprocs = 0;
2997             for (pix = 0; pix < FRENZY_PROCS_MAX; pix++) {
2998                 proc_refs[pix] = &procs[pix];
2999                 procs[pix].is_free = 1;
3000                 proc_histogram[pix] = 0;
3001             }
3002             for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
3003                 resv[rix].lock = enif_mutex_create("nif_SUITE:monitor_frenzy.resv.lock");
3004                 resv[rix].obj = NULL;
3005                 resv[rix].stopped = 0;
3006                 resv[rix].alloc_cnt = 0;
3007                 resv[rix].release_cnt = 0;
3008                 resv[rix].dtor_cnt = 0;
3009             }
3010 
3011             /* Add self as first process */
3012             enif_self(env, &procs[0].pid);
3013             procs[0].is_free = 0;
3014             old_nprocs = ++nprocs;
3015 
3016             spawn_cnt = 1;
3017             kill_cnt = 0;
3018             initialized = 1;
3019             return enif_make_uint(env, 0);  /* SelfPix */
3020         }
3021         else if (Op == atom_stats) {
3022             ERL_NIF_TERM hist[FRENZY_PROCS_MAX];
3023             unsigned long res_alloc_cnt = 0;
3024             unsigned long res_release_cnt = 0;
3025             unsigned long res_dtor_cnt = 0;
3026             for (ref_ix = 0; ref_ix < FRENZY_PROCS_MAX; ref_ix++) {
3027                 hist[ref_ix] = enif_make_ulong(env, proc_histogram[ref_ix]);
3028             }
3029             for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
3030                 res_alloc_cnt += resv[rix].alloc_cnt;
3031                 res_release_cnt += resv[rix].release_cnt;
3032                 res_dtor_cnt += resv[rix].dtor_cnt;
3033             }
3034 
3035             return
3036             enif_make_list4(env,
3037                             enif_make_tuple2(env, enif_make_string(env, "proc_histogram", ERL_NIF_LATIN1),
3038                                              enif_make_list_from_array(env, hist, FRENZY_PROCS_MAX)),
3039                             enif_make_tuple2(env, enif_make_string(env, "spawn_cnt", ERL_NIF_LATIN1),
3040                                              enif_make_ulong(env, spawn_cnt)),
3041                             enif_make_tuple2(env, enif_make_string(env, "kill_cnt", ERL_NIF_LATIN1),
3042                                              enif_make_ulong(env, kill_cnt)),
3043                             enif_make_tuple4(env, enif_make_string(env, "resource_alloc", ERL_NIF_LATIN1),
3044                                              enif_make_ulong(env, res_alloc_cnt),
3045                                              enif_make_ulong(env, res_release_cnt),
3046                                              enif_make_ulong(env, res_dtor_cnt)));
3047 
3048         }
3049         else if (Op == atom_stop && initialized) {  /* stop all */
3050 
3051             /* Release all resources */
3052             for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
3053                 enif_mutex_lock(resv[rix].lock);
3054                 r = resv[rix].obj;
3055                 if (r) {
3056                     resv[rix].obj =  NULL;
3057                     resv[rix].release_cnt++;
3058                 }
3059                 resv[rix].stopped = 1;
3060                 enif_mutex_unlock(resv[rix].lock);
3061                 if (r)
3062                     enif_release_resource(r);
3063             }
3064 
3065             /* Remove and return all pids */
3066             retval = enif_make_list(env, 0);
3067             enif_mutex_lock(procs_lock);
3068             for (ref_ix = 0; ref_ix < nprocs; ref_ix++) {
3069                 assert(!proc_refs[ref_ix]->is_free);
3070                 retval = enif_make_list_cell(env, enif_make_pid(env, &proc_refs[ref_ix]->pid),
3071                                              retval);
3072                 proc_refs[ref_ix]->is_free = 1;
3073             }
3074             kill_cnt += nprocs;
3075             nprocs = 0;
3076             old_nprocs = 0;
3077             enif_mutex_unlock(procs_lock);
3078 
3079             return retval;
3080         }
3081         return enif_make_badarg(env);
3082     }
3083 
3084     if (!enif_get_int(env, SelfPix, &self_pix) ||
3085         !enif_get_uint(env, Op, &op) ||
3086         !enif_get_uint(env, Rnd, &rnd.source))
3087         return enif_make_badarg(env);
3088 
3089     rnd.bits_consumed = 0;
3090     switch (op) {
3091     case 0:  { /* add/remove process */
3092         ErlNifPid self;
3093         enif_self(env, &self);
3094 
3095         ref_ix = rand_bits(&rnd, FRENZY_PROCS_MAX_BITS) % FRENZY_PROCS_MAX;
3096         enif_mutex_lock(procs_lock);
3097         if (procs[self_pix].is_free || procs[self_pix].pid.pid != self.pid) {
3098             /* Some one already removed me */
3099             enif_mutex_unlock(procs_lock);
3100             return atom_done;
3101         }
3102         if (ref_ix >= nprocs || nprocs < 2) { /* add process */
3103             ref_ix = nprocs++;
3104             pix = proc_refs[ref_ix] - procs;
3105             assert(procs[pix].is_free);
3106             if (!enif_get_local_pid(env, NewPid, &procs[pix].pid))
3107                 abort();
3108             procs[pix].is_free = 0;
3109             spawn_cnt++;
3110             proc_histogram[ref_ix]++;
3111             old_nprocs = nprocs;
3112             enif_mutex_unlock(procs_lock);
3113             DBG_TRACE2("Add pid %T, nprocs = %u\n", NewPid, nprocs);
3114             retval = enif_make_uint(env, pix);
3115         }
3116         else { /* remove process */
3117             pix = proc_refs[ref_ix] - procs;
3118             if (pix == self_pix) {
3119                 ref_ix = (ref_ix + 1) % nprocs;
3120                 pix = proc_refs[ref_ix] - procs;
3121             }
3122             assert(procs[pix].pid.pid != self.pid);
3123             assert(!procs[pix].is_free);
3124             retval = enif_make_pid(env, &procs[pix].pid);
3125             --nprocs;
3126             assert(!proc_refs[nprocs]->is_free);
3127             if (ref_ix != nprocs) {
3128                 struct frenzy_proc* tmp = proc_refs[ref_ix];
3129                 proc_refs[ref_ix] = proc_refs[nprocs];
3130                 proc_refs[nprocs] = tmp;
3131             }
3132             procs[pix].is_free = 1;
3133             proc_histogram[nprocs]++;
3134             kill_cnt++;
3135             enif_mutex_unlock(procs_lock);
3136             DBG_TRACE2("Removed pid %T, nprocs = %u\n", retval, nprocs);
3137         }
3138 	break;
3139     }
3140     case 1:
3141     case 2: /* create/delete/lookup resource */
3142 	rix = rand_bits(&rnd, FRENZY_RESOURCES_MAX_BITS) % FRENZY_RESOURCES_MAX;
3143 	inc = primes[rand_bits(&rnd, 2)];
3144 	while (enif_mutex_trylock(resv[rix].lock) == EBUSY) {
3145 	    rix = (rix + inc) % FRENZY_RESOURCES_MAX;
3146 	}
3147         if (resv[rix].stopped) {
3148             retval = atom_done;
3149             enif_mutex_unlock(resv[rix].lock);
3150             break;
3151         }
3152         else if (resv[rix].obj == NULL) {
3153 	    r = enif_alloc_resource(frenzy_resource_type,
3154 				    sizeof(struct frenzy_resource));
3155 	    resv[rix].obj = r;
3156             resv[rix].alloc_cnt++;
3157 	    r->rix = rix;
3158             for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
3159                 r->monv[mix].lock = enif_mutex_create("nif_SUITE:monitor_frenzy.monv.lock");
3160                 r->monv[mix].state = MON_FREE;
3161                 r->monv[mix].use_cnt = 0;
3162                 r->monv[mix].pid.pid = 0; /* null-pid */
3163             }
3164             DBG_TRACE2("New resource at r=%p rix=%u\n", r, rix);
3165 	}
3166         else {
3167             unsigned int resource_op = rand_bits(&rnd, 3);
3168             r = resv[rix].obj;
3169             if (resource_op == 0) {      /* delete resource */
3170                 resv[rix].obj = NULL;
3171                 resv[rix].release_cnt++;
3172                 enif_mutex_unlock(resv[rix].lock);
3173                 DBG_TRACE2("Delete resource at r=%p rix=%u\n", r, rix);
3174                 enif_release_resource(r);
3175                 retval = atom_ok;
3176                 break;
3177             }
3178             else if (resource_op == 1) {  /* return resource */
3179                 retval = enif_make_resource(env, r);
3180                 enif_mutex_unlock(resv[rix].lock);
3181                 break;
3182             }
3183         }
3184         enif_keep_resource(r);
3185         enif_mutex_unlock(resv[rix].lock);
3186 
3187         /* monitor/demonitor */
3188 
3189         mix = rand_bits(&rnd, FRENZY_MONITORS_MAX_BITS) % FRENZY_MONITORS_MAX;
3190         inc = primes[rand_bits(&rnd, 2)];
3191         while (enif_mutex_trylock(r->monv[mix].lock) == EBUSY) {
3192             mix = (mix + inc) % FRENZY_MONITORS_MAX;
3193         }
3194         switch (r->monv[mix].state) {
3195         case MON_FREE:
3196         case MON_FREE_DOWN:
3197         case MON_FREE_DEMONITOR: {   /* do monitor */
3198             /*
3199              * Use an old possibly larger value of 'nprocs', to increase
3200              * probability of monitoring an already terminated process
3201              */
3202             my_nprocs = old_nprocs;
3203             if (my_nprocs > 0) {
3204                 int save_state = r->monv[mix].state;
3205                 ref_ix = rand_bits(&rnd, FRENZY_PROCS_MAX_BITS) % my_nprocs;
3206                 pix = proc_refs[ref_ix] - procs;
3207                 r->monv[mix].pid.pid = procs[pix].pid.pid; /* "atomic" */
3208                 r->monv[mix].state = MON_TRYING;
3209                 rv = enif_monitor_process(env, r, &r->monv[mix].pid, &r->monv[mix].mon);
3210                 if (rv == 0) {
3211                     r->monv[mix].state = MON_ACTIVE;
3212                     r->monv[mix].use_cnt++;
3213                     DBG_TRACE3("Monitor from r=%p rix=%u to %T\n",
3214                                  r, r->rix, r->monv[mix].pid.pid);
3215                 }
3216                 else {
3217                     r->monv[mix].state = save_state;
3218                     DBG_TRACE4("Monitor from r=%p rix=%u to %T FAILED with %d\n",
3219                                r, r->rix, r->monv[mix].pid.pid, rv);
3220                 }
3221                 retval = enif_make_int(env,rv);
3222             }
3223             else {
3224                 DBG_TRACE0("No pids to monitor\n");
3225                 retval = atom_ok;
3226             }
3227             break;
3228         }
3229         case MON_ACTIVE: /* do demonitor */
3230             rv = enif_demonitor_process(env, r, &r->monv[mix].mon);
3231             if (rv == 0) {
3232                 DBG_TRACE3("Demonitor from r=%p rix=%u to %T\n",
3233                            r, r->rix, r->monv[mix].pid.pid);
3234                 r->monv[mix].state = MON_FREE_DEMONITOR;
3235             }
3236             else {
3237                 DBG_TRACE4("Demonitor from r=%p rix=%u to %T FAILED with %d\n",
3238                            r, r->rix, r->monv[mix].pid.pid, rv);
3239                 r->monv[mix].state = MON_PENDING;
3240             }
3241             retval = enif_make_int(env,rv);
3242             break;
3243 
3244         case MON_PENDING: /* waiting for 'down' callback, do nothing */
3245             retval = atom_ok;
3246             break;
3247         default:
3248             abort();
3249             break;
3250         }
3251         enif_mutex_unlock(r->monv[mix].lock);
3252         enif_release_resource(r);
3253 	break;
3254 
3255     case 3: /* no-op */
3256         retval = atom_ok;
3257         break;
3258     }
3259 
3260     {
3261         int percent = (rand_bits(&rnd, 6) + 1) * 2;  /* 2 to 128 */
3262         if (percent <= 100)
3263             enif_consume_timeslice(env, percent);
3264     }
3265 
3266     return retval;
3267 }
3268 
frenzy_resource_dtor(ErlNifEnv * env,void * obj)3269 static void frenzy_resource_dtor(ErlNifEnv* env, void* obj)
3270 {
3271     struct frenzy_resource* r = (struct frenzy_resource*) obj;
3272     unsigned int mix;
3273 
3274     DBG_TRACE2("DTOR r=%p rix=%u\n", r, r->rix);
3275 
3276     enif_mutex_lock(resv[r->rix].lock);
3277     resv[r->rix].dtor_cnt++;
3278     enif_mutex_unlock(resv[r->rix].lock);
3279 
3280     for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
3281         assert(r->monv[mix].state != MON_PENDING);
3282         enif_mutex_destroy(r->monv[mix].lock);
3283         r->monv[mix].lock = NULL;
3284     }
3285 
3286 }
3287 
frenzy_resource_down(ErlNifEnv * env,void * obj,ErlNifPid * pid,ErlNifMonitor * mon)3288 static void frenzy_resource_down(ErlNifEnv* env, void* obj, ErlNifPid* pid,
3289                                  ErlNifMonitor* mon)
3290 {
3291     struct frenzy_resource* r = (struct frenzy_resource*) obj;
3292     unsigned int mix;
3293 
3294     DBG_TRACE3("DOWN pid=%T, r=%p rix=%u\n", pid->pid, r, r->rix);
3295 
3296     for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
3297         int state1 = r->monv[mix].state;
3298         /* First do dirty access of pid and state without the lock */
3299         if (r->monv[mix].pid.pid == pid->pid && state1 >= MON_TRYING) {
3300             int state2;
3301             enif_mutex_lock(r->monv[mix].lock);
3302             state2 = r->monv[mix].state;
3303             if (state2 >= MON_ACTIVE) {
3304                 if (enif_compare_monitors(mon, &r->monv[mix].mon) == 0) {
3305                     r->monv[mix].state = MON_FREE_DOWN;
3306                     enif_mutex_unlock(r->monv[mix].lock);
3307                     return;
3308                 }
3309             }
3310             else {
3311                 assert(state2 != MON_TRYING);
3312                 assert(state1 == MON_TRYING ||  /* racing monitor failed */
3313                        state2 == MON_FREE_DEMONITOR || /* racing demonitor */
3314                        state2 == MON_FREE_DOWN);       /* racing down */
3315             }
3316             enif_mutex_unlock(r->monv[mix].lock);
3317         }
3318     }
3319     enif_fprintf(stderr, "DOWN called for unknown monitor\n");
3320     abort();
3321 }
3322 
3323 /*********** testing ioq ************/
3324 
ioq_resource_dtor(ErlNifEnv * env,void * obj)3325 static void ioq_resource_dtor(ErlNifEnv* env, void* obj) {
3326 
3327 }
3328 
3329 #ifndef __WIN32__
writeiovec(ErlNifEnv * env,ERL_NIF_TERM term,ERL_NIF_TERM * tail,ErlNifIOQueue * q,int fd)3330 static int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail, ErlNifIOQueue *q, int fd) {
3331     ErlNifIOVec vec, *iovec = &vec;
3332     SysIOVec *sysiovec;
3333     int saved_errno;
3334     int iovcnt, n;
3335 
3336     if (!enif_inspect_iovec(env, 64, term, tail, &iovec))
3337         return -2;
3338 
3339     if (enif_ioq_size(q) > 0) {
3340         /* If the I/O queue contains data we enqueue the iovec and then
3341            peek the data to write out of the queue. */
3342         if (!enif_ioq_enqv(q, iovec, 0))
3343             return -3;
3344 
3345         sysiovec = enif_ioq_peek(q, &iovcnt);
3346     } else {
3347         /* If the I/O queue is empty we skip the trip through it. */
3348         iovcnt = iovec->iovcnt;
3349         sysiovec = iovec->iov;
3350     }
3351 
3352     /* Attempt to write the data */
3353     n = writev(fd, (struct iovec*)sysiovec, iovcnt);
3354     saved_errno = errno;
3355 
3356     if (enif_ioq_size(q) == 0) {
3357         /* If the I/O queue was initially empty we enqueue any
3358            remaining data into the queue for writing later. */
3359         if (n >= 0 && !enif_ioq_enqv(q, iovec, n))
3360             return -3;
3361     } else {
3362         /* Dequeue any data that was written from the queue. */
3363         if (n > 0 && !enif_ioq_deq(q, n, NULL))
3364             return -4;
3365     }
3366 
3367     /* return n, which is either number of bytes written or -1 if
3368        some error happened */
3369     errno = saved_errno;
3370     return n;
3371 }
3372 #endif
3373 
ioq(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3374 static ERL_NIF_TERM ioq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3375 {
3376     struct ioq_resource *ioq;
3377     ERL_NIF_TERM ret;
3378     if (enif_is_identical(argv[0], enif_make_atom(env, "create"))) {
3379         ErlNifIOQueue *q = enif_ioq_create(ERL_NIF_IOQ_NORMAL);
3380         ioq = (struct ioq_resource *)enif_alloc_resource(ioq_resource_type,
3381                                                          sizeof(*ioq));
3382         ioq->q = q;
3383         ret = enif_make_resource(env, ioq);
3384         enif_release_resource(ioq);
3385         return ret;
3386     } else if (argc >= 2 && enif_is_identical(argv[0], enif_make_atom(env, "inspect"))) {
3387         ErlNifIOVec vec, *iovec = NULL;
3388         int i, iovcnt;
3389         ERL_NIF_TERM *elems, tail, list;
3390         ErlNifEnv *myenv = NULL;
3391 
3392         if (argv >= 3 && enif_is_identical(argv[2], enif_make_atom(env, "use_stack")))
3393             iovec = &vec;
3394         if (argc >= 4 && enif_is_identical(argv[3], enif_make_atom(env, "use_env")))
3395             myenv = env;
3396         if (!enif_inspect_iovec(myenv, ~(size_t)0, argv[1], &tail, &iovec))
3397             return enif_make_badarg(env);
3398 
3399         iovcnt = iovec->iovcnt;
3400         elems = enif_alloc(sizeof(ERL_NIF_TERM) * iovcnt);
3401 
3402         for (i = 0; i < iovcnt; i++) {
3403             ErlNifBinary bin;
3404             if (!enif_alloc_binary(iovec->iov[i].iov_len, &bin)) {
3405                 enif_free_iovec(iovec);
3406                 enif_free(elems);
3407                 return enif_make_badarg(env);
3408             }
3409             memcpy(bin.data, iovec->iov[i].iov_base, iovec->iov[i].iov_len);
3410             elems[i] = enif_make_binary(env, &bin);
3411         }
3412 
3413         if (!myenv)
3414             enif_free_iovec(iovec);
3415 
3416 	list = enif_make_list_from_array(env, elems, iovcnt);
3417 	enif_free(elems);
3418 	return list;
3419     } else if (argc >= 2) {
3420         unsigned skip;
3421         if (!enif_get_resource(env, argv[1], ioq_resource_type, (void**)&ioq)
3422             || !ioq->q)
3423             return enif_make_badarg(env);
3424 
3425         if (argc == 3 && enif_is_identical(argv[0], enif_make_atom(env, "example"))) {
3426 #ifndef __WIN32__
3427             int fd[2], res = 0, cnt = 0;
3428             ERL_NIF_TERM tail;
3429             char buff[255];
3430             res = pipe(fd);
3431             assert(res == 0);
3432             fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_NONBLOCK);
3433             fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL) | O_NONBLOCK);
3434 
3435             /* Write until the pipe buffer is full, which should result in data
3436              * being queued up. */
3437             for (res = 0; res >= 0; ) {
3438                 cnt += res;
3439                 res = writeiovec(env, argv[2], &tail, ioq->q, fd[1]);
3440             }
3441 
3442             /* Flush the queue while reading from the other end of the pipe. */
3443             tail = enif_make_list(env, 0);
3444             while (enif_ioq_size(ioq->q) > 0) {
3445                 res = writeiovec(env, tail, &tail, ioq->q, fd[1]);
3446 
3447                 if (res < 0 && errno != EAGAIN) {
3448                     break;
3449                 } else if (res > 0) {
3450                     cnt += res;
3451                 }
3452 
3453                 for (res = 0; res >= 0; ) {
3454                     cnt -= res;
3455                     res = read(fd[0], buff, sizeof(buff));
3456                 }
3457             }
3458 
3459             close(fd[0]);
3460             close(fd[1]);
3461 
3462             /* Check that we read as much as we wrote */
3463             if (cnt == 0 && enif_ioq_size(ioq->q) == 0)
3464                 return enif_make_atom(env, "true");
3465 
3466             return enif_make_int(env, cnt);
3467 #else
3468             return enif_make_atom(env, "true");
3469 #endif
3470         }
3471         if (enif_is_identical(argv[0], enif_make_atom(env, "destroy"))) {
3472             enif_ioq_destroy(ioq->q);
3473             ioq->q = NULL;
3474             return enif_make_atom(env, "false");
3475         } else if (argc >= 4 && enif_is_identical(argv[0], enif_make_atom(env, "enqv"))) {
3476             ErlNifIOVec vec, *iovec = &vec;
3477             ERL_NIF_TERM tail;
3478 
3479             if (!enif_get_uint(env, argv[3], &skip))
3480                 return enif_make_badarg(env);
3481             if (!enif_inspect_iovec(env, ~0ul, argv[2], &tail, &iovec))
3482                 return enif_make_badarg(env);
3483             if (!enif_ioq_enqv(ioq->q, iovec, skip))
3484                 return enif_make_badarg(env);
3485 
3486             return enif_make_atom(env, "true");
3487         } else if (argc >= 4 && enif_is_identical(argv[0], enif_make_atom(env, "enqb"))) {
3488             ErlNifBinary bin;
3489             if (!enif_get_uint(env, argv[3], &skip) ||
3490                 !enif_inspect_binary(env, argv[2], &bin))
3491                 return enif_make_badarg(env);
3492 
3493             if (!enif_ioq_enq_binary(ioq->q, &bin, skip))
3494                 return enif_make_badarg(env);
3495 
3496             return enif_make_atom(env, "true");
3497         } else if (argc >= 4 && enif_is_identical(argv[0], enif_make_atom(env, "enqbraw"))) {
3498             ErlNifBinary bin;
3499             ErlNifBinary localbin;
3500 	    int i;
3501             if (!enif_get_uint(env, argv[3], &skip) ||
3502                 !enif_inspect_binary(env, argv[2], &bin) ||
3503                 !enif_alloc_binary(bin.size, &localbin))
3504                 return enif_make_badarg(env);
3505 
3506             memcpy(localbin.data, bin.data, bin.size);
3507             i = enif_ioq_enq_binary(ioq->q, &localbin, skip);
3508 	    if (!i)
3509 		return enif_make_badarg(env);
3510 	    else
3511 		return enif_make_atom(env, "true");
3512         } else if (enif_is_identical(argv[0], enif_make_atom(env, "peek_head"))) {
3513             ERL_NIF_TERM head_term;
3514 
3515             if(enif_ioq_peek_head(env, ioq->q, NULL, &head_term)) {
3516                 return enif_make_tuple2(env,
3517                     enif_make_atom(env, "true"), head_term);
3518             }
3519 
3520             return enif_make_atom(env, "false");
3521         } else if (argc >= 3 && enif_is_identical(argv[0], enif_make_atom(env, "peek"))) {
3522             int iovlen, num, i, off = 0;
3523             SysIOVec *iov = enif_ioq_peek(ioq->q, &iovlen);
3524             ErlNifBinary bin;
3525 
3526             if (!enif_get_int(env, argv[2], &num) || !enif_alloc_binary(num, &bin))
3527                 return enif_make_badarg(env);
3528 
3529             for (i = 0; i < iovlen && num > 0; i++) {
3530                 int to_copy = num < iov[i].iov_len ? num : iov[i].iov_len;
3531                 memcpy(bin.data + off, iov[i].iov_base, to_copy);
3532                 num -= to_copy;
3533                 off += to_copy;
3534             }
3535 
3536             return enif_make_binary(env, &bin);
3537         } else if (argc >= 3 && enif_is_identical(argv[0], enif_make_atom(env, "deq"))) {
3538             int num;
3539             size_t sz;
3540             ErlNifUInt64 sz64;
3541             if (!enif_get_int(env, argv[2], &num))
3542                 return enif_make_badarg(env);
3543 
3544             if (!enif_ioq_deq(ioq->q, num, &sz))
3545                 return enif_make_badarg(env);
3546 
3547             sz64 = sz;
3548 
3549             return enif_make_uint64(env, sz64);
3550         } else if (enif_is_identical(argv[0], enif_make_atom(env, "size"))) {
3551             ErlNifUInt64 size = enif_ioq_size(ioq->q);
3552             return enif_make_uint64(env, size);
3553         }
3554     }
3555 
3556     return enif_make_badarg(env);
3557 }
3558 
make_bool(ErlNifEnv * env,int bool)3559 static ERL_NIF_TERM make_bool(ErlNifEnv* env, int bool)
3560 {
3561     return bool ? atom_true : atom_false;
3562 }
3563 
get_local_pid_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3564 static ERL_NIF_TERM get_local_pid_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3565 {
3566     ErlNifPid pid;
3567     ERL_NIF_TERM pid_bin;
3568     int ret = enif_get_local_pid(env, argv[0], &pid);
3569 
3570     memcpy(enif_make_new_binary(env, sizeof(ErlNifPid), &pid_bin),
3571            &pid, sizeof(ErlNifPid));
3572 
3573     return enif_make_tuple2(env, make_bool(env, ret), pid_bin);
3574 }
3575 
make_pid_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3576 static ERL_NIF_TERM make_pid_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3577 {
3578     ErlNifPid pid;
3579 
3580     if (!get_pidbin(env, argv[0], &pid))
3581         return enif_make_badarg(env);
3582 
3583     return enif_make_pid(env, &pid);
3584 }
3585 
set_pid_undefined_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3586 static ERL_NIF_TERM set_pid_undefined_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3587 {
3588     ErlNifPid pid;
3589     ERL_NIF_TERM pid_bin;
3590 
3591     enif_set_pid_undefined(&pid);
3592     memcpy(enif_make_new_binary(env, sizeof(ErlNifPid), &pid_bin),
3593            &pid, sizeof(ErlNifPid));
3594 
3595     return pid_bin;
3596 }
3597 
is_pid_undefined_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3598 static ERL_NIF_TERM is_pid_undefined_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3599 {
3600     ErlNifPid pid;
3601 
3602     if (!get_pidbin(env, argv[0], &pid))
3603         return enif_make_badarg(env);
3604 
3605     return make_bool(env, enif_is_pid_undefined(&pid));
3606 }
3607 
compare_pids_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3608 static ERL_NIF_TERM compare_pids_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3609 {
3610     ErlNifPid a, b;
3611 
3612     if (!get_pidbin(env, argv[0], &a) || !get_pidbin(env, argv[1], &b))
3613         return enif_make_badarg(env);
3614 
3615     return enif_make_int(env, enif_compare_pids(&a, &b));
3616 }
3617 
term_type_nif(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3618 static ERL_NIF_TERM term_type_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
3619 {
3620     switch (enif_term_type(env, argv[0])) {
3621     case ERL_NIF_TERM_TYPE_ATOM:
3622         return enif_make_atom(env, "atom");
3623     case ERL_NIF_TERM_TYPE_BITSTRING:
3624         return enif_make_atom(env, "bitstring");
3625     case ERL_NIF_TERM_TYPE_FLOAT:
3626         return enif_make_atom(env, "float");
3627     case ERL_NIF_TERM_TYPE_FUN:
3628         return enif_make_atom(env, "fun");
3629     case ERL_NIF_TERM_TYPE_INTEGER:
3630         return enif_make_atom(env, "integer");
3631     case ERL_NIF_TERM_TYPE_LIST:
3632         return enif_make_atom(env, "list");
3633     case ERL_NIF_TERM_TYPE_MAP:
3634         return enif_make_atom(env, "map");
3635     case ERL_NIF_TERM_TYPE_PID:
3636         return enif_make_atom(env, "pid");
3637     case ERL_NIF_TERM_TYPE_PORT:
3638         return enif_make_atom(env, "port");
3639     case ERL_NIF_TERM_TYPE_REFERENCE:
3640         return enif_make_atom(env, "reference");
3641     case ERL_NIF_TERM_TYPE_TUPLE:
3642         return enif_make_atom(env, "tuple");
3643     default:
3644         return enif_make_badarg(env);
3645     }
3646 }
3647 
3648 static ErlNifFunc nif_funcs[] =
3649 {
3650     {"lib_version", 0, lib_version},
3651     {"call_history", 0, call_history},
3652     {"hold_nif_mod_priv_data", 1, hold_nif_mod_priv_data},
3653     {"nif_mod_call_history", 0, nif_mod_call_history},
3654     {"list_seq", 1, list_seq},
3655     {"type_test", 0, type_test},
3656     {"tuple_2_list", 1, tuple_2_list},
3657     {"is_identical",2,is_identical},
3658     {"compare",2,compare},
3659     {"hash_nif",3,hash_nif},
3660     {"many_args_100", 100, many_args_100},
3661     {"clone_bin", 1, clone_bin},
3662     {"make_sub_bin", 3, make_sub_bin},
3663     {"string_to_bin", 2, string_to_bin},
3664     {"atom_to_bin", 2, atom_to_bin},
3665     {"macros", 1, macros},
3666     {"tuple_2_list_and_tuple",1,tuple_2_list_and_tuple},
3667     {"iolist_2_bin", 1, iolist_2_bin},
3668     {"get_resource_type", 1, get_resource_type},
3669     {"alloc_resource", 2, alloc_resource},
3670     {"make_resource", 1, make_resource},
3671     {"get_resource", 2, get_resource},
3672     {"release_resource", 1, release_resource},
3673     {"release_resource_from_thread", 1, release_resource_from_thread},
3674     {"last_resource_dtor_call_nif", 0, last_resource_dtor_call_nif},
3675     {"check_is", 11, check_is},
3676     {"check_is_exception", 0, check_is_exception},
3677     {"length_test", 6, length_test},
3678     {"make_atoms", 0, make_atoms},
3679     {"make_strings", 0, make_strings},
3680     {"make_new_resource", 2, make_new_resource},
3681     {"make_new_resource_binary", 1, make_new_resource_binary},
3682     {"send_list_seq", 2, send_list_seq},
3683     {"send_new_blob", 2, send_new_blob},
3684     {"alloc_msgenv", 0, alloc_msgenv},
3685     {"clear_msgenv", 1, clear_msgenv},
3686     {"grow_blob", 2, grow_blob},
3687     {"grow_blob", 3, grow_blob},
3688     {"send_blob", 2, send_blob},
3689     {"send3_blob", 3, send3_blob},
3690     {"send_blob_thread", 3, send_blob_thread},
3691     {"join_send_thread", 1, join_send_thread},
3692     {"copy_blob", 1, copy_blob},
3693     {"send_term", 2, send_term},
3694     {"send_copy_term", 2, send_copy_term},
3695     {"reverse_list",1, reverse_list},
3696     {"echo_int", 1, echo_int},
3697     {"type_sizes", 0, type_sizes},
3698     {"otp_9668_nif", 1, otp_9668_nif},
3699     {"otp_9828_nif", 1, otp_9828_nif},
3700     {"consume_timeslice_nif", 2, consume_timeslice_nif},
3701     {"call_nif_schedule", 2, call_nif_schedule},
3702     {"call_nif_exception", 1, call_nif_exception},
3703     {"call_nif_nan_or_inf", 1, call_nif_nan_or_inf},
3704     {"call_nif_atom_too_long", 1, call_nif_atom_too_long},
3705     {"is_map_nif", 1, is_map_nif},
3706     {"get_map_size_nif", 1, get_map_size_nif},
3707     {"make_new_map_nif", 0, make_new_map_nif},
3708     {"make_map_put_nif", 3, make_map_put_nif},
3709     {"get_map_value_nif", 2, get_map_value_nif},
3710     {"make_map_update_nif", 3, make_map_update_nif},
3711     {"make_map_remove_nif", 2, make_map_remove_nif},
3712     {"maps_from_list_nif", 1, maps_from_list_nif},
3713     {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif},
3714     {"monotonic_time", 1, monotonic_time},
3715     {"time_offset", 1, time_offset},
3716     {"convert_time_unit", 3, convert_time_unit},
3717     {"now_time", 0, now_time},
3718     {"cpu_time", 0, cpu_time},
3719     {"unique_integer_nif", 1, unique_integer},
3720     {"is_process_alive_nif", 1, is_process_alive},
3721     {"is_port_alive_nif", 1, is_port_alive},
3722     {"term_to_binary_nif", 2, term_to_binary},
3723     {"binary_to_term_nif", 3, binary_to_term},
3724     {"port_command_nif", 2, port_command},
3725     {"format_term_nif", 2, format_term},
3726     {"select_nif", 6, select_nif},
3727 #ifndef __WIN32__
3728     {"pipe_nif", 0, pipe_nif},
3729     {"write_nif", 2, write_nif},
3730     {"dupe_resource_nif", 1, dupe_resource_nif},
3731     {"read_nif", 2, read_nif},
3732     {"is_closed_nif", 1, is_closed_nif},
3733     {"clear_select_nif", 1, clear_select_nif},
3734 #endif
3735     {"last_fd_stop_call", 0, last_fd_stop_call},
3736     {"alloc_monitor_resource_nif", 0, alloc_monitor_resource_nif},
3737     {"monitor_process_nif", 4, monitor_process_nif},
3738     {"demonitor_process_nif", 2, demonitor_process_nif},
3739     {"compare_monitors_nif", 2, compare_monitors_nif},
3740     {"make_monitor_term_nif", 1, make_monitor_term_nif},
3741     {"monitor_frenzy_nif", 4, monitor_frenzy_nif},
3742     {"whereis_send", 3, whereis_send},
3743     {"whereis_term", 2, whereis_term},
3744     {"whereis_thd_lookup", 3, whereis_thd_lookup},
3745     {"whereis_thd_result", 1, whereis_thd_result},
3746     {"ioq_nif", 1, ioq},
3747     {"ioq_nif", 2, ioq},
3748     {"ioq_nif", 3, ioq},
3749     {"ioq_nif", 4, ioq},
3750     {"get_local_pid_nif", 1, get_local_pid_nif},
3751     {"make_pid_nif", 1, make_pid_nif},
3752     {"set_pid_undefined_nif", 0, set_pid_undefined_nif},
3753     {"is_pid_undefined_nif", 1, is_pid_undefined_nif},
3754     {"compare_pids_nif", 2, compare_pids_nif},
3755     {"term_type_nif", 1, term_type_nif}
3756 };
3757 
3758 ERL_NIF_INIT(nif_SUITE,nif_funcs,load,NULL,upgrade,unload)
3759