xref: /haiku/src/system/libroot/posix/pthread/pthread_mutex.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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 
24 static const pthread_mutexattr pthread_mutexattr_default = {
25 	PTHREAD_MUTEX_DEFAULT,
26 	false
27 };
28 
29 
30 int
31 pthread_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 
45 int
46 pthread_mutex_destroy(pthread_mutex_t* mutex)
47 {
48 	return 0;
49 }
50 
51 
52 status_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 	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, flags, timeout);
87 		} while (error == B_INTERRUPTED);
88 
89 		if (error != B_OK)
90 			return error;
91 	}
92 
93 	// we have locked the mutex for the first time
94 	mutex->owner = thisThread;
95 	mutex->owner_count = 1;
96 
97 	return 0;
98 }
99 
100 
101 int
102 pthread_mutex_lock(pthread_mutex_t* mutex)
103 {
104 	return __pthread_mutex_lock(mutex, 0, B_INFINITE_TIMEOUT);
105 }
106 
107 
108 int
109 pthread_mutex_trylock(pthread_mutex_t* mutex)
110 {
111 	return __pthread_mutex_lock(mutex, B_ABSOLUTE_REAL_TIME_TIMEOUT, -1);
112 }
113 
114 
115 int
116 pthread_mutex_clocklock(pthread_mutex_t* mutex, clockid_t clock_id,
117 	const struct timespec* abstime)
118 {
119 	// translate the timeout
120 	bool invalidTime = false;
121 	bigtime_t timeout = 0;
122 	if (abstime != NULL && abstime->tv_nsec < 1000 * 1000 * 1000
123 		&& abstime->tv_nsec >= 0) {
124 		timeout = abstime->tv_sec * 1000000LL + abstime->tv_nsec / 1000LL;
125 	} else
126 		invalidTime = true;
127 
128 	uint32 flags = 0;
129 	switch (clock_id) {
130 		case CLOCK_REALTIME:
131 			flags = B_ABSOLUTE_REAL_TIME_TIMEOUT;
132 			break;
133 		case CLOCK_MONOTONIC:
134 			flags = B_ABSOLUTE_TIMEOUT;
135 			break;
136 		default:
137 			invalidTime = true;
138 			break;
139 	}
140 
141 	status_t status = __pthread_mutex_lock(mutex, flags, timeout);
142 	if (status != B_OK && invalidTime) {
143 		// The timespec was not valid and the mutex could not be locked
144 		// immediately.
145 		return EINVAL;
146 	}
147 
148 	return status;
149 }
150 
151 
152 int
153 pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* abstime)
154 {
155 	return pthread_mutex_clocklock(mutex, CLOCK_REALTIME, abstime);
156 }
157 
158 
159 int
160 pthread_mutex_unlock(pthread_mutex_t* mutex)
161 {
162 	if (mutex->owner != find_thread(NULL))
163 		return EPERM;
164 
165 	if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_RECURSIVE
166 		&& --mutex->owner_count > 0) {
167 		// still locked
168 		return 0;
169 	}
170 
171 	mutex->owner = -1;
172 
173 	// clear the locked flag
174 	int32 oldValue = atomic_and((int32*)&mutex->lock,
175 		~(int32)B_USER_MUTEX_LOCKED);
176 	if ((oldValue & B_USER_MUTEX_WAITING) != 0)
177 		_kern_mutex_unlock((int32*)&mutex->lock, 0);
178 
179 	return 0;
180 }
181 
182 
183 int
184 pthread_mutex_getprioceiling(const pthread_mutex_t* mutex, int* _prioCeiling)
185 {
186 	if (mutex == NULL || _prioCeiling == NULL)
187 		return EINVAL;
188 
189 	*_prioCeiling = 0;
190 		// not implemented
191 
192 	return 0;
193 }
194 
195 
196 int
197 pthread_mutex_setprioceiling(pthread_mutex_t* mutex, int prioCeiling,
198 	int* _oldCeiling)
199 {
200 	if (mutex == NULL)
201 		return EINVAL;
202 
203 	// not implemented
204 	return EPERM;
205 }
206