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