1 /*	$NetBSD: condition.c,v 1.1.1.1 2009/12/13 16:54:46 kardel 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 
33 isc_result_t
34 isc_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  */
62 static isc_result_t
63 register_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 
99 static isc_result_t
100 find_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 
126 isc_result_t
127 isc_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 
143 isc_result_t
144 isc_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 
172 isc_result_t
173 isc_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  */
207 static isc_result_t
208 wait(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 
236 isc_result_t
237 isc_condition_wait(isc_condition_t *cond, isc_mutex_t *mutex) {
238 	return (wait(cond, mutex, INFINITE));
239 }
240 
241 isc_result_t
242 isc_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