Project

General

Profile

Bug #3746 » zrlock-test.c

test program reproducing the issue, and with proposed fix for zrl_add_impl - Youzhong Yang, 2016-09-23 03:07 PM

 
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
*/
(2-2/2)