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