1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2010-2016. 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
21 /*
22 * Stress tests of rwmutex implementation.
23 *
24 * Author: Rickard Green
25 */
26
27 #include <erl_nif.h>
28
29 #ifdef __WIN32__
30 # ifndef WIN32_LEAN_AND_MEAN
31 # define WIN32_LEAN_AND_MEAN
32 # endif
33 # include <windows.h>
34 #else
35 # include "ethread.h"
36 # include "erl_misc_utils.h"
37 # include <unistd.h>
38 #endif
39
40 #include <errno.h>
41 #include <stdio.h>
42 #include <string.h>
43
44 static int
45 fail(const char *file, int line, const char *function, const char *assertion);
46
47 #undef ASSERT
48 #define ASSERT(X) ((void) ((X) ? 1 : fail(__FILE__, __LINE__, __func__, #X)))
49
50 #ifdef __WIN32__
51 /*
52 * We cannot access the ethread symbols directly; test
53 * what we got in the nif api instead...
54 */
55 #define HAVE_FREQREAD_SUPPORT 0
56 #define RWMUTEX_T ErlNifRWLock
57 #define RWMUTEX_CREATE(FR) enif_rwlock_create("dummy")
58 #define RWMUTEX_DESTROY enif_rwlock_destroy
59 #define RWMUTEX_WLOCK enif_rwlock_rwlock
60 #define RWMUTEX_TRYWLOCK enif_rwlock_tryrwlock
61 #define RWMUTEX_WUNLOCK enif_rwlock_rwunlock
62 #define RWMUTEX_TRYRLOCK enif_rwlock_tryrlock
63 #define RWMUTEX_RLOCK enif_rwlock_rlock
64 #define RWMUTEX_RUNLOCK enif_rwlock_runlock
65 #define THR_ID ErlNifTid
66 #define THR_CREATE(A, B, C, D) enif_thread_create("dummy", (A), (B), (C), (D))
67 #define THR_JOIN enif_thread_join
68 #define ATOMIC_T volatile LONG
69 #define ATOMIC_INIT(VarP, Val) (*(VarP) = (Val))
70 #define ATOMIC_SET(VarP, Val) (*(VarP) = (Val))
71 #define ATOMIC_READ(VarP) (*(VarP))
72 #define ATOMIC_INC InterlockedIncrement
73 #define ATOMIC_DEC InterlockedDecrement
74
75 #else
76
77 #ifdef ETHR_USE_OWN_RWMTX_IMPL__
78 # define HAVE_FREQREAD_SUPPORT 1
79 #else
80 # define HAVE_FREQREAD_SUPPORT 0
81 #endif
82
83 #define RWMUTEX_T ethr_rwmutex
84 static ethr_rwmutex *
RWMUTEX_CREATE(int freqread)85 RWMUTEX_CREATE(int freqread)
86 {
87 ethr_rwmutex *rwmtx = enif_alloc(sizeof(ethr_rwmutex));
88 ethr_rwmutex_opt rwmtx_opt = ETHR_RWMUTEX_OPT_DEFAULT_INITER;
89 if (freqread)
90 rwmtx_opt.type = ETHR_RWMUTEX_TYPE_FREQUENT_READ;
91 ASSERT(rwmtx);
92 ASSERT(ethr_rwmutex_init_opt(rwmtx, &rwmtx_opt) == 0);
93 return rwmtx;
94 }
95 static void
RWMUTEX_DESTROY(ethr_rwmutex * rwmtx)96 RWMUTEX_DESTROY(ethr_rwmutex *rwmtx)
97 {
98 ASSERT(ethr_rwmutex_destroy(rwmtx) == 0);
99 enif_free(rwmtx);
100 }
101 #define RWMUTEX_TRYWLOCK ethr_rwmutex_tryrwlock
102 #define RWMUTEX_WLOCK ethr_rwmutex_rwlock
103 #define RWMUTEX_WUNLOCK ethr_rwmutex_rwunlock
104 #define RWMUTEX_TRYRLOCK ethr_rwmutex_tryrlock
105 #define RWMUTEX_RLOCK ethr_rwmutex_rlock
106 #define RWMUTEX_RUNLOCK ethr_rwmutex_runlock
107 #define THR_ID ethr_tid
108 #define THR_CREATE ethr_thr_create
109 #define THR_JOIN ethr_thr_join
110 #define ATOMIC_T ethr_atomic_t
111 #define ATOMIC_INIT ethr_atomic_init
112 #define ATOMIC_SET ethr_atomic_set
113 #define ATOMIC_READ ethr_atomic_read
114 #define ATOMIC_INC ethr_atomic_inc
115 #define ATOMIC_DEC ethr_atomic_dec
116
117 #endif
118
119
120 #if !defined(__func__)
121 # if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L
122 # if !defined(__GNUC__) || __GNUC__ < 2
123 # define __func__ "[unknown_function]"
124 # else
125 # define __func__ __FUNCTION__
126 # endif
127 # endif
128 #endif
129
130 static void milli_sleep(int ms);
131 static int get_bool(ErlNifEnv* env, ERL_NIF_TERM term);
132
133 /*
134 * Long rwlock testcase
135 */
136
137 #define LONG_RW_NO_W_THREADS 6
138 #define LONG_RW_NO_THREADS 20
139 #define LONG_RW_NO_WLOCK_COUNT 100
140
141 typedef struct {
142 RWMUTEX_T *rwlock;
143 ATOMIC_T *is_wlocked;
144 ATOMIC_T *is_rlocked;
145 int *stop;
146 int *count;
147 int sleep;
148 } long_rw_t;
149
150 static void *
long_rw_w(void * varg)151 long_rw_w(void *varg)
152 {
153 long_rw_t *arg = varg;
154 int stop = 0;
155 do {
156 RWMUTEX_WLOCK(arg->rwlock);
157 ASSERT(!ATOMIC_READ(arg->is_wlocked));
158 ATOMIC_SET(arg->is_wlocked, 1);
159 ASSERT(!ATOMIC_READ(arg->is_rlocked));
160 milli_sleep(arg->sleep);
161 if (++(*arg->count) > LONG_RW_NO_WLOCK_COUNT)
162 stop = *arg->stop = 1;
163 ATOMIC_SET(arg->is_wlocked, 0);
164 ASSERT(!ATOMIC_READ(arg->is_rlocked));
165 RWMUTEX_WUNLOCK(arg->rwlock);
166 } while (!stop);
167 return NULL;
168 }
169
170 static void *
long_rw_r(void * varg)171 long_rw_r(void *varg)
172 {
173 long_rw_t *arg = varg;
174 int stop;
175 do {
176 RWMUTEX_RLOCK(arg->rwlock);
177 ASSERT(!ATOMIC_READ(arg->is_wlocked));
178 ATOMIC_INC(arg->is_rlocked);
179 milli_sleep(arg->sleep);
180 stop = *arg->stop;
181 ATOMIC_DEC(arg->is_rlocked);
182 ASSERT(!ATOMIC_READ(arg->is_wlocked));
183 RWMUTEX_RUNLOCK(arg->rwlock);
184 } while (!stop);
185 return NULL;
186 }
187
188
long_rw_test(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])189 static ERL_NIF_TERM long_rw_test(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
190 {
191 int res, freqread, i, count, stop;
192 ATOMIC_T is_wlocked, is_rlocked;
193 THR_ID tid[LONG_RW_NO_THREADS];
194 long_rw_t arg;
195 long_rw_t targ[LONG_RW_NO_THREADS];
196
197 ATOMIC_INIT(&is_wlocked, 0);
198 ATOMIC_INIT(&is_rlocked, 0);
199
200 freqread = 0;
201
202 arg.is_wlocked = &is_wlocked;
203 arg.is_rlocked = &is_rlocked;
204 arg.count = &count;
205 arg.stop = &stop;
206
207 restart:
208
209 stop = 0;
210 count = 0;
211
212 arg.rwlock = RWMUTEX_CREATE(freqread);
213
214 ASSERT(arg.rwlock);
215
216 for (i = 0; i < LONG_RW_NO_W_THREADS; i++) {
217 targ[i] = arg;
218 targ[i].sleep = 100 + i*10;
219 ASSERT(THR_CREATE(&tid[i], long_rw_w, &targ[i], NULL) == 0);
220 }
221 for (; i < LONG_RW_NO_THREADS; i++) {
222 targ[i] = arg;
223 targ[i].sleep = 100;
224 ASSERT(THR_CREATE(&tid[i], long_rw_r, &targ[i], NULL) == 0);
225 }
226 for (i = 0; i < LONG_RW_NO_THREADS; i++)
227 ASSERT(THR_JOIN(tid[i], NULL) == 0);
228
229 ASSERT(!ATOMIC_READ(arg.is_wlocked));
230 ASSERT(!ATOMIC_READ(arg.is_rlocked));
231
232 RWMUTEX_DESTROY(arg.rwlock);
233
234 if (HAVE_FREQREAD_SUPPORT && !freqread) {
235 freqread = 1;
236 goto restart;
237 }
238
239 if (freqread)
240 return enif_make_atom(env, "ok");
241 else
242 return enif_make_tuple2(env,
243 enif_make_atom(env,
244 "comment"),
245 enif_make_string(env,
246 "No frequent read test made.",
247 ERL_NIF_LATIN1));
248 }
249
250 /*
251 * Hammer rwlock testcase
252 */
253
254 #define HAMMER_RW_NO_W_THREADS 6
255 #define HAMMER_RW_NO_THREADS 20
256 #define HAMMER_RW_NO_WLOCK_COUNT 1000000
257
258 typedef struct {
259 RWMUTEX_T *rwlock;
260 ATOMIC_T is_locked;
261 int lock_check;
262 int stop;
263 int count;
264 } hammer_rw_t;
265
266 static void *
hammer_rw_w(void * varg)267 hammer_rw_w(void *varg)
268 {
269 hammer_rw_t *arg = varg;
270 int stop = 0;
271 do {
272 RWMUTEX_WLOCK(arg->rwlock);
273 if (arg->lock_check) {
274 ASSERT(!ATOMIC_READ(&arg->is_locked));
275 ATOMIC_SET(&arg->is_locked, -1);
276 }
277 if (++arg->count > HAMMER_RW_NO_WLOCK_COUNT)
278 stop = arg->stop = 1;
279 if (arg->lock_check) {
280 ASSERT(ATOMIC_READ(&arg->is_locked) == -1);
281 ATOMIC_SET(&arg->is_locked, 0);
282 }
283 RWMUTEX_WUNLOCK(arg->rwlock);
284 } while (!stop);
285 return NULL;
286 }
287
288 static void *
hammer_rw_r(void * varg)289 hammer_rw_r(void *varg)
290 {
291 hammer_rw_t *arg = varg;
292 int stop;
293 do {
294 RWMUTEX_RLOCK(arg->rwlock);
295 if (arg->lock_check) {
296 ASSERT(ATOMIC_READ(&arg->is_locked) >= 0);
297 ATOMIC_INC(&arg->is_locked);
298 }
299 stop = arg->stop;
300 if (arg->lock_check) {
301 ASSERT(ATOMIC_READ(&arg->is_locked) > 0);
302 ATOMIC_DEC(&arg->is_locked);
303 }
304 RWMUTEX_RUNLOCK(arg->rwlock);
305 } while (!stop);
306 return NULL;
307 }
308
309
hammer_rw_test(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])310 static ERL_NIF_TERM hammer_rw_test(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
311 {
312 hammer_rw_t arg;
313 char buf[10];
314 int res, freqread, i;
315 THR_ID tid[HAMMER_RW_NO_THREADS];
316
317 if (argc != 1)
318 goto badarg;
319
320 arg.lock_check = get_bool(env, argv[0]);
321 if (arg.lock_check < 0)
322 goto badarg;
323
324 ATOMIC_INIT(&arg.is_locked, 0);
325
326 freqread = 0;
327
328 restart:
329 arg.stop = 0;
330 arg.count = 0;
331
332 arg.rwlock = RWMUTEX_CREATE(freqread);
333
334 ASSERT(arg.rwlock);
335
336 for (i = 0; i < HAMMER_RW_NO_W_THREADS; i++)
337 ASSERT(THR_CREATE(&tid[i], hammer_rw_w, &arg, NULL) == 0);
338 for (; i < HAMMER_RW_NO_THREADS; i++)
339 ASSERT(THR_CREATE(&tid[i], hammer_rw_r, &arg, NULL) == 0);
340 for (i = 0; i < HAMMER_RW_NO_THREADS; i++)
341 ASSERT(THR_JOIN(tid[i], NULL) == 0);
342
343 ASSERT(!ATOMIC_READ(&arg.is_locked));
344
345 RWMUTEX_DESTROY(arg.rwlock);
346
347 if (HAVE_FREQREAD_SUPPORT && !freqread) {
348 freqread = 1;
349 goto restart;
350 }
351
352 if (freqread)
353 return enif_make_atom(env, "ok");
354 else
355 return enif_make_tuple2(env,
356 enif_make_atom(env,
357 "comment"),
358 enif_make_string(env,
359 "No frequent read test made.",
360 ERL_NIF_LATIN1));
361 badarg:
362 return enif_make_badarg(env);
363 }
364
365 /*
366 * Hammer try rwlock testcase
367 */
368
369 #define HAMMER_TRYRW_NO_W_THREADS 10
370 #define HAMMER_TRYRW_NO_THREADS 20
371 #define HAMMER_TRYRW_NO_WLOCK_COUNT 10000000
372 #define HAMMER_TRYRW_NO_RLOCK_COUNT 10000000
373 #define HAMMER_TRYRW_NO_WLOCK_WAIT_COUNT ((10*HAMMER_TRYRW_NO_WLOCK_COUNT)/8)
374 #define HAMMER_TRYRW_NO_RLOCK_WAIT_COUNT ((10*HAMMER_TRYRW_NO_RLOCK_COUNT)/8)
375
376 typedef struct {
377 RWMUTEX_T *rwlock;
378 ATOMIC_T is_locked;
379 int lock_check;
380 int w_count;
381 ATOMIC_T r_count;
382 } hammer_tryrw_t;
383
384 static void *
hammer_tryrw_w(void * varg)385 hammer_tryrw_w(void *varg)
386 {
387 hammer_tryrw_t *arg = varg;
388 int stop = 0;
389 int wait = 0;
390 do {
391 while (EBUSY == RWMUTEX_TRYWLOCK(arg->rwlock));
392 if (arg->lock_check) {
393 ASSERT(!ATOMIC_READ(&arg->is_locked));
394 ATOMIC_SET(&arg->is_locked, -1);
395 }
396 if (++arg->w_count > HAMMER_TRYRW_NO_WLOCK_COUNT)
397 stop = 1;
398 else if (arg->w_count > HAMMER_TRYRW_NO_RLOCK_WAIT_COUNT)
399 wait = 1;
400 if (arg->lock_check) {
401 ASSERT(ATOMIC_READ(&arg->is_locked) == -1);
402 ATOMIC_SET(&arg->is_locked, 0);
403 }
404 RWMUTEX_WUNLOCK(arg->rwlock);
405 if (wait)
406 milli_sleep(1);
407 } while (!stop);
408 return NULL;
409 }
410
411 static void *
hammer_tryrw_r(void * varg)412 hammer_tryrw_r(void *varg)
413 {
414 hammer_tryrw_t *arg = varg;
415 long r_count;
416 int stop = 0;
417 int wait = 0;
418 do {
419 while (EBUSY == RWMUTEX_TRYRLOCK(arg->rwlock));
420 if (arg->lock_check) {
421 ASSERT(ATOMIC_READ(&arg->is_locked) >= 0);
422 ATOMIC_INC(&arg->is_locked);
423 }
424 ATOMIC_INC(&arg->r_count);
425 r_count = ATOMIC_READ(&arg->r_count);
426 if (r_count > HAMMER_TRYRW_NO_RLOCK_COUNT)
427 stop = 1;
428 else if (r_count > HAMMER_TRYRW_NO_RLOCK_WAIT_COUNT)
429 wait = 1;
430 if (arg->lock_check) {
431 ASSERT(ATOMIC_READ(&arg->is_locked) > 0);
432 ATOMIC_DEC(&arg->is_locked);
433 }
434 RWMUTEX_RUNLOCK(arg->rwlock);
435 if (wait)
436 milli_sleep(1);
437 } while (!stop);
438 return NULL;
439 }
440
441
hammer_tryrw_test(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])442 static ERL_NIF_TERM hammer_tryrw_test(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
443 {
444 hammer_tryrw_t arg;
445 char buf[10];
446 int res, freqread, i;
447 THR_ID tid[HAMMER_TRYRW_NO_THREADS];
448
449 if (argc != 1)
450 goto badarg;
451
452 arg.lock_check = get_bool(env, argv[0]);
453 if (arg.lock_check < 0)
454 goto badarg;
455
456 ATOMIC_INIT(&arg.is_locked, 0);
457 freqread = 0;
458
459 restart:
460
461 arg.w_count = 0;
462 ATOMIC_INIT(&arg.r_count, 0);
463
464 arg.rwlock = RWMUTEX_CREATE(freqread);
465
466 ASSERT(arg.rwlock);
467
468 for (i = 0; i < HAMMER_TRYRW_NO_W_THREADS; i++)
469 ASSERT(THR_CREATE(&tid[i], hammer_tryrw_w, &arg, NULL) == 0);
470 for (; i < HAMMER_TRYRW_NO_THREADS; i++)
471 ASSERT(THR_CREATE(&tid[i], hammer_tryrw_r, &arg, NULL) == 0);
472 for (i = 0; i < HAMMER_TRYRW_NO_THREADS; i++)
473 ASSERT(THR_JOIN(tid[i], NULL) == 0);
474
475 ASSERT(!ATOMIC_READ(&arg.is_locked));
476
477 RWMUTEX_DESTROY(arg.rwlock);
478
479 if (HAVE_FREQREAD_SUPPORT && !freqread) {
480 freqread = 1;
481 goto restart;
482 }
483
484 if (freqread)
485 return enif_make_atom(env, "ok");
486 else
487 return enif_make_tuple2(env,
488 enif_make_atom(env,
489 "comment"),
490 enif_make_string(env,
491 "No frequent read test made.",
492 ERL_NIF_LATIN1));
493 badarg:
494 return enif_make_badarg(env);
495 }
496
497 typedef struct {
498 int lock_check;
499 ATOMIC_T is_locked;
500 RWMUTEX_T *rwlock;
501 } rwlock_resource_t;
502
503 static void
rwlock_destructor(ErlNifEnv * env,void * obj)504 rwlock_destructor(ErlNifEnv* env, void* obj)
505 {
506 rwlock_resource_t *rwlr = obj;
507 if (rwlr->lock_check)
508 ASSERT(!ATOMIC_READ(&rwlr->is_locked));
509 RWMUTEX_DESTROY(rwlr->rwlock);
510 }
511
512 /*
513 * create_rwlock(FreqRead, LockCheck)
514 */
515
516 static ERL_NIF_TERM
create_rwlock(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])517 create_rwlock(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
518 {
519 int lock_check, freqread;
520 ERL_NIF_TERM rwlock_term;
521 rwlock_resource_t *rwlr;
522 char buf[100];
523
524 if (argc != 2)
525 goto badarg;
526
527 freqread = get_bool(env, argv[0]);
528 if (freqread < 0)
529 goto badarg;
530
531 if (!HAVE_FREQREAD_SUPPORT && freqread)
532 return enif_make_atom(env, "enotsup");
533
534 lock_check = get_bool(env, argv[1]);
535 if (lock_check < 0)
536 goto badarg;
537
538 rwlr = enif_alloc_resource(enif_priv_data(env), sizeof(rwlock_resource_t));
539 rwlr->lock_check = lock_check;
540 ATOMIC_INIT(&rwlr->is_locked, 0);
541 rwlr->rwlock = RWMUTEX_CREATE(freqread);
542 rwlock_term = enif_make_resource(env, rwlr);
543 enif_release_resource(rwlr);
544 return rwlock_term;
545
546 badarg:
547 return enif_make_badarg(env);
548 }
549
550 /*
551 * rwlock_op(RWLock, Blocking, WriteOp, WaitTime)
552 */
553
554 static ERL_NIF_TERM
rwlock_op(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])555 rwlock_op(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
556 {
557 /*
558 * Use a union for pointer type conversion to avoid compiler warnings
559 * about strict-aliasing violations with gcc-4.1. gcc >= 4.2 does not
560 * emit the warning.
561 * TODO: Reconsider use of union once gcc-4.1 is obsolete?
562 */
563 union { void* vp; rwlock_resource_t *p; } rwlr;
564 int blocking, write, wait_locked, wait_unlocked;
565
566 if (argc != 5)
567 goto badarg;
568
569 if (!enif_get_resource(env, argv[0], enif_priv_data(env), &rwlr.vp))
570 goto badarg;
571
572 blocking = get_bool(env, argv[1]);
573 if (blocking < 0)
574 goto badarg;
575
576 write = get_bool(env, argv[2]);
577 if (write < 0)
578 goto badarg;
579
580 if (!enif_get_int(env, argv[3], &wait_locked))
581 goto badarg;
582 if (wait_locked < 0)
583 goto badarg;
584
585 if (!enif_get_int(env, argv[4], &wait_unlocked))
586 goto badarg;
587 if (wait_unlocked < 0)
588 goto badarg;
589
590 if (write) {
591 if (blocking)
592 RWMUTEX_WLOCK(rwlr.p->rwlock);
593 else
594 while (EBUSY == RWMUTEX_TRYWLOCK(rwlr.p->rwlock));
595 if (rwlr.p->lock_check) {
596 ASSERT(!ATOMIC_READ(&rwlr.p->is_locked));
597 ATOMIC_SET(&rwlr.p->is_locked, -1);
598 }
599 }
600 else {
601 if (blocking)
602 RWMUTEX_RLOCK(rwlr.p->rwlock);
603 else
604 while (EBUSY == RWMUTEX_TRYRLOCK(rwlr.p->rwlock));
605 if (rwlr.p->lock_check) {
606 ASSERT(ATOMIC_READ(&rwlr.p->is_locked) >= 0);
607 ATOMIC_INC(&rwlr.p->is_locked);
608 }
609 }
610
611 if (wait_locked)
612 milli_sleep(wait_locked);
613
614 if (write) {
615 if (rwlr.p->lock_check) {
616 ASSERT(ATOMIC_READ(&rwlr.p->is_locked) == -1);
617 ATOMIC_SET(&rwlr.p->is_locked, 0);
618 }
619 RWMUTEX_WUNLOCK(rwlr.p->rwlock);
620 }
621 else {
622 if (rwlr.p->lock_check) {
623 ASSERT(ATOMIC_READ(&rwlr.p->is_locked) > 0);
624 ATOMIC_DEC(&rwlr.p->is_locked);
625 }
626 RWMUTEX_RUNLOCK(rwlr.p->rwlock);
627 }
628
629 if (wait_unlocked)
630 milli_sleep(wait_unlocked);
631
632 return enif_make_atom(env, "ok");
633 badarg:
634 return enif_make_badarg(env);
635 }
636
load_nif_lib(ErlNifEnv * env,void ** priv_data,ERL_NIF_TERM load_info)637 static int load_nif_lib(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
638 {
639 *priv_data = enif_open_resource_type(env,
640 NULL,
641 "rwlock_resource",
642 rwlock_destructor,
643 ERL_NIF_RT_CREATE,
644 NULL);
645 if (*priv_data)
646 return 0;
647 else
648 return -1;
649 }
650
651 /*
652 * 0 -> false
653 * >0 -> true
654 * <0 -> error
655 */
656
657 static int
get_bool(ErlNifEnv * env,ERL_NIF_TERM term)658 get_bool(ErlNifEnv* env, ERL_NIF_TERM term)
659 {
660 int res;
661 char buf[10];
662
663 res = enif_get_atom(env, term, buf, sizeof(buf), ERL_NIF_LATIN1);
664 if (res == 0)
665 return -1;
666 if (strcmp("false", buf) == 0)
667 return 0;
668 else if (strcmp("true", buf) == 0)
669 return 1;
670 else
671 return -1;
672 }
673
674 static int
fail(const char * file,int line,const char * function,const char * assertion)675 fail(const char *file, int line, const char *function, const char *assertion)
676 {
677 fprintf(stderr, "%s:%d: Assertion failed in %s(): %s\n",
678 file, line, function, assertion);
679 abort();
680 }
681
682 static void
milli_sleep(int ms)683 milli_sleep(int ms)
684 {
685 #ifdef __WIN32__
686 Sleep(ms);
687 #else
688 while (erts_milli_sleep(ms) != 0);
689 #endif
690 }
691
692 static ErlNifFunc nif_funcs[] = {
693 {"long_rw_test", 0, long_rw_test},
694 {"hammer_rw_test", 1, hammer_rw_test},
695 {"hammer_tryrw_test", 1, hammer_tryrw_test},
696 {"create_rwlock", 2, create_rwlock},
697 {"rwlock_op", 5, rwlock_op}
698 };
699
700 ERL_NIF_INIT(mtx_SUITE, nif_funcs, load_nif_lib, NULL, NULL, NULL)
701