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
25static const pthread_condattr pthread_condattr_default = {
26	false,
27	CLOCK_REALTIME
28};
29
30
31int
32pthread_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
52int
53pthread_cond_destroy(pthread_cond_t* cond)
54{
55	return 0;
56}
57
58
59static status_t
60cond_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
107static inline void
108cond_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
125int
126pthread_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
132int
133pthread_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
157int
158pthread_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
167int
168pthread_cond_broadcast(pthread_cond_t* cond)
169{
170	cond_signal(cond, true);
171	return 0;
172}
173
174
175int
176pthread_cond_signal(pthread_cond_t* cond)
177{
178	cond_signal(cond, false);
179	return 0;
180}
181