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