1
|
#define DEBUG 1
|
2
|
#include <thread.h>
|
3
|
#include <sys/synch.h>
|
4
|
#include <sys/types.h>
|
5
|
#include <sys/debug.h>
|
6
|
#include <stdio.h>
|
7
|
#include <stdlib.h>
|
8
|
|
9
|
typedef struct zrlock {
|
10
|
mutex_t zr_mtx;
|
11
|
volatile int32_t zr_refcount;
|
12
|
cond_t zr_cv;
|
13
|
uint16_t zr_pad;
|
14
|
} zrlock_t;
|
15
|
|
16
|
void zrl_init(zrlock_t *);
|
17
|
void zrl_destroy(zrlock_t *);
|
18
|
#define zrl_add(_z) zrl_add_impl((_z), __func__)
|
19
|
void zrl_add_impl(zrlock_t *, const char *);
|
20
|
void zrl_remove(zrlock_t *);
|
21
|
int zrl_tryenter(zrlock_t *);
|
22
|
void zrl_exit(zrlock_t *);
|
23
|
int zrl_is_zero(zrlock_t *);
|
24
|
int zrl_is_locked(zrlock_t *);
|
25
|
|
26
|
#define ZRL_LOCKED -1
|
27
|
#define ZRL_DESTROYED -2
|
28
|
|
29
|
void
|
30
|
zrl_init(zrlock_t *zrl)
|
31
|
{
|
32
|
mutex_init(&zrl->zr_mtx, USYNC_THREAD, NULL);
|
33
|
zrl->zr_refcount = 0;
|
34
|
cond_init(&zrl->zr_cv, USYNC_THREAD, NULL);
|
35
|
}
|
36
|
|
37
|
void
|
38
|
zrl_destroy(zrlock_t *zrl)
|
39
|
{
|
40
|
ASSERT0(zrl->zr_refcount);
|
41
|
|
42
|
mutex_destroy(&zrl->zr_mtx);
|
43
|
zrl->zr_refcount = ZRL_DESTROYED;
|
44
|
cond_destroy(&zrl->zr_cv);
|
45
|
}
|
46
|
|
47
|
void
|
48
|
zrl_add_impl_old(zrlock_t *zrl, const char *zc)
|
49
|
{
|
50
|
uint32_t n = (uint32_t)zrl->zr_refcount;
|
51
|
|
52
|
while (n != ZRL_LOCKED) {
|
53
|
uint32_t cas = atomic_cas_32(
|
54
|
(uint32_t *)&zrl->zr_refcount, n, n + 1);
|
55
|
if (cas == n) {
|
56
|
ASSERT3S((int32_t)n, >=, 0);
|
57
|
return;
|
58
|
}
|
59
|
n = cas;
|
60
|
}
|
61
|
|
62
|
mutex_lock(&zrl->zr_mtx);
|
63
|
while (zrl->zr_refcount == ZRL_LOCKED) {
|
64
|
cond_wait(&zrl->zr_cv, &zrl->zr_mtx);
|
65
|
}
|
66
|
ASSERT3S(zrl->zr_refcount, >=, 0);
|
67
|
zrl->zr_refcount++;
|
68
|
mutex_unlock(&zrl->zr_mtx);
|
69
|
}
|
70
|
|
71
|
void
|
72
|
zrl_add_impl(zrlock_t *zrl, const char *zc)
|
73
|
{
|
74
|
uint32_t n = (uint32_t)zrl->zr_refcount;
|
75
|
|
76
|
while (1) {
|
77
|
while (n != ZRL_LOCKED) {
|
78
|
uint32_t cas = atomic_cas_32(
|
79
|
(uint32_t *)&zrl->zr_refcount, n, n + 1);
|
80
|
if (cas == n) {
|
81
|
ASSERT3S((int32_t)n, >=, 0);
|
82
|
return;
|
83
|
}
|
84
|
n = cas;
|
85
|
}
|
86
|
mutex_lock(&zrl->zr_mtx);
|
87
|
while (zrl->zr_refcount == ZRL_LOCKED) {
|
88
|
cond_wait(&zrl->zr_cv, &zrl->zr_mtx);
|
89
|
}
|
90
|
n = (uint32_t)zrl->zr_refcount;
|
91
|
mutex_unlock(&zrl->zr_mtx);
|
92
|
}
|
93
|
}
|
94
|
|
95
|
void
|
96
|
zrl_remove(zrlock_t *zrl)
|
97
|
{
|
98
|
uint32_t n;
|
99
|
|
100
|
n = atomic_dec_32_nv((uint32_t *)&zrl->zr_refcount);
|
101
|
ASSERT3S((int32_t)n, >=, 0);
|
102
|
}
|
103
|
|
104
|
int
|
105
|
zrl_tryenter(zrlock_t *zrl)
|
106
|
{
|
107
|
uint32_t n = (uint32_t)zrl->zr_refcount;
|
108
|
|
109
|
if (n == 0) {
|
110
|
uint32_t cas = atomic_cas_32(
|
111
|
(uint32_t *)&zrl->zr_refcount, 0, ZRL_LOCKED);
|
112
|
if (cas == 0) {
|
113
|
return (1);
|
114
|
}
|
115
|
}
|
116
|
|
117
|
ASSERT3S((int32_t)n, >, ZRL_DESTROYED);
|
118
|
|
119
|
return (0);
|
120
|
}
|
121
|
|
122
|
void
|
123
|
zrl_exit(zrlock_t *zrl)
|
124
|
{
|
125
|
ASSERT3S(zrl->zr_refcount, ==, ZRL_LOCKED);
|
126
|
|
127
|
mutex_lock(&zrl->zr_mtx);
|
128
|
zrl->zr_refcount = 0;
|
129
|
cond_broadcast(&zrl->zr_cv);
|
130
|
mutex_unlock(&zrl->zr_mtx);
|
131
|
}
|
132
|
|
133
|
int
|
134
|
zrl_refcount(zrlock_t *zrl)
|
135
|
{
|
136
|
ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
|
137
|
|
138
|
int n = (int)zrl->zr_refcount;
|
139
|
return (n <= 0 ? 0 : n);
|
140
|
}
|
141
|
|
142
|
int
|
143
|
zrl_is_zero(zrlock_t *zrl)
|
144
|
{
|
145
|
ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
|
146
|
|
147
|
return (zrl->zr_refcount <= 0);
|
148
|
}
|
149
|
|
150
|
int
|
151
|
zrl_is_locked(zrlock_t *zrl)
|
152
|
{
|
153
|
ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
|
154
|
|
155
|
return (zrl->zr_refcount == ZRL_LOCKED);
|
156
|
}
|
157
|
|
158
|
zrlock_t zrlock;
|
159
|
|
160
|
void *
|
161
|
func_zrl_add_remove_old(void *arg)
|
162
|
{
|
163
|
int *p = (int *) arg;
|
164
|
int i;
|
165
|
for(i = 0; i < *p; i++)
|
166
|
{
|
167
|
zrl_add_impl_old(&zrlock, NULL);
|
168
|
zrl_remove(&zrlock);
|
169
|
}
|
170
|
}
|
171
|
|
172
|
void *
|
173
|
func_zrl_add_remove_new(void *arg)
|
174
|
{
|
175
|
int *p = (int *) arg;
|
176
|
int i;
|
177
|
for(i = 0; i < *p; i++)
|
178
|
{
|
179
|
zrl_add_impl(&zrlock, NULL);
|
180
|
zrl_remove(&zrlock);
|
181
|
}
|
182
|
}
|
183
|
|
184
|
void *
|
185
|
func_zrl_tryenter_exit(void *arg)
|
186
|
{
|
187
|
int *p = (int *) arg;
|
188
|
int i;
|
189
|
for(i = 0; i < *p; i++)
|
190
|
{
|
191
|
if(!zrl_tryenter(&zrlock))
|
192
|
continue;
|
193
|
zrl_exit(&zrlock);
|
194
|
}
|
195
|
}
|
196
|
|
197
|
int main(int argc, char **argv)
|
198
|
{
|
199
|
pthread_t *tids_add_remove = NULL;
|
200
|
pthread_t *tids_enter_exit = NULL;
|
201
|
int nthreads_add_remove = 10;
|
202
|
int nthread_enter_exit = 1;
|
203
|
int loops = 1000;
|
204
|
int i;
|
205
|
int c;
|
206
|
int ret = 0;
|
207
|
boolean_t new_zrl_add_impl = B_FALSE;
|
208
|
|
209
|
while ((c = getopt(argc, argv, ":na:e:l:")) != -1) {
|
210
|
switch(c) {
|
211
|
case 'n':
|
212
|
new_zrl_add_impl = B_TRUE;
|
213
|
break;
|
214
|
case 'a':
|
215
|
nthreads_add_remove = atoi(optarg);
|
216
|
break;
|
217
|
case 'e':
|
218
|
nthread_enter_exit = atoi(optarg);
|
219
|
break;
|
220
|
case 'l':
|
221
|
loops = atoi(optarg);
|
222
|
break;
|
223
|
case ':':
|
224
|
printf("-%c without arg\n", optopt);
|
225
|
exit(1);
|
226
|
break;
|
227
|
default:
|
228
|
exit(2);
|
229
|
}
|
230
|
}
|
231
|
|
232
|
printf("-a %d: nthreads_add_remove = %d\n", nthreads_add_remove,
|
233
|
nthreads_add_remove);
|
234
|
printf("-e %d: nthread_enter_exit = %d\n", nthread_enter_exit,
|
235
|
nthread_enter_exit);
|
236
|
printf("-l %d: loops = %d\n", loops, loops);
|
237
|
printf("Run new zrl_add_impl: %s\n", new_zrl_add_impl ? "YES" : "NO");
|
238
|
|
239
|
zrl_init(&zrlock);
|
240
|
tids_add_remove = calloc(nthreads_add_remove, sizeof(pthread_t));
|
241
|
tids_enter_exit = calloc(nthread_enter_exit, sizeof(pthread_t));
|
242
|
|
243
|
for(i = 0; i < nthreads_add_remove; i++) {
|
244
|
if(pthread_create(&tids_add_remove[i], NULL,
|
245
|
new_zrl_add_impl ? func_zrl_add_remove_new : func_zrl_add_remove_old, &loops) != 0) {
|
246
|
fprintf(stderr, "ERROR: failed to create thread\n");
|
247
|
ret = 3;
|
248
|
goto done;
|
249
|
}
|
250
|
}
|
251
|
|
252
|
for(i = 0; i < nthread_enter_exit; i++) {
|
253
|
if(pthread_create(&tids_enter_exit[i], NULL, func_zrl_tryenter_exit, &loops) != 0) {
|
254
|
fprintf(stderr, "ERROR: failed to create thread\n");
|
255
|
ret = 3;
|
256
|
goto done;
|
257
|
}
|
258
|
}
|
259
|
|
260
|
for(i = 0; i < nthreads_add_remove; i++) {
|
261
|
(void) pthread_join(tids_add_remove[i], NULL);
|
262
|
}
|
263
|
|
264
|
for(i = 0; i < nthread_enter_exit; i++) {
|
265
|
(void) pthread_join(tids_enter_exit[i], NULL);
|
266
|
}
|
267
|
|
268
|
ASSERT3S(zrlock.zr_refcount, ==, 0);
|
269
|
|
270
|
done:
|
271
|
zrl_destroy(&zrlock);
|
272
|
if(tids_add_remove) free(tids_add_remove);
|
273
|
if(tids_enter_exit) free(tids_enter_exit);
|
274
|
|
275
|
printf("DONE.\n");
|
276
|
return(ret);
|
277
|
}
|
278
|
|
279
|
/*
|
280
|
build: gcc -m64 -o zrlock-test zrlock-test.c
|
281
|
|
282
|
./zrlock-test
|
283
|
assertion failed for thread 0xfffffd7ffef03240, thread-id 9: (int32_t)n >= 0 (0xffffffffffffffff >= 0x0), file zrlock-test.c, line 101
|
284
|
|
285
|
./zrlock-test -n
|
286
|
Run new zrl_add_impl: YES
|
287
|
*/
|