xref: /openbsd-src/lib/libc/thread/rthread_cond.c (revision c90a81c56dcebd6a1b73fe4aff9b03385b8e63b3)
1 /*	$OpenBSD: rthread_cond.c,v 1.4 2017/09/05 02:40:54 guenther 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 <assert.h>
20 #include <errno.h>
21 #include <pthread.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "rthread.h"
28 #include "cancel.h"
29 #include "synch.h"
30 
31 int
32 pthread_cond_init(pthread_cond_t *condp, const pthread_condattr_t *attr)
33 {
34 	pthread_cond_t cond;
35 
36 	cond = calloc(1, sizeof(*cond));
37 	if (cond == NULL)
38 		return (ENOMEM);
39 
40 	if (attr == NULL)
41 		cond->clock = CLOCK_REALTIME;
42 	else
43 		cond->clock = (*attr)->ca_clock;
44 	*condp = cond;
45 
46 	return (0);
47 }
48 DEF_STRONG(pthread_cond_init);
49 
50 int
51 pthread_cond_destroy(pthread_cond_t *condp)
52 {
53 	pthread_cond_t cond;
54 
55 	assert(condp != NULL);
56 	cond = *condp;
57 
58 	if (cond != NULL) {
59 		if (cond->mutex != NULL) {
60 #define MSG "pthread_cond_destroy on condvar with waiters!\n"
61 			write(2, MSG, sizeof(MSG) - 1);
62 #undef MSG
63 			return (EBUSY);
64 		}
65 		free(cond);
66 	}
67 	*condp = NULL;
68 
69 	return (0);
70 }
71 
72 int
73 _rthread_cond_timedwait(pthread_cond_t cond, pthread_mutex_t *mutexp,
74     const struct timespec *abs)
75 {
76 	struct pthread_mutex *mutex = (struct pthread_mutex *)*mutexp;
77 	struct tib *tib = TIB_GET();
78 	pthread_t self = tib->tib_thread;
79 	int error, rv = 0, canceled = 0, mutex_count = 0;
80 	clockid_t clock = cond->clock;
81 	int seq = cond->seq;
82 	PREP_CANCEL_POINT(tib);
83 
84 	_rthread_debug(5, "%p: cond_timed %p,%p (%p)\n", self,
85 	    (void *)cond, (void *)mutex, (void *)mutex->owner);
86 
87 	ENTER_DELAYED_CANCEL_POINT(tib, self);
88 
89 #if notyet
90 	/* mark the condvar as being associated with this mutex */
91 	if (cond->mutex == NULL)
92 		atomic_cas_ptr(&cond->mutex, NULL, mutex);
93 
94 	if (cond->mutex != mutex) {
95 		LEAVE_CANCEL_POINT_INNER(tib, 1);
96 		return (EINVAL);
97 	}
98 #endif
99 
100 	/* snag the count in case this is a recursive mutex */
101 	if (mutex->type == PTHREAD_MUTEX_RECURSIVE)
102 		mutex_count = mutex->count;
103 
104 	pthread_mutex_unlock(mutexp);
105 
106 	do {
107 		/* If ``seq'' wraps you deserve to lose a signal. */
108 		error = _twait(&cond->seq, seq, clock, abs);
109 		/*
110 		* If we took a normal signal (not from cancellation) then
111 		* we should just go back to sleep without changing state
112 		* (timeouts, etc).
113 		*/
114 	} while ((error == EINTR) &&
115 	   (tib->tib_canceled == 0 || (tib->tib_cantcancel & CANCEL_DISABLED)));
116 
117 	/* if timeout or canceled, make note of that */
118 	if (error == ETIMEDOUT)
119 		rv = ETIMEDOUT;
120 	else if (error == EINTR)
121 		canceled = 1;
122 
123 	pthread_mutex_lock(mutexp);
124 
125 	/* restore the mutex's count */
126 	if (mutex->type == PTHREAD_MUTEX_RECURSIVE)
127 		mutex->count = mutex_count;
128 
129 	LEAVE_CANCEL_POINT_INNER(tib, canceled);
130 
131 	return rv;
132 }
133 
134 int
135 pthread_cond_timedwait(pthread_cond_t *condp, pthread_mutex_t *mutexp,
136     const struct timespec *abs)
137 {
138 	pthread_cond_t cond;
139 	int error;
140 
141 	if (*condp == NULL) {
142 		if ((error = pthread_cond_init(condp, NULL)))
143 			return (error);
144 	}
145 
146 	cond = *condp;
147 	if (abs == NULL || abs->tv_sec < 0 || abs->tv_nsec < 0 ||
148 	    abs->tv_nsec >= 1000000000)
149 		return (EINVAL);
150 
151 	return (_rthread_cond_timedwait(cond, mutexp, abs));
152 }
153 
154 int
155 pthread_cond_wait(pthread_cond_t *condp, pthread_mutex_t *mutexp)
156 {
157 	pthread_cond_t cond;
158 	int error;
159 
160 	if (*condp == NULL) {
161 		if ((error = pthread_cond_init(condp, NULL)))
162 			return (error);
163 	}
164 
165 	cond = *condp;
166 	return (_rthread_cond_timedwait(cond, mutexp, NULL));
167 }
168 
169 int
170 pthread_cond_signal(pthread_cond_t *condp)
171 {
172 	pthread_cond_t cond;
173 	int count;
174 
175 	if (*condp == NULL)
176 		return (0);
177 
178 	cond = *condp;
179 
180 	atomic_inc_int(&cond->seq);
181 	count = _wake(&cond->seq, 1);
182 
183 	_rthread_debug(5, "%p: cond_signal %p, %d awaken\n", pthread_self(),
184 	    (void *)cond, count);
185 
186 	return (0);
187 }
188 
189 int
190 pthread_cond_broadcast(pthread_cond_t *condp)
191 {
192 	pthread_cond_t cond;
193 	int count;
194 
195 	if (*condp == NULL)
196 		return (0);
197 
198 	cond = *condp;
199 
200 	atomic_inc_int(&cond->seq);
201 #if notyet
202 	count = _requeue(&cond->seq, 1, INT_MAX, &cond->mutex->lock);
203 #else
204 	count = _wake(&cond->seq, INT_MAX);
205 #endif
206 
207 	_rthread_debug(5, "%p: cond_broadcast %p, %d awaken\n", pthread_self(),
208 	    (void *)cond, count);
209 
210 	return (0);
211 }
212