xref: /openbsd/lib/libc/thread/rthread_cond.c (revision 3cab2bb3)
1 /*	$OpenBSD: rthread_cond.c,v 1.5 2019/01/29 17:40:26 mpi 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 
30 int
31 pthread_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 }
47 DEF_STRONG(pthread_cond_init);
48 
49 int
50 pthread_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 
70 int
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 
132 int
133 pthread_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_sec < 0 || abs->tv_nsec < 0 ||
146 	    abs->tv_nsec >= 1000000000)
147 		return (EINVAL);
148 
149 	return (_rthread_cond_timedwait(cond, mutexp, abs));
150 }
151 
152 int
153 pthread_cond_wait(pthread_cond_t *condp, pthread_mutex_t *mutexp)
154 {
155 	pthread_cond_t cond;
156 	int error;
157 
158 	if (*condp == NULL) {
159 		if ((error = pthread_cond_init(condp, NULL)))
160 			return (error);
161 	}
162 
163 	cond = *condp;
164 	return (_rthread_cond_timedwait(cond, mutexp, NULL));
165 }
166 
167 int
168 pthread_cond_signal(pthread_cond_t *condp)
169 {
170 	pthread_cond_t cond;
171 	int count;
172 
173 	if (*condp == NULL)
174 		return (0);
175 
176 	cond = *condp;
177 
178 	atomic_inc_int(&cond->seq);
179 	count = _wake(&cond->seq, 1);
180 
181 	_rthread_debug(5, "%p: cond_signal %p, %d awaken\n", pthread_self(),
182 	    (void *)cond, count);
183 
184 	return (0);
185 }
186 
187 int
188 pthread_cond_broadcast(pthread_cond_t *condp)
189 {
190 	pthread_cond_t cond;
191 	int count;
192 
193 	if (*condp == NULL)
194 		return (0);
195 
196 	cond = *condp;
197 
198 	atomic_inc_int(&cond->seq);
199 #if notyet
200 	count = _requeue(&cond->seq, 1, INT_MAX, &cond->mutex->lock);
201 #else
202 	count = _wake(&cond->seq, INT_MAX);
203 #endif
204 
205 	_rthread_debug(5, "%p: cond_broadcast %p, %d awaken\n", pthread_self(),
206 	    (void *)cond, count);
207 
208 	return (0);
209 }
210