1 /* $OpenBSD: pthread_rwlock.c,v 1.2 2012/02/26 11:47:51 miod Exp $ */
2 /* PUBLIC DOMAIN Feb 2012 <guenther@openbsd.org> */
3 
4 #include <sys/types.h>
5 #include <assert.h>
6 #include <err.h>
7 #include <pthread.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <time.h>
11 #include <unistd.h>
12 
13 /*
14  * Set up an rwlock with a few reader threads, start a writer blocking,
15  * then let go the reader threads one by one.  Verify that the writer
16  * thread gets, gets out, and then the rwlock can be locked for reading
17  * again.
18  */
19 
20 pthread_rwlock_t	rw;
21 
22 pthread_mutex_t m;
23 pthread_cond_t c;
24 enum
25 {
26 	UNLOCKED,
27 	NUM_READERS = 6,
28 	WRITE_STARTED,
29 	WRITE,
30 	WRITE_DONE,
31 } state;
32 time_t write_started;
33 
34 static void *
35 reader(void *arg)
36 {
37 	int	me = *(int *)arg;
38 	int diff;
39 
40 	pthread_mutex_lock(&m);
41 	assert(state < NUM_READERS);
42 	pthread_rwlock_rdlock(&rw);
43 	state++;
44 	printf("reader %d locked, state = %d\n", me, state);
45 	pthread_cond_broadcast(&c);
46 	pthread_mutex_unlock(&m);
47 
48 	pthread_mutex_lock(&m);
49 	while (state < WRITE_STARTED) {
50 		pthread_cond_wait(&c, &m);
51 		printf("reader %d woken, state = %d\n", me, state);
52 	}
53 
54 	diff = difftime(time(NULL), write_started);
55 	if (diff < 2)
56 		sleep(3 - diff);
57 
58 	pthread_rwlock_unlock(&rw);
59 	printf("reader %d unlocked\n", me);
60 	sleep(1);
61 
62 	pthread_mutex_unlock(&m);
63 
64 	pthread_mutex_lock(&m);
65 	while (state >= WRITE_STARTED) {
66 		pthread_cond_wait(&c, &m);
67 		printf("reader %d woken, state = %d\n", me, state);
68 	}
69 	state++;
70 	printf("reader %d trying again (%d)\n", me, state);
71 	pthread_rwlock_rdlock(&rw);
72 	printf("reader %d locked again (%d)\n", me, state);
73 	pthread_cond_broadcast(&c);
74 	while (state != NUM_READERS) {
75 		pthread_cond_wait(&c, &m);
76 		printf("reader %d woken, state = %d\n", me, state);
77 	}
78 	pthread_mutex_unlock(&m);
79 	pthread_rwlock_unlock(&rw);
80 
81 	printf("reader %d exiting\n", me);
82 	return NULL;
83 }
84 
85 static void *
86 writer(void *arg)
87 {
88 	pthread_mutex_lock(&m);
89 	printf("writer started, state = %d\n", state);
90 	while (state != NUM_READERS) {
91 		pthread_cond_wait(&c, &m);
92 		printf("writer woken, state = %d\n", state);
93 	}
94 	state = WRITE_STARTED;
95 	printf("writer starting\n");
96 	write_started = time(NULL);
97 	pthread_cond_broadcast(&c);
98 	pthread_mutex_unlock(&m);
99 
100 	pthread_rwlock_wrlock(&rw);
101 	printf("writer locked\n");
102 
103 	pthread_mutex_lock(&m);
104 	state = WRITE;
105 	pthread_cond_broadcast(&c);
106 
107 	while (state == WRITE)
108 		pthread_cond_wait(&c, &m);
109 
110 	printf("writer unlocking\n");
111 	pthread_rwlock_unlock(&rw);
112 	state = UNLOCKED;
113 	pthread_cond_broadcast(&c);
114 	pthread_mutex_unlock(&m);
115 
116 	printf("writer exiting\n");
117 	return NULL;
118 }
119 
120 
121 int
122 main(void)
123 {
124 	pthread_t	tr[NUM_READERS], tw;
125 	int	ids[NUM_READERS], i, r;
126 
127 	pthread_rwlock_init(&rw, NULL);
128 	pthread_mutex_init(&m, NULL);
129 	pthread_cond_init(&c, NULL);
130 	state = UNLOCKED;
131 
132 	for (i = 0; i < NUM_READERS; i++) {
133 		ids[i] = i;
134 		if ((r = pthread_create(&tr[i], NULL, reader, &ids[i])))
135 			errx(1, "create %d: %s", i, strerror(r));
136 	}
137 
138 	if ((r = pthread_create(&tw, NULL, writer, NULL)))
139 		errx(1, "create writer: %s", strerror(r));
140 
141 	pthread_mutex_lock(&m);
142 	while (state != WRITE)
143 		pthread_cond_wait(&c, &m);
144 	state = WRITE_DONE;
145 	pthread_mutex_unlock(&m);
146 	pthread_cond_broadcast(&c);
147 
148 	pthread_join(tw, NULL);
149 
150 	for (i = 0; i < NUM_READERS; i++)
151 		pthread_join(tr[i], NULL);
152 	return 0;
153 }
154