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