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