4
* Copyright Ericsson AB 2010. All Rights Reserved.
6
* The contents of this file are subject to the Erlang Public License,
7
* Version 1.1, (the "License"); you may not use this file except in
8
* compliance with the License. You should have received a copy of the
9
* Erlang Public License along with this software. If not, it can be
10
* retrieved online at http://www.erlang.org/.
12
* Software distributed under the License is distributed on an "AS IS"
13
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
* the License for the specific language governing rights and limitations
21
* Stress tests of rwmutex implementation.
23
* Author: Rickard Green
29
# ifndef WIN32_LEAN_AND_MEAN
30
# define WIN32_LEAN_AND_MEAN
35
# include "erl_misc_utils.h"
43
fail(const char *file, int line, const char *function, const char *assertion);
46
#define ASSERT(X) ((void) ((X) ? 1 : fail(__FILE__, __LINE__, __func__, #X)))
50
* We cannot access the ethread symbols directly; test
51
* what we got in the nif api instead...
53
#define HAVE_FREQREAD_SUPPORT 0
54
#define RWMUTEX_T ErlNifRWLock
55
#define RWMUTEX_CREATE(FR) enif_rwlock_create("dummy")
56
#define RWMUTEX_DESTROY enif_rwlock_destroy
57
#define RWMUTEX_WLOCK enif_rwlock_rwlock
58
#define RWMUTEX_TRYWLOCK enif_rwlock_tryrwlock
59
#define RWMUTEX_WUNLOCK enif_rwlock_rwunlock
60
#define RWMUTEX_TRYRLOCK enif_rwlock_tryrlock
61
#define RWMUTEX_RLOCK enif_rwlock_rlock
62
#define RWMUTEX_RUNLOCK enif_rwlock_runlock
63
#define THR_ID ErlNifTid
64
#define THR_CREATE(A, B, C, D) enif_thread_create("dummy", (A), (B), (C), (D))
65
#define THR_JOIN enif_thread_join
66
#define ATOMIC_T volatile LONG
67
#define ATOMIC_INIT(VarP, Val) (*(VarP) = (Val))
68
#define ATOMIC_SET(VarP, Val) (*(VarP) = (Val))
69
#define ATOMIC_READ(VarP) (*(VarP))
70
#define ATOMIC_INC InterlockedIncrement
71
#define ATOMIC_DEC InterlockedDecrement
75
#ifdef ETHR_USE_OWN_RWMTX_IMPL__
76
# define HAVE_FREQREAD_SUPPORT 1
78
# define HAVE_FREQREAD_SUPPORT 0
81
#define RWMUTEX_T ethr_rwmutex
83
RWMUTEX_CREATE(int freqread)
85
ethr_rwmutex *rwmtx = enif_alloc(sizeof(ethr_rwmutex));
86
ethr_rwmutex_opt rwmtx_opt = ETHR_RWMUTEX_OPT_DEFAULT_INITER;
88
rwmtx_opt.type = ETHR_RWMUTEX_TYPE_FREQUENT_READ;
90
ASSERT(ethr_rwmutex_init_opt(rwmtx, &rwmtx_opt) == 0);
94
RWMUTEX_DESTROY(ethr_rwmutex *rwmtx)
96
ASSERT(ethr_rwmutex_destroy(rwmtx) == 0);
99
#define RWMUTEX_TRYWLOCK ethr_rwmutex_tryrwlock
100
#define RWMUTEX_WLOCK ethr_rwmutex_rwlock
101
#define RWMUTEX_WUNLOCK ethr_rwmutex_rwunlock
102
#define RWMUTEX_TRYRLOCK ethr_rwmutex_tryrlock
103
#define RWMUTEX_RLOCK ethr_rwmutex_rlock
104
#define RWMUTEX_RUNLOCK ethr_rwmutex_runlock
105
#define THR_ID ethr_tid
106
#define THR_CREATE ethr_thr_create
107
#define THR_JOIN ethr_thr_join
108
#define ATOMIC_T ethr_atomic_t
109
#define ATOMIC_INIT ethr_atomic_init
110
#define ATOMIC_SET ethr_atomic_set
111
#define ATOMIC_READ ethr_atomic_read
112
#define ATOMIC_INC ethr_atomic_inc
113
#define ATOMIC_DEC ethr_atomic_dec
118
#if !defined(__func__)
119
# if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L
120
# if !defined(__GNUC__) || __GNUC__ < 2
121
# define __func__ "[unknown_function]"
123
# define __func__ __FUNCTION__
128
static void milli_sleep(int ms);
129
static int get_bool(ErlNifEnv* env, ERL_NIF_TERM term);
132
* Long rwlock testcase
135
#define LONG_RW_NO_W_THREADS 6
136
#define LONG_RW_NO_THREADS 20
137
#define LONG_RW_NO_WLOCK_COUNT 100
141
ATOMIC_T *is_wlocked;
142
ATOMIC_T *is_rlocked;
149
long_rw_w(void *varg)
151
long_rw_t *arg = varg;
154
RWMUTEX_WLOCK(arg->rwlock);
155
ASSERT(!ATOMIC_READ(arg->is_wlocked));
156
ATOMIC_SET(arg->is_wlocked, 1);
157
ASSERT(!ATOMIC_READ(arg->is_rlocked));
158
milli_sleep(arg->sleep);
159
if (++(*arg->count) > LONG_RW_NO_WLOCK_COUNT)
160
stop = *arg->stop = 1;
161
ATOMIC_SET(arg->is_wlocked, 0);
162
ASSERT(!ATOMIC_READ(arg->is_rlocked));
163
RWMUTEX_WUNLOCK(arg->rwlock);
169
long_rw_r(void *varg)
171
long_rw_t *arg = varg;
174
RWMUTEX_RLOCK(arg->rwlock);
175
ASSERT(!ATOMIC_READ(arg->is_wlocked));
176
ATOMIC_INC(arg->is_rlocked);
177
milli_sleep(arg->sleep);
179
ATOMIC_DEC(arg->is_rlocked);
180
ASSERT(!ATOMIC_READ(arg->is_wlocked));
181
RWMUTEX_RUNLOCK(arg->rwlock);
187
static ERL_NIF_TERM long_rw_test(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
189
int res, freqread, i, count, stop;
190
ATOMIC_T is_wlocked, is_rlocked;
191
THR_ID tid[LONG_RW_NO_THREADS];
193
long_rw_t targ[LONG_RW_NO_THREADS];
195
ATOMIC_INIT(&is_wlocked, 0);
196
ATOMIC_INIT(&is_rlocked, 0);
200
arg.is_wlocked = &is_wlocked;
201
arg.is_rlocked = &is_rlocked;
210
arg.rwlock = RWMUTEX_CREATE(freqread);
214
for (i = 0; i < LONG_RW_NO_W_THREADS; i++) {
216
targ[i].sleep = 100 + i*10;
217
ASSERT(THR_CREATE(&tid[i], long_rw_w, &targ[i], NULL) == 0);
219
for (; i < LONG_RW_NO_THREADS; i++) {
222
ASSERT(THR_CREATE(&tid[i], long_rw_r, &targ[i], NULL) == 0);
224
for (i = 0; i < LONG_RW_NO_THREADS; i++)
225
ASSERT(THR_JOIN(tid[i], NULL) == 0);
227
ASSERT(!ATOMIC_READ(arg.is_wlocked));
228
ASSERT(!ATOMIC_READ(arg.is_rlocked));
230
RWMUTEX_DESTROY(arg.rwlock);
232
if (HAVE_FREQREAD_SUPPORT && !freqread) {
238
return enif_make_atom(env, "ok");
240
return enif_make_tuple2(env,
243
enif_make_string(env,
244
"No frequent read test made.",
249
* Hammer rwlock testcase
252
#define HAMMER_RW_NO_W_THREADS 6
253
#define HAMMER_RW_NO_THREADS 20
254
#define HAMMER_RW_NO_WLOCK_COUNT 1000000
265
hammer_rw_w(void *varg)
267
hammer_rw_t *arg = varg;
270
RWMUTEX_WLOCK(arg->rwlock);
271
if (arg->lock_check) {
272
ASSERT(!ATOMIC_READ(&arg->is_locked));
273
ATOMIC_SET(&arg->is_locked, -1);
275
if (++arg->count > HAMMER_RW_NO_WLOCK_COUNT)
276
stop = arg->stop = 1;
277
if (arg->lock_check) {
278
ASSERT(ATOMIC_READ(&arg->is_locked) == -1);
279
ATOMIC_SET(&arg->is_locked, 0);
281
RWMUTEX_WUNLOCK(arg->rwlock);
287
hammer_rw_r(void *varg)
289
hammer_rw_t *arg = varg;
292
RWMUTEX_RLOCK(arg->rwlock);
293
if (arg->lock_check) {
294
ASSERT(ATOMIC_READ(&arg->is_locked) >= 0);
295
ATOMIC_INC(&arg->is_locked);
298
if (arg->lock_check) {
299
ASSERT(ATOMIC_READ(&arg->is_locked) > 0);
300
ATOMIC_DEC(&arg->is_locked);
302
RWMUTEX_RUNLOCK(arg->rwlock);
308
static ERL_NIF_TERM hammer_rw_test(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
312
int res, freqread, i;
313
THR_ID tid[HAMMER_RW_NO_THREADS];
318
arg.lock_check = get_bool(env, argv[0]);
319
if (arg.lock_check < 0)
322
ATOMIC_INIT(&arg.is_locked, 0);
330
arg.rwlock = RWMUTEX_CREATE(freqread);
334
for (i = 0; i < HAMMER_RW_NO_W_THREADS; i++)
335
ASSERT(THR_CREATE(&tid[i], hammer_rw_w, &arg, NULL) == 0);
336
for (; i < HAMMER_RW_NO_THREADS; i++)
337
ASSERT(THR_CREATE(&tid[i], hammer_rw_r, &arg, NULL) == 0);
338
for (i = 0; i < HAMMER_RW_NO_THREADS; i++)
339
ASSERT(THR_JOIN(tid[i], NULL) == 0);
341
ASSERT(!ATOMIC_READ(&arg.is_locked));
343
RWMUTEX_DESTROY(arg.rwlock);
345
if (HAVE_FREQREAD_SUPPORT && !freqread) {
351
return enif_make_atom(env, "ok");
353
return enif_make_tuple2(env,
356
enif_make_string(env,
357
"No frequent read test made.",
360
return enif_make_badarg(env);
364
* Hammer try rwlock testcase
367
#define HAMMER_TRYRW_NO_W_THREADS 10
368
#define HAMMER_TRYRW_NO_THREADS 20
369
#define HAMMER_TRYRW_NO_WLOCK_COUNT 10000000
370
#define HAMMER_TRYRW_NO_RLOCK_COUNT 10000000
371
#define HAMMER_TRYRW_NO_WLOCK_WAIT_COUNT ((10*HAMMER_TRYRW_NO_WLOCK_COUNT)/8)
372
#define HAMMER_TRYRW_NO_RLOCK_WAIT_COUNT ((10*HAMMER_TRYRW_NO_RLOCK_COUNT)/8)
383
hammer_tryrw_w(void *varg)
385
hammer_tryrw_t *arg = varg;
389
while (EBUSY == RWMUTEX_TRYWLOCK(arg->rwlock));
390
if (arg->lock_check) {
391
ASSERT(!ATOMIC_READ(&arg->is_locked));
392
ATOMIC_SET(&arg->is_locked, -1);
394
if (++arg->w_count > HAMMER_TRYRW_NO_WLOCK_COUNT)
396
else if (arg->w_count > HAMMER_TRYRW_NO_RLOCK_WAIT_COUNT)
398
if (arg->lock_check) {
399
ASSERT(ATOMIC_READ(&arg->is_locked) == -1);
400
ATOMIC_SET(&arg->is_locked, 0);
402
RWMUTEX_WUNLOCK(arg->rwlock);
410
hammer_tryrw_r(void *varg)
412
hammer_tryrw_t *arg = varg;
417
while (EBUSY == RWMUTEX_TRYRLOCK(arg->rwlock));
418
if (arg->lock_check) {
419
ASSERT(ATOMIC_READ(&arg->is_locked) >= 0);
420
ATOMIC_INC(&arg->is_locked);
422
ATOMIC_INC(&arg->r_count);
423
r_count = ATOMIC_READ(&arg->r_count);
424
if (r_count > HAMMER_TRYRW_NO_RLOCK_COUNT)
426
else if (r_count > HAMMER_TRYRW_NO_RLOCK_WAIT_COUNT)
428
if (arg->lock_check) {
429
ASSERT(ATOMIC_READ(&arg->is_locked) > 0);
430
ATOMIC_DEC(&arg->is_locked);
432
RWMUTEX_RUNLOCK(arg->rwlock);
440
static ERL_NIF_TERM hammer_tryrw_test(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
444
int res, freqread, i;
445
THR_ID tid[HAMMER_TRYRW_NO_THREADS];
450
arg.lock_check = get_bool(env, argv[0]);
451
if (arg.lock_check < 0)
454
ATOMIC_INIT(&arg.is_locked, 0);
460
ATOMIC_INIT(&arg.r_count, 0);
462
arg.rwlock = RWMUTEX_CREATE(freqread);
466
for (i = 0; i < HAMMER_TRYRW_NO_W_THREADS; i++)
467
ASSERT(THR_CREATE(&tid[i], hammer_tryrw_w, &arg, NULL) == 0);
468
for (; i < HAMMER_TRYRW_NO_THREADS; i++)
469
ASSERT(THR_CREATE(&tid[i], hammer_tryrw_r, &arg, NULL) == 0);
470
for (i = 0; i < HAMMER_TRYRW_NO_THREADS; i++)
471
ASSERT(THR_JOIN(tid[i], NULL) == 0);
473
ASSERT(!ATOMIC_READ(&arg.is_locked));
475
RWMUTEX_DESTROY(arg.rwlock);
477
if (HAVE_FREQREAD_SUPPORT && !freqread) {
483
return enif_make_atom(env, "ok");
485
return enif_make_tuple2(env,
488
enif_make_string(env,
489
"No frequent read test made.",
492
return enif_make_badarg(env);
502
rwlock_destructor(ErlNifEnv* env, void* obj)
504
rwlock_resource_t *rwlr = obj;
505
if (rwlr->lock_check)
506
ASSERT(!ATOMIC_READ(&rwlr->is_locked));
507
RWMUTEX_DESTROY(rwlr->rwlock);
511
* create_rwlock(FreqRead, LockCheck)
515
create_rwlock(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
517
int lock_check, freqread;
518
ERL_NIF_TERM rwlock_term;
519
rwlock_resource_t *rwlr;
525
freqread = get_bool(env, argv[0]);
529
if (!HAVE_FREQREAD_SUPPORT && freqread)
530
return enif_make_atom(env, "enotsup");
532
lock_check = get_bool(env, argv[1]);
536
rwlr = enif_alloc_resource(enif_priv_data(env), sizeof(rwlock_resource_t));
537
rwlr->lock_check = lock_check;
538
ATOMIC_INIT(&rwlr->is_locked, 0);
539
rwlr->rwlock = RWMUTEX_CREATE(freqread);
540
rwlock_term = enif_make_resource(env, rwlr);
541
enif_release_resource(rwlr);
545
return enif_make_badarg(env);
549
* rwlock_op(RWLock, Blocking, WriteOp, WaitTime)
553
rwlock_op(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
555
rwlock_resource_t *rwlr;
556
int blocking, write, wait_locked, wait_unlocked;
561
if (!enif_get_resource(env, argv[0], enif_priv_data(env), (void **) &rwlr))
564
blocking = get_bool(env, argv[1]);
568
write = get_bool(env, argv[2]);
572
if (!enif_get_int(env, argv[3], &wait_locked))
577
if (!enif_get_int(env, argv[4], &wait_unlocked))
579
if (wait_unlocked < 0)
584
RWMUTEX_WLOCK(rwlr->rwlock);
586
while (EBUSY == RWMUTEX_TRYWLOCK(rwlr->rwlock));
587
if (rwlr->lock_check) {
588
ASSERT(!ATOMIC_READ(&rwlr->is_locked));
589
ATOMIC_SET(&rwlr->is_locked, -1);
594
RWMUTEX_RLOCK(rwlr->rwlock);
596
while (EBUSY == RWMUTEX_TRYRLOCK(rwlr->rwlock));
597
if (rwlr->lock_check) {
598
ASSERT(ATOMIC_READ(&rwlr->is_locked) >= 0);
599
ATOMIC_INC(&rwlr->is_locked);
604
milli_sleep(wait_locked);
607
if (rwlr->lock_check) {
608
ASSERT(ATOMIC_READ(&rwlr->is_locked) == -1);
609
ATOMIC_SET(&rwlr->is_locked, 0);
611
RWMUTEX_WUNLOCK(rwlr->rwlock);
614
if (rwlr->lock_check) {
615
ASSERT(ATOMIC_READ(&rwlr->is_locked) > 0);
616
ATOMIC_DEC(&rwlr->is_locked);
618
RWMUTEX_RUNLOCK(rwlr->rwlock);
622
milli_sleep(wait_unlocked);
624
return enif_make_atom(env, "ok");
626
return enif_make_badarg(env);
629
static int load_nif_lib(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
631
*priv_data = enif_open_resource_type(env,
650
get_bool(ErlNifEnv* env, ERL_NIF_TERM term)
655
res = enif_get_atom(env, term, buf, sizeof(buf), ERL_NIF_LATIN1);
658
if (strcmp("false", buf) == 0)
660
else if (strcmp("true", buf) == 0)
667
fail(const char *file, int line, const char *function, const char *assertion)
669
fprintf(stderr, "%s:%d: Assertion failed in %s(): %s\n",
670
file, line, function, assertion);
680
while (erts_milli_sleep(ms) != 0);
684
static ErlNifFunc nif_funcs[] = {
685
{"long_rw_test", 0, long_rw_test},
686
{"hammer_rw_test", 1, hammer_rw_test},
687
{"hammer_tryrw_test", 1, hammer_tryrw_test},
688
{"create_rwlock", 2, create_rwlock},
689
{"rwlock_op", 5, rwlock_op}
692
ERL_NIF_INIT(mtx_SUITE, nif_funcs, load_nif_lib, NULL, NULL, NULL)