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