xref: /haiku/src/system/libroot/posix/pthread/pthread_mutex.cpp (revision 4d8811742fa447ec05b4993a16a0931bc29aafab)
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 static status_t
53 mutex_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 
104 int
105 pthread_mutex_lock(pthread_mutex_t* mutex)
106 {
107 	return mutex_lock(mutex, B_INFINITE_TIMEOUT);
108 }
109 
110 
111 int
112 pthread_mutex_trylock(pthread_mutex_t* mutex)
113 {
114 	return mutex_lock(mutex, -1);
115 }
116 
117 
118 int
119 pthread_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 
140 int
141 pthread_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 
164 int
165 pthread_mutex_getprioceiling(const 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 
177 int
178 pthread_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