xref: /haiku/src/system/libroot/posix/pthread/pthread_cond.cpp (revision 344ded80d400028c8f561b4b876257b94c12db4a)
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 #include <time_private.h>
17 
18 #include <syscalls.h>
19 #include <user_mutex_defs.h>
20 
21 
22 #define COND_FLAG_SHARED	0x01
23 #define COND_FLAG_MONOTONIC	0x02
24 
25 
26 static const pthread_condattr pthread_condattr_default = {
27 	false,
28 	CLOCK_REALTIME
29 };
30 
31 
32 int
33 pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* _attr)
34 {
35 	const pthread_condattr* attr = _attr != NULL
36 		? *_attr : &pthread_condattr_default;
37 
38 	cond->flags = 0;
39 	if (attr->process_shared)
40 		cond->flags |= COND_FLAG_SHARED;
41 
42 	if (attr->clock_id == CLOCK_MONOTONIC)
43 		cond->flags |= COND_FLAG_MONOTONIC;
44 
45 	cond->mutex = NULL;
46 	cond->waiter_count = 0;
47 	cond->lock = 0;
48 
49 	return 0;
50 }
51 
52 
53 int
54 pthread_cond_destroy(pthread_cond_t* cond)
55 {
56 	return 0;
57 }
58 
59 
60 static status_t
61 cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex, uint32 flags,
62 	bigtime_t timeout)
63 {
64 	if (mutex->owner != find_thread(NULL)) {
65 		// calling thread isn't mutex owner
66 		return EPERM;
67 	}
68 
69 	if (cond->mutex != NULL && cond->mutex != mutex) {
70 		// condition variable already used with different mutex
71 		return EINVAL;
72 	}
73 
74 	cond->mutex = mutex;
75 	cond->waiter_count++;
76 
77 	// make sure the user mutex we use for blocking is locked
78 	atomic_test_and_set((int32*)&cond->lock, B_USER_MUTEX_LOCKED, 0);
79 
80 	// atomically unlock the mutex and start waiting on the user mutex
81 	mutex->owner = -1;
82 	mutex->owner_count = 0;
83 
84 	if ((cond->flags & COND_FLAG_SHARED) != 0)
85 		flags |= B_USER_MUTEX_SHARED;
86 	status_t status = _kern_mutex_switch_lock((int32*)&mutex->lock,
87 		((mutex->flags & MUTEX_FLAG_SHARED) ? B_USER_MUTEX_SHARED : 0),
88 		(int32*)&cond->lock, "pthread condition", flags, timeout);
89 
90 	if (status == B_INTERRUPTED) {
91 		// EINTR is not an allowed return value. We either have to restart
92 		// waiting -- which we can't atomically -- or return a spurious 0.
93 		status = 0;
94 	}
95 
96 	pthread_mutex_lock(mutex);
97 
98 	cond->waiter_count--;
99 
100 	// If there are no more waiters, we can change mutexes.
101 	if (cond->waiter_count == 0)
102 		cond->mutex = NULL;
103 
104 	return status;
105 }
106 
107 
108 static inline void
109 cond_signal(pthread_cond_t* cond, bool broadcast)
110 {
111 	if (cond->waiter_count == 0)
112 		return;
113 
114 	uint32 flags = 0;
115 	if (broadcast)
116 		flags |= B_USER_MUTEX_UNBLOCK_ALL;
117 	if ((cond->flags & COND_FLAG_SHARED) != 0)
118 		flags |= B_USER_MUTEX_SHARED;
119 
120 	// release the condition lock
121 	atomic_and((int32*)&cond->lock, ~(int32)B_USER_MUTEX_LOCKED);
122 	_kern_mutex_unblock((int32*)&cond->lock, flags);
123 }
124 
125 
126 int
127 pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* _mutex)
128 {
129 	RETURN_AND_TEST_CANCEL(cond_wait(cond, _mutex, 0, B_INFINITE_TIMEOUT));
130 }
131 
132 
133 int
134 pthread_cond_clockwait(pthread_cond_t* cond, pthread_mutex_t* mutex,
135 	clockid_t clock_id, const struct timespec* abstime)
136 {
137 	bigtime_t timeoutMicros;
138 	if (abstime == NULL || !timespec_to_bigtime(*abstime, timeoutMicros))
139 		RETURN_AND_TEST_CANCEL(EINVAL);
140 
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