1/*	$NetBSD: condition.c,v 1.2.6.1 2012/06/05 21:15:31 bouyer Exp $	*/
2
3/*
4 * Copyright (C) 2004, 2006, 2007  Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1998-2001  Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20/* Id: condition.c,v 1.23 2007/06/18 23:47:49 tbox Exp  */
21
22#include <config.h>
23
24#include <isc/condition.h>
25#include <isc/assertions.h>
26#include <isc/util.h>
27#include <isc/thread.h>
28#include <isc/time.h>
29
30#define LSIGNAL		0
31#define LBROADCAST	1
32
33isc_result_t
34isc_condition_init(isc_condition_t *cond) {
35	HANDLE h;
36
37	REQUIRE(cond != NULL);
38
39	cond->waiters = 0;
40	/*
41	 * This handle is shared across all threads
42	 */
43	h = CreateEvent(NULL, FALSE, FALSE, NULL);
44	if (h == NULL) {
45		/* XXX */
46		return (ISC_R_UNEXPECTED);
47	}
48	cond->events[LSIGNAL] = h;
49
50	/*
51	 * The threadlist will hold the actual events needed
52	 * for the wait condition
53	 */
54	ISC_LIST_INIT(cond->threadlist);
55
56	return (ISC_R_SUCCESS);
57}
58
59/*
60 * Add the thread to the threadlist along with the required events
61 */
62static isc_result_t
63register_thread(unsigned long thrd, isc_condition_t *gblcond,
64		isc_condition_thread_t **localcond)
65{
66	HANDLE hc;
67	isc_condition_thread_t *newthread;
68
69	REQUIRE(localcond != NULL && *localcond == NULL);
70
71	newthread = malloc(sizeof(isc_condition_thread_t));
72	if (newthread == NULL)
73		return (ISC_R_NOMEMORY);
74
75	/*
76	 * Create the thread-specific handle
77	 */
78	hc = CreateEvent(NULL, FALSE, FALSE, NULL);
79	if (hc == NULL) {
80		free(newthread);
81		return (ISC_R_UNEXPECTED);
82	}
83
84	/*
85	 * Add the thread ID and handles to list of threads for broadcast
86	 */
87	newthread->handle[LSIGNAL] = gblcond->events[LSIGNAL];
88	newthread->handle[LBROADCAST] = hc;
89	newthread->th = thrd;
90
91	/*
92	 * The thread is holding the manager lock so this is safe
93	 */
94	ISC_LIST_APPEND(gblcond->threadlist, newthread, link);
95	*localcond = newthread;
96	return (ISC_R_SUCCESS);
97}
98
99static isc_result_t
100find_thread_condition(unsigned long thrd, isc_condition_t *cond,
101		      isc_condition_thread_t **threadcondp)
102{
103	isc_condition_thread_t *threadcond;
104
105	REQUIRE(threadcondp != NULL && *threadcondp == NULL);
106
107	/*
108	 * Look for the thread ID.
109	 */
110	for (threadcond = ISC_LIST_HEAD(cond->threadlist);
111	     threadcond != NULL;
112	     threadcond = ISC_LIST_NEXT(threadcond, link)) {
113
114		if (threadcond->th == thrd) {
115			*threadcondp = threadcond;
116			return (ISC_R_SUCCESS);
117		}
118	}
119
120	/*
121	 * Not found, so add it.
122	 */
123	return (register_thread(thrd, cond, threadcondp));
124}
125
126isc_result_t
127isc_condition_signal(isc_condition_t *cond) {
128
129	/*
130	 * Unlike pthreads, the caller MUST hold the lock associated with
131	 * the condition variable when calling us.
132	 */
133	REQUIRE(cond != NULL);
134
135	if (!SetEvent(cond->events[LSIGNAL])) {
136		/* XXX */
137		return (ISC_R_UNEXPECTED);
138	}
139
140	return (ISC_R_SUCCESS);
141}
142
143isc_result_t
144isc_condition_broadcast(isc_condition_t *cond) {
145
146	isc_condition_thread_t *threadcond;
147	isc_boolean_t failed = ISC_FALSE;
148
149	/*
150	 * Unlike pthreads, the caller MUST hold the lock associated with
151	 * the condition variable when calling us.
152	 */
153	REQUIRE(cond != NULL);
154
155	/*
156	 * Notify every thread registered for this
157	 */
158	for (threadcond = ISC_LIST_HEAD(cond->threadlist);
159	     threadcond != NULL;
160	     threadcond = ISC_LIST_NEXT(threadcond, link)) {
161
162		if (!SetEvent(threadcond->handle[LBROADCAST]))
163			failed = ISC_TRUE;
164	}
165
166	if (failed)
167		return (ISC_R_UNEXPECTED);
168
169	return (ISC_R_SUCCESS);
170}
171
172isc_result_t
173isc_condition_destroy(isc_condition_t *cond) {
174
175	isc_condition_thread_t *next, *threadcond;
176
177	REQUIRE(cond != NULL);
178	REQUIRE(cond->waiters == 0);
179
180	(void)CloseHandle(cond->events[LSIGNAL]);
181
182	/*
183	 * Delete the threadlist
184	 */
185	threadcond = ISC_LIST_HEAD(cond->threadlist);
186
187	while (threadcond != NULL) {
188		next = ISC_LIST_NEXT(threadcond, link);
189		DEQUEUE(cond->threadlist, threadcond, link);
190		(void) CloseHandle(threadcond->handle[LBROADCAST]);
191		free(threadcond);
192		threadcond = next;
193	}
194
195	return (ISC_R_SUCCESS);
196}
197
198/*
199 * This is always called when the mutex (lock) is held, but because
200 * we are waiting we need to release it and reacquire it as soon as the wait
201 * is over. This allows other threads to make use of the object guarded
202 * by the mutex but it should never try to delete it as long as the
203 * number of waiters > 0. Always reacquire the mutex regardless of the
204 * result of the wait. Note that EnterCriticalSection will wait to acquire
205 * the mutex.
206 */
207static isc_result_t
208wait(isc_condition_t *cond, isc_mutex_t *mutex, DWORD milliseconds) {
209	DWORD result;
210	isc_result_t tresult;
211	isc_condition_thread_t *threadcond = NULL;
212
213	/*
214	 * Get the thread events needed for the wait
215	 */
216	tresult = find_thread_condition(isc_thread_self(), cond, &threadcond);
217	if (tresult !=  ISC_R_SUCCESS)
218		return (tresult);
219
220	cond->waiters++;
221	LeaveCriticalSection(mutex);
222	result = WaitForMultipleObjects(2, threadcond->handle, FALSE,
223					milliseconds);
224	EnterCriticalSection(mutex);
225	cond->waiters--;
226	if (result == WAIT_FAILED) {
227		/* XXX */
228		return (ISC_R_UNEXPECTED);
229	}
230	if (result == WAIT_TIMEOUT)
231		return (ISC_R_TIMEDOUT);
232
233	return (ISC_R_SUCCESS);
234}
235
236isc_result_t
237isc_condition_wait(isc_condition_t *cond, isc_mutex_t *mutex) {
238	return (wait(cond, mutex, INFINITE));
239}
240
241isc_result_t
242isc_condition_waituntil(isc_condition_t *cond, isc_mutex_t *mutex,
243			isc_time_t *t) {
244	DWORD milliseconds;
245	isc_uint64_t microseconds;
246	isc_time_t now;
247
248	if (isc_time_now(&now) != ISC_R_SUCCESS) {
249		/* XXX */
250		return (ISC_R_UNEXPECTED);
251	}
252
253	microseconds = isc_time_microdiff(t, &now);
254	if (microseconds > 0xFFFFFFFFi64 * 1000)
255		milliseconds = 0xFFFFFFFF;
256	else
257		milliseconds = (DWORD)(microseconds / 1000);
258
259	return (wait(cond, mutex, milliseconds));
260}
261