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