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
24static const pthread_condattr pthread_condattr_default = {
25	false
26};
27
28
29int
30pthread_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
47int
48pthread_cond_destroy(pthread_cond_t* cond)
49{
50	return 0;
51}
52
53
54static status_t
55cond_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
99static inline void
100cond_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
111int
112pthread_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
118int
119pthread_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
130int
131pthread_cond_broadcast(pthread_cond_t* cond)
132{
133	cond_signal(cond, true);
134	return 0;
135}
136
137
138int
139pthread_cond_signal(pthread_cond_t* cond)
140{
141	cond_signal(cond, false);
142	return 0;
143}
144