GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/librthread/rthread_rwlock.c Lines: 76 104 73.1 %
Date: 2017-11-07 Branches: 32 76 42.1 %

Line Branch Exec Source
1
/*	$OpenBSD: rthread_rwlock.c,v 1.10 2017/07/29 16:42:10 deraadt Exp $ */
2
/*
3
 * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
4
 * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org>
5
 * All Rights Reserved.
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 */
19
/*
20
 * rwlocks
21
 */
22
23
#include <assert.h>
24
#include <stdlib.h>
25
#include <unistd.h>
26
#include <errno.h>
27
28
#include <pthread.h>
29
30
#include "rthread.h"
31
32
static _atomic_lock_t rwlock_init_lock = _SPINLOCK_UNLOCKED;
33
34
int
35
pthread_rwlock_init(pthread_rwlock_t *lockp,
36
    const pthread_rwlockattr_t *attrp __unused)
37
{
38
	pthread_rwlock_t lock;
39
40
128
	lock = calloc(1, sizeof(*lock));
41
64
	if (!lock)
42
		return (errno);
43
64
	lock->lock = _SPINLOCK_UNLOCKED;
44
64
	TAILQ_INIT(&lock->writers);
45
46
64
	*lockp = lock;
47
48
64
	return (0);
49
64
}
50
DEF_STD(pthread_rwlock_init);
51
52
int
53
pthread_rwlock_destroy(pthread_rwlock_t *lockp)
54
{
55
	pthread_rwlock_t lock;
56
57
	assert(lockp);
58
	lock = *lockp;
59
	if (lock) {
60
		if (lock->readers || !TAILQ_EMPTY(&lock->writers)) {
61
#define MSG "pthread_rwlock_destroy on rwlock with waiters!\n"
62
			write(2, MSG, sizeof(MSG) - 1);
63
#undef MSG
64
			return (EBUSY);
65
		}
66
		free(lock);
67
	}
68
	*lockp = NULL;
69
70
	return (0);
71
}
72
73
static int
74
_rthread_rwlock_ensure_init(pthread_rwlock_t *lockp)
75
{
76
	int ret = 0;
77
78
	/*
79
	 * If the rwlock is statically initialized, perform the dynamic
80
	 * initialization.
81
	 */
82
4990
	if (*lockp == NULL)
83
	{
84
24
		_spinlock(&rwlock_init_lock);
85
24
		if (*lockp == NULL)
86
24
			ret = pthread_rwlock_init(lockp, NULL);
87
24
		_spinunlock(&rwlock_init_lock);
88
24
	}
89
2495
	return (ret);
90
}
91
92
93
static int
94
_rthread_rwlock_rdlock(pthread_rwlock_t *lockp, const struct timespec *abstime,
95
    int try)
96
{
97
	pthread_rwlock_t lock;
98
4772
	pthread_t thread = pthread_self();
99
	int error;
100
101
2386
	if ((error = _rthread_rwlock_ensure_init(lockp)))
102
		return (error);
103
104
2386
	lock = *lockp;
105
4772
	_rthread_debug(5, "%p: rwlock_rdlock %p\n", (void *)thread,
106
2386
	    (void *)lock);
107
2386
	_spinlock(&lock->lock);
108
109
	/* writers have precedence */
110

4767
	if (lock->owner == NULL && TAILQ_EMPTY(&lock->writers))
111
2381
		lock->readers++;
112
5
	else if (try)
113
		error = EBUSY;
114
5
	else if (lock->owner == thread)
115
		error = EDEADLK;
116
	else {
117
		do {
118
10
			if (__thrsleep(lock, CLOCK_REALTIME, abstime,
119
10
			    &lock->lock, NULL) == EWOULDBLOCK)
120
				return (ETIMEDOUT);
121
5
			_spinlock(&lock->lock);
122

10
		} while (lock->owner != NULL || !TAILQ_EMPTY(&lock->writers));
123
5
		lock->readers++;
124
	}
125
2386
	_spinunlock(&lock->lock);
126
127
2386
	return (error);
128
2386
}
129
130
int
131
pthread_rwlock_rdlock(pthread_rwlock_t *lockp)
132
{
133
4772
	return (_rthread_rwlock_rdlock(lockp, NULL, 0));
134
}
135
136
int
137
pthread_rwlock_tryrdlock(pthread_rwlock_t *lockp)
138
{
139
	return (_rthread_rwlock_rdlock(lockp, NULL, 1));
140
}
141
142
int
143
pthread_rwlock_timedrdlock(pthread_rwlock_t *lockp,
144
    const struct timespec *abstime)
145
{
146
	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
147
	    abstime->tv_nsec > 1000000000)
148
		return (EINVAL);
149
	return (_rthread_rwlock_rdlock(lockp, abstime, 0));
150
}
151
152
153
static int
154
_rthread_rwlock_wrlock(pthread_rwlock_t *lockp, const struct timespec *abstime,
155
    int try)
156
{
157
	pthread_rwlock_t lock;
158
218
	pthread_t thread = pthread_self();
159
	int error;
160
161
109
	if ((error = _rthread_rwlock_ensure_init(lockp)))
162
		return (error);
163
164
109
	lock = *lockp;
165
166
218
	_rthread_debug(5, "%p: rwlock_timedwrlock %p\n", (void *)thread,
167
109
	    (void *)lock);
168
109
	_spinlock(&lock->lock);
169

197
	if (lock->readers == 0 && lock->owner == NULL)
170
88
		lock->owner = thread;
171
21
	else if (try)
172
		error = EBUSY;
173
21
	else if (lock->owner == thread)
174
		error = EDEADLK;
175
	else {
176
		int do_wait;
177
178
		/* gotta block */
179
21
		TAILQ_INSERT_TAIL(&lock->writers, thread, waiting);
180
21
		do {
181
21
			do_wait = __thrsleep(thread, CLOCK_REALTIME, abstime,
182
42
			    &lock->lock, NULL) != EWOULDBLOCK;
183
21
			_spinlock(&lock->lock);
184
21
		} while (lock->owner != thread && do_wait);
185
186
21
		if (lock->owner != thread) {
187
			/* timed out, sigh */
188
			TAILQ_REMOVE(&lock->writers, thread, waiting);
189
			error = ETIMEDOUT;
190
		}
191
	}
192
109
	_spinunlock(&lock->lock);
193
194
109
	return (error);
195
109
}
196
197
int
198
pthread_rwlock_wrlock(pthread_rwlock_t *lockp)
199
{
200
218
	return (_rthread_rwlock_wrlock(lockp, NULL, 0));
201
}
202
203
int
204
pthread_rwlock_trywrlock(pthread_rwlock_t *lockp)
205
{
206
	return (_rthread_rwlock_wrlock(lockp, NULL, 1));
207
}
208
209
int
210
pthread_rwlock_timedwrlock(pthread_rwlock_t *lockp,
211
    const struct timespec *abstime)
212
{
213
	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
214
	    abstime->tv_nsec > 1000000000)
215
		return (EINVAL);
216
	return (_rthread_rwlock_wrlock(lockp, abstime, 0));
217
}
218
219
220
int
221
pthread_rwlock_unlock(pthread_rwlock_t *lockp)
222
{
223
	pthread_rwlock_t lock;
224
4918
	pthread_t thread = pthread_self();
225
	pthread_t next;
226
	int was_writer;
227
228
2459
	lock = *lockp;
229
230
4918
	_rthread_debug(5, "%p: rwlock_unlock %p\n", (void *)thread,
231
2459
	    (void *)lock);
232
2459
	_spinlock(&lock->lock);
233
2459
	if (lock->owner != NULL) {
234
73
		assert(lock->owner == thread);
235
		was_writer = 1;
236
73
	} else {
237
2386
		assert(lock->readers > 0);
238
2386
		lock->readers--;
239
2386
		if (lock->readers > 0)
240
			goto out;
241
		was_writer = 0;
242
	}
243
244
1365
	lock->owner = next = TAILQ_FIRST(&lock->writers);
245
1365
	if (next != NULL) {
246
		/* dequeue and wake first writer */
247
63
		TAILQ_REMOVE(&lock->writers, next, waiting);
248
21
		_spinunlock(&lock->lock);
249
21
		__thrwakeup(next, 1);
250
21
		return (0);
251
	}
252
253
	/* could there have been blocked readers?  wake them all */
254
1344
	if (was_writer)
255
73
		__thrwakeup(lock, 0);
256
out:
257
2438
	_spinunlock(&lock->lock);
258
259
2438
	return (0);
260
2459
}