1/* $OpenBSD: pthread_rwlock.c,v 1.3 2014/05/20 01:25:24 guenther 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
20pthread_rwlock_t	rw;
21
22pthread_mutex_t m;
23pthread_cond_t c;
24enum
25{
26	UNLOCKED,
27	NUM_READERS = 6,
28	WRITE_STARTED,
29	WRITE,
30	WRITE_DONE,
31} state;
32time_t write_started;
33
34static void *
35reader(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
85static void *
86writer(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
121int
122main(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			errc(1, r, "create %d", i);
136	}
137
138	if ((r = pthread_create(&tw, NULL, writer, NULL)))
139		errc(1, r, "create writer");
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