1/*	$OpenBSD: rthread_cond.c,v 1.6 2024/01/10 04:28:43 cheloha Exp $ */
2/*
3 * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org>
4 * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <errno.h>
20#include <pthread.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26#include "rthread.h"
27#include "cancel.h"
28#include "synch.h"
29
30int
31pthread_cond_init(pthread_cond_t *condp, const pthread_condattr_t *attr)
32{
33	pthread_cond_t cond;
34
35	cond = calloc(1, sizeof(*cond));
36	if (cond == NULL)
37		return (ENOMEM);
38
39	if (attr == NULL)
40		cond->clock = CLOCK_REALTIME;
41	else
42		cond->clock = (*attr)->ca_clock;
43	*condp = cond;
44
45	return (0);
46}
47DEF_STRONG(pthread_cond_init);
48
49int
50pthread_cond_destroy(pthread_cond_t *condp)
51{
52	pthread_cond_t cond;
53
54	cond = *condp;
55
56	if (cond != NULL) {
57		if (cond->mutex != NULL) {
58#define MSG "pthread_cond_destroy on condvar with waiters!\n"
59			write(2, MSG, sizeof(MSG) - 1);
60#undef MSG
61			return (EBUSY);
62		}
63		free(cond);
64	}
65	*condp = NULL;
66
67	return (0);
68}
69
70int
71_rthread_cond_timedwait(pthread_cond_t cond, pthread_mutex_t *mutexp,
72    const struct timespec *abs)
73{
74	struct pthread_mutex *mutex = (struct pthread_mutex *)*mutexp;
75	struct tib *tib = TIB_GET();
76	pthread_t self = tib->tib_thread;
77	int error, rv = 0, canceled = 0, mutex_count = 0;
78	clockid_t clock = cond->clock;
79	int seq = cond->seq;
80	PREP_CANCEL_POINT(tib);
81
82	_rthread_debug(5, "%p: cond_timed %p,%p (%p)\n", self,
83	    (void *)cond, (void *)mutex, (void *)mutex->owner);
84
85	ENTER_DELAYED_CANCEL_POINT(tib, self);
86
87#if notyet
88	/* mark the condvar as being associated with this mutex */
89	if (cond->mutex == NULL)
90		atomic_cas_ptr(&cond->mutex, NULL, mutex);
91
92	if (cond->mutex != mutex) {
93		LEAVE_CANCEL_POINT_INNER(tib, 1);
94		return (EINVAL);
95	}
96#endif
97
98	/* snag the count in case this is a recursive mutex */
99	if (mutex->type == PTHREAD_MUTEX_RECURSIVE)
100		mutex_count = mutex->count;
101
102	pthread_mutex_unlock(mutexp);
103
104	do {
105		/* If ``seq'' wraps you deserve to lose a signal. */
106		error = _twait(&cond->seq, seq, clock, abs);
107		/*
108		* If we took a normal signal (not from cancellation) then
109		* we should just go back to sleep without changing state
110		* (timeouts, etc).
111		*/
112	} while ((error == EINTR) &&
113	   (tib->tib_canceled == 0 || (tib->tib_cantcancel & CANCEL_DISABLED)));
114
115	/* if timeout or canceled, make note of that */
116	if (error == ETIMEDOUT)
117		rv = ETIMEDOUT;
118	else if (error == EINTR)
119		canceled = 1;
120
121	pthread_mutex_lock(mutexp);
122
123	/* restore the mutex's count */
124	if (mutex->type == PTHREAD_MUTEX_RECURSIVE)
125		mutex->count = mutex_count;
126
127	LEAVE_CANCEL_POINT_INNER(tib, canceled);
128
129	return rv;
130}
131
132int
133pthread_cond_timedwait(pthread_cond_t *condp, pthread_mutex_t *mutexp,
134    const struct timespec *abs)
135{
136	pthread_cond_t cond;
137	int error;
138
139	if (*condp == NULL) {
140		if ((error = pthread_cond_init(condp, NULL)))
141			return (error);
142	}
143
144	cond = *condp;
145	if (abs == NULL || abs->tv_nsec < 0 || abs->tv_nsec >= 1000000000)
146		return (EINVAL);
147
148	return (_rthread_cond_timedwait(cond, mutexp, abs));
149}
150
151int
152pthread_cond_wait(pthread_cond_t *condp, pthread_mutex_t *mutexp)
153{
154	pthread_cond_t cond;
155	int error;
156
157	if (*condp == NULL) {
158		if ((error = pthread_cond_init(condp, NULL)))
159			return (error);
160	}
161
162	cond = *condp;
163	return (_rthread_cond_timedwait(cond, mutexp, NULL));
164}
165
166int
167pthread_cond_signal(pthread_cond_t *condp)
168{
169	pthread_cond_t cond;
170	int count;
171
172	if (*condp == NULL)
173		return (0);
174
175	cond = *condp;
176
177	atomic_inc_int(&cond->seq);
178	count = _wake(&cond->seq, 1);
179
180	_rthread_debug(5, "%p: cond_signal %p, %d awaken\n", pthread_self(),
181	    (void *)cond, count);
182
183	return (0);
184}
185
186int
187pthread_cond_broadcast(pthread_cond_t *condp)
188{
189	pthread_cond_t cond;
190	int count;
191
192	if (*condp == NULL)
193		return (0);
194
195	cond = *condp;
196
197	atomic_inc_int(&cond->seq);
198#if notyet
199	count = _requeue(&cond->seq, 1, INT_MAX, &cond->mutex->lock);
200#else
201	count = _wake(&cond->seq, INT_MAX);
202#endif
203
204	_rthread_debug(5, "%p: cond_broadcast %p, %d awaken\n", pthread_self(),
205	    (void *)cond, count);
206
207	return (0);
208}
209