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 <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#include <syscalls.h>
16#include <user_mutex_defs.h>
17
18
19#define MUTEX_FLAG_SHARED	0x80000000
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
52static status_t
53mutex_lock(pthread_mutex_t* mutex, 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	int32 oldValue = atomic_or((int32*)&mutex->lock, B_USER_MUTEX_LOCKED);
77
78	if ((oldValue & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) != 0) {
79		// someone else has the lock or is at least waiting for it
80		if (timeout < 0)
81			return EBUSY;
82
83		// we have to call the kernel
84		status_t error;
85		do {
86			error = _kern_mutex_lock((int32*)&mutex->lock, NULL,
87				timeout == B_INFINITE_TIMEOUT
88					? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT,
89				timeout);
90		} while (error == B_INTERRUPTED);
91
92		if (error != B_OK)
93			return error;
94	}
95
96	// we have locked the mutex for the first time
97	mutex->owner = thisThread;
98	mutex->owner_count = 1;
99
100	return 0;
101}
102
103
104int
105pthread_mutex_lock(pthread_mutex_t* mutex)
106{
107	return mutex_lock(mutex, B_INFINITE_TIMEOUT);
108}
109
110
111int
112pthread_mutex_trylock(pthread_mutex_t* mutex)
113{
114	return mutex_lock(mutex, -1);
115}
116
117
118int
119pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* tv)
120{
121	// translate the timeout
122	bool invalidTime = false;
123	bigtime_t timeout = 0;
124	if (tv && tv->tv_nsec < 1000 * 1000 * 1000 && tv->tv_nsec >= 0)
125		timeout = tv->tv_sec * 1000000LL + tv->tv_nsec / 1000LL;
126	else
127		invalidTime = true;
128
129	status_t status = mutex_lock(mutex, timeout);
130	if (status != B_OK && invalidTime) {
131		// The timespec was not valid and the mutex could not be locked
132		// immediately.
133		return EINVAL;
134	}
135
136	return status;
137}
138
139
140int
141pthread_mutex_unlock(pthread_mutex_t* mutex)
142{
143	if (mutex->owner != find_thread(NULL))
144		return EPERM;
145
146	if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_RECURSIVE
147		&& --mutex->owner_count > 0) {
148		// still locked
149		return 0;
150	}
151
152	mutex->owner = -1;
153
154	// clear the locked flag
155	int32 oldValue = atomic_and((int32*)&mutex->lock,
156		~(int32)B_USER_MUTEX_LOCKED);
157	if ((oldValue & B_USER_MUTEX_WAITING) != 0)
158		_kern_mutex_unlock((int32*)&mutex->lock, 0);
159
160	return 0;
161}
162
163
164int
165pthread_mutex_getprioceiling(pthread_mutex_t* mutex, int* _prioCeiling)
166{
167	if (mutex == NULL || _prioCeiling == NULL)
168		return EINVAL;
169
170	*_prioCeiling = 0;
171		// not implemented
172
173	return 0;
174}
175
176
177int
178pthread_mutex_setprioceiling(pthread_mutex_t* mutex, int prioCeiling,
179	int* _oldCeiling)
180{
181	if (mutex == NULL)
182		return EINVAL;
183
184	// not implemented
185	return EPERM;
186}
187