xref: /haiku/src/system/libroot/posix/pthread/pthread_cond.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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_or((int32*)&cond->lock, B_USER_MUTEX_LOCKED);
78 
79 	// atomically unlock the mutex and start waiting on the user mutex
80 	mutex->owner = -1;
81 	mutex->owner_count = 0;
82 
83 	status_t status = _kern_mutex_switch_lock((int32*)&mutex->lock,
84 		(int32*)&cond->lock, "pthread condition", flags, timeout);
85 
86 	if (status == B_INTERRUPTED) {
87 		// EINTR is not an allowed return value. We either have to restart
88 		// waiting -- which we can't atomically -- or return a spurious 0.
89 		status = 0;
90 	}
91 
92 	pthread_mutex_lock(mutex);
93 
94 	cond->waiter_count--;
95 	// If there are no more waiters, we can change mutexes.
96 	if (cond->waiter_count == 0)
97 		cond->mutex = NULL;
98 
99 	return status;
100 }
101 
102 
103 static inline void
104 cond_signal(pthread_cond_t* cond, bool broadcast)
105 {
106 	if (cond->waiter_count == 0)
107 		return;
108 
109 	// release the condition lock
110 	_kern_mutex_unlock((int32*)&cond->lock,
111 		broadcast ? B_USER_MUTEX_UNBLOCK_ALL : 0);
112 }
113 
114 
115 int
116 pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* _mutex)
117 {
118 	RETURN_AND_TEST_CANCEL(cond_wait(cond, _mutex, 0, B_INFINITE_TIMEOUT));
119 }
120 
121 
122 int
123 pthread_cond_clockwait(pthread_cond_t* cond, pthread_mutex_t* mutex,
124 	clockid_t clock_id, const struct timespec* abstime)
125 {
126 	if (abstime == NULL || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000 * 1000 * 1000)
127 		RETURN_AND_TEST_CANCEL(EINVAL);
128 
129 	bigtime_t timeoutMicros = ((bigtime_t)abstime->tv_sec) * 1000000
130 		+ abstime->tv_nsec / 1000;
131 	uint32 flags = 0;
132 	switch (clock_id) {
133 		case CLOCK_REALTIME:
134 			flags = B_ABSOLUTE_REAL_TIME_TIMEOUT;
135 			break;
136 		case CLOCK_MONOTONIC :
137 			flags = B_ABSOLUTE_TIMEOUT;
138 			break;
139 		default:
140 			return B_BAD_VALUE;
141 	}
142 
143 	RETURN_AND_TEST_CANCEL(cond_wait(cond, mutex, flags, timeoutMicros));
144 }
145 
146 
147 int
148 pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex,
149 	const struct timespec* abstime)
150 {
151 	return pthread_cond_clockwait(cond, mutex,
152 		(cond->flags & COND_FLAG_MONOTONIC) != 0 ? CLOCK_MONOTONIC : CLOCK_REALTIME,
153 		abstime);
154 }
155 
156 
157 int
158 pthread_cond_broadcast(pthread_cond_t* cond)
159 {
160 	cond_signal(cond, true);
161 	return 0;
162 }
163 
164 
165 int
166 pthread_cond_signal(pthread_cond_t* cond)
167 {
168 	cond_signal(cond, false);
169 	return 0;
170 }
171