GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/thread/rthread_mutex.c Lines: 0 105 0.0 %
Date: 2017-11-07 Branches: 0 72 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: rthread_mutex.c,v 1.3 2017/08/15 07:06:29 guenther Exp $ */
2
/*
3
 * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org>
4
 * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <assert.h>
20
#include <errno.h>
21
#include <pthread.h>
22
#include <stdint.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <unistd.h>
26
27
#include "rthread.h"
28
#include "cancel.h"
29
#include "synch.h"
30
31
/*
32
 * States defined in "Futexes Are Tricky" 5.2
33
 */
34
enum {
35
	UNLOCKED = 0,
36
	LOCKED = 1,	/* locked without waiter */
37
	CONTENDED = 2,	/* threads waiting for this mutex */
38
};
39
40
#define SPIN_COUNT	128
41
#if defined(__i386__) || defined(__amd64__)
42
#define SPIN_WAIT()	asm volatile("pause": : : "memory")
43
#else
44
#define SPIN_WAIT()	do { } while (0)
45
#endif
46
47
static _atomic_lock_t static_init_lock = _SPINLOCK_UNLOCKED;
48
49
int
50
pthread_mutex_init(pthread_mutex_t *mutexp, const pthread_mutexattr_t *attr)
51
{
52
	pthread_mutex_t mutex;
53
54
	mutex = calloc(1, sizeof(*mutex));
55
	if (mutex == NULL)
56
		return (ENOMEM);
57
58
	if (attr == NULL) {
59
		mutex->type = PTHREAD_MUTEX_DEFAULT;
60
		mutex->prioceiling = -1;
61
	} else {
62
		mutex->type = (*attr)->ma_type;
63
		mutex->prioceiling = (*attr)->ma_protocol ==
64
		    PTHREAD_PRIO_PROTECT ? (*attr)->ma_prioceiling : -1;
65
	}
66
	*mutexp = mutex;
67
68
	return (0);
69
}
70
DEF_STRONG(pthread_mutex_init);
71
72
int
73
pthread_mutex_destroy(pthread_mutex_t *mutexp)
74
{
75
	pthread_mutex_t mutex;
76
77
	if (mutexp == NULL || *mutexp == NULL)
78
		return (EINVAL);
79
80
	mutex = *mutexp;
81
	if (mutex) {
82
		if (mutex->lock != UNLOCKED) {
83
#define MSG "pthread_mutex_destroy on mutex with waiters!\n"
84
			write(2, MSG, sizeof(MSG) - 1);
85
#undef MSG
86
			return (EBUSY);
87
		}
88
		free((void *)mutex);
89
		*mutexp = NULL;
90
	}
91
92
	return (0);
93
}
94
DEF_STRONG(pthread_mutex_destroy);
95
96
static int
97
_rthread_mutex_trylock(pthread_mutex_t mutex, int trywait,
98
    const struct timespec *abs)
99
{
100
	pthread_t self = pthread_self();
101
102
	if (atomic_cas_uint(&mutex->lock, UNLOCKED, LOCKED) == UNLOCKED) {
103
		membar_enter_after_atomic();
104
		mutex->owner = self;
105
		return (0);
106
	}
107
108
	if (mutex->owner == self) {
109
		int type = mutex->type;
110
111
		/* already owner?  handle recursive behavior */
112
		if (type != PTHREAD_MUTEX_RECURSIVE) {
113
			if (trywait || type == PTHREAD_MUTEX_ERRORCHECK)
114
				return (trywait ? EBUSY : EDEADLK);
115
116
			/* self-deadlock is disallowed by strict */
117
			if (type == PTHREAD_MUTEX_STRICT_NP && abs == NULL)
118
				abort();
119
120
			/* self-deadlock, possibly until timeout */
121
			while (_twait(&mutex->type, type, CLOCK_REALTIME,
122
			    abs) != ETIMEDOUT)
123
				;
124
			return (ETIMEDOUT);
125
		} else {
126
			if (mutex->count == INT_MAX)
127
				return (EAGAIN);
128
			mutex->count++;
129
			return (0);
130
		}
131
	}
132
133
	return (EBUSY);
134
}
135
136
static int
137
_rthread_mutex_timedlock(pthread_mutex_t *mutexp, int trywait,
138
    const struct timespec *abs, int timed)
139
{
140
	pthread_t self = pthread_self();
141
	pthread_mutex_t mutex;
142
	unsigned int i, lock;
143
	int error = 0;
144
145
	if (mutexp == NULL)
146
		return (EINVAL);
147
148
	/*
149
	 * If the mutex is statically initialized, perform the dynamic
150
	 * initialization. Note: _thread_mutex_lock() in libc requires
151
	 * pthread_mutex_lock() to perform the mutex init when *mutexp
152
	 * is NULL.
153
	 */
154
	if (*mutexp == NULL) {
155
		_spinlock(&static_init_lock);
156
		if (*mutexp == NULL)
157
			error = pthread_mutex_init(mutexp, NULL);
158
		_spinunlock(&static_init_lock);
159
		if (error != 0)
160
			return (EINVAL);
161
	}
162
163
	mutex = *mutexp;
164
	_rthread_debug(5, "%p: mutex_%slock %p (%p)\n", self,
165
	    (timed ? "timed" : (trywait ? "try" : "")), (void *)mutex,
166
	    (void *)mutex->owner);
167
168
	error = _rthread_mutex_trylock(mutex, trywait, abs);
169
	if (error != EBUSY || trywait)
170
		return (error);
171
172
	/* Try hard to not enter the kernel. */
173
	for (i = 0; i < SPIN_COUNT; i ++) {
174
		if (mutex->lock == UNLOCKED)
175
			break;
176
177
		SPIN_WAIT();
178
	}
179
180
	lock = atomic_cas_uint(&mutex->lock, UNLOCKED, LOCKED);
181
	if (lock == UNLOCKED) {
182
		membar_enter_after_atomic();
183
		mutex->owner = self;
184
		return (0);
185
	}
186
187
	if (lock != CONTENDED) {
188
		/* Indicate that we're waiting on this mutex. */
189
		lock = atomic_swap_uint(&mutex->lock, CONTENDED);
190
	}
191
192
	while (lock != UNLOCKED) {
193
		error = _twait(&mutex->lock, CONTENDED, CLOCK_REALTIME, abs);
194
		if (error == ETIMEDOUT)
195
			return (error);
196
		/*
197
		 * We cannot know if there's another waiter, so in
198
		 * doubt set the state to CONTENDED.
199
		 */
200
		lock = atomic_swap_uint(&mutex->lock, CONTENDED);
201
	};
202
203
	membar_enter_after_atomic();
204
	mutex->owner = self;
205
	return (0);
206
}
207
208
int
209
pthread_mutex_trylock(pthread_mutex_t *mutexp)
210
{
211
	return (_rthread_mutex_timedlock(mutexp, 1, NULL, 0));
212
}
213
214
int
215
pthread_mutex_timedlock(pthread_mutex_t *mutexp, const struct timespec *abs)
216
{
217
	return (_rthread_mutex_timedlock(mutexp, 0, abs, 1));
218
}
219
220
int
221
pthread_mutex_lock(pthread_mutex_t *mutexp)
222
{
223
	return (_rthread_mutex_timedlock(mutexp, 0, NULL, 0));
224
}
225
DEF_STRONG(pthread_mutex_lock);
226
227
int
228
pthread_mutex_unlock(pthread_mutex_t *mutexp)
229
{
230
	pthread_t self = pthread_self();
231
	pthread_mutex_t mutex;
232
233
	if (mutexp == NULL)
234
		return (EINVAL);
235
236
	if (*mutexp == NULL)
237
#if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_ERRORCHECK
238
		return (EPERM);
239
#elif PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL
240
		return(0);
241
#else
242
		abort();
243
#endif
244
245
	mutex = *mutexp;
246
	_rthread_debug(5, "%p: mutex_unlock %p (%p)\n", self, (void *)mutex,
247
	    (void *)mutex->owner);
248
249
	if (mutex->owner != self) {
250
	_rthread_debug(5, "%p: different owner %p (%p)\n", self, (void *)mutex,
251
	    (void *)mutex->owner);
252
		if (mutex->type == PTHREAD_MUTEX_ERRORCHECK ||
253
		    mutex->type == PTHREAD_MUTEX_RECURSIVE) {
254
			return (EPERM);
255
		} else {
256
			/*
257
			 * For mutex type NORMAL our undefined behavior for
258
			 * unlocking an unlocked mutex is to succeed without
259
			 * error.  All other undefined behaviors are to
260
			 * abort() immediately.
261
			 */
262
			if (mutex->owner == NULL &&
263
			    mutex->type == PTHREAD_MUTEX_NORMAL)
264
				return (0);
265
			else
266
				abort();
267
268
		}
269
	}
270
271
	if (mutex->type == PTHREAD_MUTEX_RECURSIVE) {
272
		if (mutex->count > 0) {
273
			mutex->count--;
274
			return (0);
275
		}
276
	}
277
278
	mutex->owner = NULL;
279
	membar_exit_before_atomic();
280
	if (atomic_dec_int_nv(&mutex->lock) != UNLOCKED) {
281
		mutex->lock = UNLOCKED;
282
		_wake(&mutex->lock, 1);
283
	}
284
285
	return (0);
286
}
287
DEF_STRONG(pthread_mutex_unlock);