1290001Sglebius/*
2290001Sglebius * Copyright (C) 2004, 2006, 2007  Internet Systems Consortium, Inc. ("ISC")
3290001Sglebius * Copyright (C) 1998-2001  Internet Software Consortium.
4290001Sglebius *
5290001Sglebius * Permission to use, copy, modify, and/or distribute this software for any
6290001Sglebius * purpose with or without fee is hereby granted, provided that the above
7290001Sglebius * copyright notice and this permission notice appear in all copies.
8290001Sglebius *
9290001Sglebius * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10290001Sglebius * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11290001Sglebius * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12290001Sglebius * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13290001Sglebius * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14290001Sglebius * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15290001Sglebius * PERFORMANCE OF THIS SOFTWARE.
16290001Sglebius */
17290001Sglebius
18290001Sglebius/* $Id: condition.c,v 1.23 2007/06/18 23:47:49 tbox Exp $ */
19290001Sglebius
20290001Sglebius#include <config.h>
21290001Sglebius
22290001Sglebius#include <isc/condition.h>
23290001Sglebius#include <isc/assertions.h>
24290001Sglebius#include <isc/util.h>
25290001Sglebius#include <isc/thread.h>
26290001Sglebius#include <isc/time.h>
27290001Sglebius
28290001Sglebius#define LSIGNAL		0
29290001Sglebius#define LBROADCAST	1
30290001Sglebius
31290001Sglebiusisc_result_t
32290001Sglebiusisc_condition_init(isc_condition_t *cond) {
33290001Sglebius	HANDLE h;
34290001Sglebius
35290001Sglebius	REQUIRE(cond != NULL);
36290001Sglebius
37290001Sglebius	cond->waiters = 0;
38290001Sglebius	/*
39290001Sglebius	 * This handle is shared across all threads
40290001Sglebius	 */
41290001Sglebius	h = CreateEvent(NULL, FALSE, FALSE, NULL);
42290001Sglebius	if (h == NULL) {
43290001Sglebius		/* XXX */
44290001Sglebius		return (ISC_R_UNEXPECTED);
45290001Sglebius	}
46290001Sglebius	cond->events[LSIGNAL] = h;
47290001Sglebius
48290001Sglebius	/*
49290001Sglebius	 * The threadlist will hold the actual events needed
50290001Sglebius	 * for the wait condition
51290001Sglebius	 */
52290001Sglebius	ISC_LIST_INIT(cond->threadlist);
53290001Sglebius
54290001Sglebius	return (ISC_R_SUCCESS);
55290001Sglebius}
56290001Sglebius
57290001Sglebius/*
58290001Sglebius * Add the thread to the threadlist along with the required events
59290001Sglebius */
60290001Sglebiusstatic isc_result_t
61290001Sglebiusregister_thread(unsigned long thrd, isc_condition_t *gblcond,
62290001Sglebius		isc_condition_thread_t **localcond)
63290001Sglebius{
64290001Sglebius	HANDLE hc;
65290001Sglebius	isc_condition_thread_t *newthread;
66290001Sglebius
67290001Sglebius	REQUIRE(localcond != NULL && *localcond == NULL);
68290001Sglebius
69290001Sglebius	newthread = malloc(sizeof(isc_condition_thread_t));
70290001Sglebius	if (newthread == NULL)
71290001Sglebius		return (ISC_R_NOMEMORY);
72290001Sglebius
73290001Sglebius	/*
74290001Sglebius	 * Create the thread-specific handle
75290001Sglebius	 */
76290001Sglebius	hc = CreateEvent(NULL, FALSE, FALSE, NULL);
77290001Sglebius	if (hc == NULL) {
78290001Sglebius		free(newthread);
79290001Sglebius		return (ISC_R_UNEXPECTED);
80290001Sglebius	}
81290001Sglebius
82290001Sglebius	/*
83290001Sglebius	 * Add the thread ID and handles to list of threads for broadcast
84290001Sglebius	 */
85290001Sglebius	newthread->handle[LSIGNAL] = gblcond->events[LSIGNAL];
86290001Sglebius	newthread->handle[LBROADCAST] = hc;
87290001Sglebius	newthread->th = thrd;
88290001Sglebius
89290001Sglebius	/*
90290001Sglebius	 * The thread is holding the manager lock so this is safe
91290001Sglebius	 */
92290001Sglebius	ISC_LIST_APPEND(gblcond->threadlist, newthread, link);
93290001Sglebius	*localcond = newthread;
94290001Sglebius	return (ISC_R_SUCCESS);
95290001Sglebius}
96290001Sglebius
97290001Sglebiusstatic isc_result_t
98290001Sglebiusfind_thread_condition(unsigned long thrd, isc_condition_t *cond,
99290001Sglebius		      isc_condition_thread_t **threadcondp)
100290001Sglebius{
101290001Sglebius	isc_condition_thread_t *threadcond;
102290001Sglebius
103290001Sglebius	REQUIRE(threadcondp != NULL && *threadcondp == NULL);
104290001Sglebius
105290001Sglebius	/*
106290001Sglebius	 * Look for the thread ID.
107290001Sglebius	 */
108290001Sglebius	for (threadcond = ISC_LIST_HEAD(cond->threadlist);
109290001Sglebius	     threadcond != NULL;
110290001Sglebius	     threadcond = ISC_LIST_NEXT(threadcond, link)) {
111290001Sglebius
112290001Sglebius		if (threadcond->th == thrd) {
113290001Sglebius			*threadcondp = threadcond;
114290001Sglebius			return (ISC_R_SUCCESS);
115290001Sglebius		}
116290001Sglebius	}
117290001Sglebius
118290001Sglebius	/*
119290001Sglebius	 * Not found, so add it.
120290001Sglebius	 */
121290001Sglebius	return (register_thread(thrd, cond, threadcondp));
122290001Sglebius}
123290001Sglebius
124290001Sglebiusisc_result_t
125290001Sglebiusisc_condition_signal(isc_condition_t *cond) {
126290001Sglebius
127290001Sglebius	/*
128290001Sglebius	 * Unlike pthreads, the caller MUST hold the lock associated with
129290001Sglebius	 * the condition variable when calling us.
130290001Sglebius	 */
131290001Sglebius	REQUIRE(cond != NULL);
132290001Sglebius
133290001Sglebius	if (!SetEvent(cond->events[LSIGNAL])) {
134290001Sglebius		/* XXX */
135290001Sglebius		return (ISC_R_UNEXPECTED);
136290001Sglebius	}
137290001Sglebius
138290001Sglebius	return (ISC_R_SUCCESS);
139290001Sglebius}
140290001Sglebius
141290001Sglebiusisc_result_t
142290001Sglebiusisc_condition_broadcast(isc_condition_t *cond) {
143290001Sglebius
144290001Sglebius	isc_condition_thread_t *threadcond;
145290001Sglebius	isc_boolean_t failed = ISC_FALSE;
146290001Sglebius
147290001Sglebius	/*
148290001Sglebius	 * Unlike pthreads, the caller MUST hold the lock associated with
149290001Sglebius	 * the condition variable when calling us.
150290001Sglebius	 */
151290001Sglebius	REQUIRE(cond != NULL);
152290001Sglebius
153290001Sglebius	/*
154290001Sglebius	 * Notify every thread registered for this
155290001Sglebius	 */
156290001Sglebius	for (threadcond = ISC_LIST_HEAD(cond->threadlist);
157290001Sglebius	     threadcond != NULL;
158290001Sglebius	     threadcond = ISC_LIST_NEXT(threadcond, link)) {
159290001Sglebius
160290001Sglebius		if (!SetEvent(threadcond->handle[LBROADCAST]))
161290001Sglebius			failed = ISC_TRUE;
162290001Sglebius	}
163290001Sglebius
164290001Sglebius	if (failed)
165290001Sglebius		return (ISC_R_UNEXPECTED);
166290001Sglebius
167290001Sglebius	return (ISC_R_SUCCESS);
168290001Sglebius}
169290001Sglebius
170290001Sglebiusisc_result_t
171290001Sglebiusisc_condition_destroy(isc_condition_t *cond) {
172290001Sglebius
173290001Sglebius	isc_condition_thread_t *next, *threadcond;
174290001Sglebius
175290001Sglebius	REQUIRE(cond != NULL);
176290001Sglebius	REQUIRE(cond->waiters == 0);
177290001Sglebius
178290001Sglebius	(void)CloseHandle(cond->events[LSIGNAL]);
179290001Sglebius
180290001Sglebius	/*
181290001Sglebius	 * Delete the threadlist
182290001Sglebius	 */
183290001Sglebius	threadcond = ISC_LIST_HEAD(cond->threadlist);
184290001Sglebius
185290001Sglebius	while (threadcond != NULL) {
186290001Sglebius		next = ISC_LIST_NEXT(threadcond, link);
187290001Sglebius		DEQUEUE(cond->threadlist, threadcond, link);
188290001Sglebius		(void) CloseHandle(threadcond->handle[LBROADCAST]);
189290001Sglebius		free(threadcond);
190290001Sglebius		threadcond = next;
191290001Sglebius	}
192290001Sglebius
193290001Sglebius	return (ISC_R_SUCCESS);
194290001Sglebius}
195290001Sglebius
196290001Sglebius/*
197290001Sglebius * This is always called when the mutex (lock) is held, but because
198290001Sglebius * we are waiting we need to release it and reacquire it as soon as the wait
199290001Sglebius * is over. This allows other threads to make use of the object guarded
200290001Sglebius * by the mutex but it should never try to delete it as long as the
201290001Sglebius * number of waiters > 0. Always reacquire the mutex regardless of the
202290001Sglebius * result of the wait. Note that EnterCriticalSection will wait to acquire
203290001Sglebius * the mutex.
204290001Sglebius */
205290001Sglebiusstatic isc_result_t
206290001Sglebiuswait(isc_condition_t *cond, isc_mutex_t *mutex, DWORD milliseconds) {
207290001Sglebius	DWORD result;
208290001Sglebius	isc_result_t tresult;
209290001Sglebius	isc_condition_thread_t *threadcond = NULL;
210290001Sglebius
211290001Sglebius	/*
212290001Sglebius	 * Get the thread events needed for the wait
213290001Sglebius	 */
214290001Sglebius	tresult = find_thread_condition(isc_thread_self(), cond, &threadcond);
215290001Sglebius	if (tresult !=  ISC_R_SUCCESS)
216290001Sglebius		return (tresult);
217290001Sglebius
218290001Sglebius	cond->waiters++;
219290001Sglebius	LeaveCriticalSection(mutex);
220290001Sglebius	result = WaitForMultipleObjects(2, threadcond->handle, FALSE,
221290001Sglebius					milliseconds);
222290001Sglebius	EnterCriticalSection(mutex);
223290001Sglebius	cond->waiters--;
224290001Sglebius	if (result == WAIT_FAILED) {
225290001Sglebius		/* XXX */
226290001Sglebius		return (ISC_R_UNEXPECTED);
227290001Sglebius	}
228290001Sglebius	if (result == WAIT_TIMEOUT)
229290001Sglebius		return (ISC_R_TIMEDOUT);
230290001Sglebius
231290001Sglebius	return (ISC_R_SUCCESS);
232290001Sglebius}
233290001Sglebius
234290001Sglebiusisc_result_t
235290001Sglebiusisc_condition_wait(isc_condition_t *cond, isc_mutex_t *mutex) {
236290001Sglebius	return (wait(cond, mutex, INFINITE));
237290001Sglebius}
238290001Sglebius
239290001Sglebiusisc_result_t
240290001Sglebiusisc_condition_waituntil(isc_condition_t *cond, isc_mutex_t *mutex,
241290001Sglebius			isc_time_t *t) {
242290001Sglebius	DWORD milliseconds;
243290001Sglebius	isc_uint64_t microseconds;
244290001Sglebius	isc_time_t now;
245290001Sglebius
246290001Sglebius	if (isc_time_now(&now) != ISC_R_SUCCESS) {
247290001Sglebius		/* XXX */
248290001Sglebius		return (ISC_R_UNEXPECTED);
249290001Sglebius	}
250290001Sglebius
251290001Sglebius	microseconds = isc_time_microdiff(t, &now);
252290001Sglebius	if (microseconds > 0xFFFFFFFFi64 * 1000)
253290001Sglebius		milliseconds = 0xFFFFFFFF;
254290001Sglebius	else
255290001Sglebius		milliseconds = (DWORD)(microseconds / 1000);
256290001Sglebius
257290001Sglebius	return (wait(cond, mutex, milliseconds));
258290001Sglebius}
259