1/*
2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2003-2005, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include <pthread.h>
9#include "pthread_private.h"
10
11#include <assert.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include <syscalls.h>
17#include <user_mutex_defs.h>
18
19
20#define MUTEX_TYPE_BITS		0x0000000f
21#define MUTEX_TYPE(mutex)	((mutex)->flags & MUTEX_TYPE_BITS)
22
23
24static const pthread_mutexattr pthread_mutexattr_default = {
25	PTHREAD_MUTEX_DEFAULT,
26	false
27};
28
29
30int
31pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* _attr)
32{
33	const pthread_mutexattr* attr = _attr != NULL
34		? *_attr : &pthread_mutexattr_default;
35
36	mutex->lock = 0;
37	mutex->owner = -1;
38	mutex->owner_count = 0;
39	mutex->flags = attr->type | (attr->process_shared ? MUTEX_FLAG_SHARED : 0);
40
41	return 0;
42}
43
44
45int
46pthread_mutex_destroy(pthread_mutex_t* mutex)
47{
48	return 0;
49}
50
51
52status_t
53__pthread_mutex_lock(pthread_mutex_t* mutex, uint32 flags, bigtime_t timeout)
54{
55	thread_id thisThread = find_thread(NULL);
56
57	if (mutex->owner == thisThread) {
58		// recursive locking handling
59		if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_RECURSIVE) {
60			if (mutex->owner_count == INT32_MAX)
61				return EAGAIN;
62
63			mutex->owner_count++;
64			return 0;
65		}
66
67		// deadlock check (not for PTHREAD_MUTEX_NORMAL as per the specs)
68		if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_ERRORCHECK
69			|| MUTEX_TYPE(mutex) == PTHREAD_MUTEX_DEFAULT) {
70			// we detect this kind of deadlock and return an error
71			return timeout < 0 ? EBUSY : EDEADLK;
72		}
73	}
74
75	// set the locked flag
76	const int32 oldValue = atomic_test_and_set((int32*)&mutex->lock, B_USER_MUTEX_LOCKED, 0);
77	if (oldValue != 0) {
78		// someone else has the lock or is at least waiting for it
79		if (timeout < 0)
80			return EBUSY;
81		if ((mutex->flags & MUTEX_FLAG_SHARED) != 0)
82			flags |= B_USER_MUTEX_SHARED;
83
84		// we have to call the kernel
85		status_t error;
86		do {
87			error = _kern_mutex_lock((int32*)&mutex->lock, NULL, flags, timeout);
88		} while (error == B_INTERRUPTED);
89
90		if (error != B_OK)
91			return error;
92	}
93
94	// we have locked the mutex for the first time
95	assert(mutex->owner == -1);
96	mutex->owner = thisThread;
97	mutex->owner_count = 1;
98
99	return 0;
100}
101
102
103int
104pthread_mutex_lock(pthread_mutex_t* mutex)
105{
106	return __pthread_mutex_lock(mutex, 0, B_INFINITE_TIMEOUT);
107}
108
109
110int
111pthread_mutex_trylock(pthread_mutex_t* mutex)
112{
113	return __pthread_mutex_lock(mutex, B_ABSOLUTE_REAL_TIME_TIMEOUT, -1);
114}
115
116
117int
118pthread_mutex_clocklock(pthread_mutex_t* mutex, clockid_t clock_id,
119	const struct timespec* abstime)
120{
121	// translate the timeout
122	bool invalidTime = false;
123	bigtime_t timeout = 0;
124	if (abstime != NULL && abstime->tv_nsec < 1000 * 1000 * 1000
125		&& abstime->tv_nsec >= 0) {
126		timeout = abstime->tv_sec * 1000000LL + abstime->tv_nsec / 1000LL;
127	} else
128		invalidTime = true;
129
130	uint32 flags = 0;
131	switch (clock_id) {
132		case CLOCK_REALTIME:
133			flags = B_ABSOLUTE_REAL_TIME_TIMEOUT;
134			break;
135		case CLOCK_MONOTONIC:
136			flags = B_ABSOLUTE_TIMEOUT;
137			break;
138		default:
139			invalidTime = true;
140			break;
141	}
142
143	status_t status = __pthread_mutex_lock(mutex, flags, timeout);
144	if (status != B_OK && invalidTime) {
145		// The timespec was not valid and the mutex could not be locked
146		// immediately.
147		return EINVAL;
148	}
149
150	return status;
151}
152
153
154int
155pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* abstime)
156{
157	return pthread_mutex_clocklock(mutex, CLOCK_REALTIME, abstime);
158}
159
160
161int
162pthread_mutex_unlock(pthread_mutex_t* mutex)
163{
164	if (mutex->owner != find_thread(NULL))
165		return EPERM;
166
167	if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_RECURSIVE
168		&& --mutex->owner_count > 0) {
169		// still locked
170		return 0;
171	}
172
173	mutex->owner = -1;
174
175	// clear the locked flag
176	int32 oldValue = atomic_and((int32*)&mutex->lock,
177		~(int32)B_USER_MUTEX_LOCKED);
178	if ((oldValue & B_USER_MUTEX_WAITING) != 0) {
179		_kern_mutex_unblock((int32*)&mutex->lock,
180			(mutex->flags & MUTEX_FLAG_SHARED) ? B_USER_MUTEX_SHARED : 0);
181	}
182
183	if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_ERRORCHECK
184		|| MUTEX_TYPE(mutex) == PTHREAD_MUTEX_DEFAULT) {
185		if ((oldValue & B_USER_MUTEX_LOCKED) == 0)
186			return EPERM;
187	}
188
189	return 0;
190}
191
192
193int
194pthread_mutex_getprioceiling(const pthread_mutex_t* mutex, int* _prioCeiling)
195{
196	if (mutex == NULL || _prioCeiling == NULL)
197		return EINVAL;
198
199	*_prioCeiling = 0;
200		// not implemented
201
202	return 0;
203}
204
205
206int
207pthread_mutex_setprioceiling(pthread_mutex_t* mutex, int prioCeiling,
208	int* _oldCeiling)
209{
210	if (mutex == NULL)
211		return EINVAL;
212
213	// not implemented
214	return EPERM;
215}
216