xref: /haiku/src/system/libroot/posix/pthread/pthread_cond.cpp (revision b247f935d133a42c427cad8a759a1bf2f65bc290)
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, bigtime_t timeout)
61 {
62 	if (mutex->owner != find_thread(NULL)) {
63 		// calling thread isn't mutex owner
64 		return EPERM;
65 	}
66 
67 	if (cond->mutex != NULL && cond->mutex != mutex) {
68 		// condition variable already used with different mutex
69 		return EINVAL;
70 	}
71 
72 	cond->mutex = mutex;
73 	cond->waiter_count++;
74 
75 	// make sure the user mutex we use for blocking is locked
76 	atomic_or((int32*)&cond->lock, B_USER_MUTEX_LOCKED);
77 
78 	// atomically unlock the mutex and start waiting on the user mutex
79 	mutex->owner = -1;
80 	mutex->owner_count = 0;
81 
82 	int32 flags = (cond->flags & COND_FLAG_MONOTONIC) != 0 ? B_ABSOLUTE_TIMEOUT
83 		: B_ABSOLUTE_REAL_TIME_TIMEOUT;
84 
85 	status_t status = _kern_mutex_switch_lock((int32*)&mutex->lock,
86 		(int32*)&cond->lock, "pthread condition",
87 		timeout == B_INFINITE_TIMEOUT ? 0 : 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 	// If there are no more waiters, we can change mutexes.
99 	if (cond->waiter_count == 0)
100 		cond->mutex = NULL;
101 
102 	return status;
103 }
104 
105 
106 static inline void
107 cond_signal(pthread_cond_t* cond, bool broadcast)
108 {
109 	if (cond->waiter_count == 0)
110 		return;
111 
112 	// release the condition lock
113 	_kern_mutex_unlock((int32*)&cond->lock,
114 		broadcast ? B_USER_MUTEX_UNBLOCK_ALL : 0);
115 }
116 
117 
118 int
119 pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* _mutex)
120 {
121 	RETURN_AND_TEST_CANCEL(cond_wait(cond, _mutex, B_INFINITE_TIMEOUT));
122 }
123 
124 
125 int
126 pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex,
127 	const struct timespec* tv)
128 {
129 	if (tv == NULL || tv->tv_nsec < 0 || tv->tv_nsec >= 1000 * 1000 * 1000)
130 		RETURN_AND_TEST_CANCEL(EINVAL);
131 
132 	RETURN_AND_TEST_CANCEL(
133 		cond_wait(cond, mutex, tv->tv_sec * 1000000LL + tv->tv_nsec / 1000LL));
134 }
135 
136 
137 int
138 pthread_cond_broadcast(pthread_cond_t* cond)
139 {
140 	cond_signal(cond, true);
141 	return 0;
142 }
143 
144 
145 int
146 pthread_cond_signal(pthread_cond_t* cond)
147 {
148 	cond_signal(cond, false);
149 	return 0;
150 }
151