xref: /haiku/src/system/libroot/posix/pthread/pthread_cond.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
1 /*
2  * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2007, Ryan Leavengood, leavengood@gmail.com.
4  * All rights reserved. Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <pthread.h>
9 #include "pthread_private.h"
10 
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 #include <syscall_utils.h>
16 
17 #include <syscalls.h>
18 #include <user_mutex_defs.h>
19 
20 
21 #define COND_FLAG_SHARED	0x01
22 #define COND_FLAG_MONOTONIC	0x02
23 
24 
25 static const pthread_condattr pthread_condattr_default = {
26 	false,
27 	CLOCK_REALTIME
28 };
29 
30 
31 int
32 pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* _attr)
33 {
34 	const pthread_condattr* attr = _attr != NULL
35 		? *_attr : &pthread_condattr_default;
36 
37 	cond->flags = 0;
38 	if (attr->process_shared)
39 		cond->flags |= COND_FLAG_SHARED;
40 
41 	if (attr->clock_id == CLOCK_MONOTONIC)
42 		cond->flags |= COND_FLAG_MONOTONIC;
43 
44 	cond->mutex = NULL;
45 	cond->waiter_count = 0;
46 	cond->lock = 0;
47 
48 	return 0;
49 }
50 
51 
52 int
53 pthread_cond_destroy(pthread_cond_t* cond)
54 {
55 	return 0;
56 }
57 
58 
59 static status_t
60 cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex, uint32 flags,
61 	bigtime_t timeout)
62 {
63 	if (mutex->owner != find_thread(NULL)) {
64 		// calling thread isn't mutex owner
65 		return EPERM;
66 	}
67 
68 	if (cond->mutex != NULL && cond->mutex != mutex) {
69 		// condition variable already used with different mutex
70 		return EINVAL;
71 	}
72 
73 	cond->mutex = mutex;
74 	cond->waiter_count++;
75 
76 	// make sure the user mutex we use for blocking is locked
77 	atomic_test_and_set((int32*)&cond->lock, B_USER_MUTEX_LOCKED, 0);
78 
79 	// atomically unlock the mutex and start waiting on the user mutex
80 	mutex->owner = -1;
81 	mutex->owner_count = 0;
82 
83 	if ((cond->flags & COND_FLAG_SHARED) != 0)
84 		flags |= B_USER_MUTEX_SHARED;
85 	status_t status = _kern_mutex_switch_lock((int32*)&mutex->lock,
86 		((mutex->flags & MUTEX_FLAG_SHARED) ? B_USER_MUTEX_SHARED : 0),
87 		(int32*)&cond->lock, "pthread condition", flags, timeout);
88 
89 	if (status == B_INTERRUPTED) {
90 		// EINTR is not an allowed return value. We either have to restart
91 		// waiting -- which we can't atomically -- or return a spurious 0.
92 		status = 0;
93 	}
94 
95 	pthread_mutex_lock(mutex);
96 
97 	cond->waiter_count--;
98 
99 	// If there are no more waiters, we can change mutexes.
100 	if (cond->waiter_count == 0)
101 		cond->mutex = NULL;
102 
103 	return status;
104 }
105 
106 
107 static inline void
108 cond_signal(pthread_cond_t* cond, bool broadcast)
109 {
110 	if (cond->waiter_count == 0)
111 		return;
112 
113 	uint32 flags = 0;
114 	if (broadcast)
115 		flags |= B_USER_MUTEX_UNBLOCK_ALL;
116 	if ((cond->flags & COND_FLAG_SHARED) != 0)
117 		flags |= B_USER_MUTEX_SHARED;
118 
119 	// release the condition lock
120 	atomic_and((int32*)&cond->lock, ~(int32)B_USER_MUTEX_LOCKED);
121 	_kern_mutex_unblock((int32*)&cond->lock, flags);
122 }
123 
124 
125 int
126 pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* _mutex)
127 {
128 	RETURN_AND_TEST_CANCEL(cond_wait(cond, _mutex, 0, B_INFINITE_TIMEOUT));
129 }
130 
131 
132 int
133 pthread_cond_clockwait(pthread_cond_t* cond, pthread_mutex_t* mutex,
134 	clockid_t clock_id, const struct timespec* abstime)
135 {
136 	if (abstime == NULL || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000 * 1000 * 1000)
137 		RETURN_AND_TEST_CANCEL(EINVAL);
138 
139 	bigtime_t timeoutMicros = ((bigtime_t)abstime->tv_sec) * 1000000
140 		+ abstime->tv_nsec / 1000;
141 	uint32 flags = 0;
142 	switch (clock_id) {
143 		case CLOCK_REALTIME:
144 			flags = B_ABSOLUTE_REAL_TIME_TIMEOUT;
145 			break;
146 		case CLOCK_MONOTONIC :
147 			flags = B_ABSOLUTE_TIMEOUT;
148 			break;
149 		default:
150 			return B_BAD_VALUE;
151 	}
152 
153 	RETURN_AND_TEST_CANCEL(cond_wait(cond, mutex, flags, timeoutMicros));
154 }
155 
156 
157 int
158 pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex,
159 	const struct timespec* abstime)
160 {
161 	return pthread_cond_clockwait(cond, mutex,
162 		(cond->flags & COND_FLAG_MONOTONIC) != 0 ? CLOCK_MONOTONIC : CLOCK_REALTIME,
163 		abstime);
164 }
165 
166 
167 int
168 pthread_cond_broadcast(pthread_cond_t* cond)
169 {
170 	cond_signal(cond, true);
171 	return 0;
172 }
173 
174 
175 int
176 pthread_cond_signal(pthread_cond_t* cond)
177 {
178 	cond_signal(cond, false);
179 	return 0;
180 }
181