#include <config.h>
#include <isc/condition.h>
#include <isc/assertions.h>
#include <isc/util.h>
#include <isc/thread.h>
#include <isc/time.h>
#define LSIGNAL 0
#define LBROADCAST 1
isc_result_t
isc_condition_init(isc_condition_t *cond) {
HANDLE h;
REQUIRE(cond != NULL);
cond->waiters = 0;
h = CreateEvent(NULL, FALSE, FALSE, NULL);
if (h == NULL) {
return (ISC_R_UNEXPECTED);
}
cond->events[LSIGNAL] = h;
ISC_LIST_INIT(cond->threadlist);
return (ISC_R_SUCCESS);
}
static isc_result_t
register_thread(unsigned long thrd, isc_condition_t *gblcond,
isc_condition_thread_t **localcond)
{
HANDLE hc;
isc_condition_thread_t *newthread;
REQUIRE(localcond != NULL && *localcond == NULL);
newthread = malloc(sizeof(isc_condition_thread_t));
if (newthread == NULL)
return (ISC_R_NOMEMORY);
hc = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hc == NULL) {
free(newthread);
return (ISC_R_UNEXPECTED);
}
newthread->handle[LSIGNAL] = gblcond->events[LSIGNAL];
newthread->handle[LBROADCAST] = hc;
newthread->th = thrd;
ISC_LIST_APPEND(gblcond->threadlist, newthread, link);
*localcond = newthread;
return (ISC_R_SUCCESS);
}
static isc_result_t
find_thread_condition(unsigned long thrd, isc_condition_t *cond,
isc_condition_thread_t **threadcondp)
{
isc_condition_thread_t *threadcond;
REQUIRE(threadcondp != NULL && *threadcondp == NULL);
for (threadcond = ISC_LIST_HEAD(cond->threadlist);
threadcond != NULL;
threadcond = ISC_LIST_NEXT(threadcond, link)) {
if (threadcond->th == thrd) {
*threadcondp = threadcond;
return (ISC_R_SUCCESS);
}
}
return (register_thread(thrd, cond, threadcondp));
}
isc_result_t
isc_condition_signal(isc_condition_t *cond) {
REQUIRE(cond != NULL);
if (!SetEvent(cond->events[LSIGNAL])) {
return (ISC_R_UNEXPECTED);
}
return (ISC_R_SUCCESS);
}
isc_result_t
isc_condition_broadcast(isc_condition_t *cond) {
isc_condition_thread_t *threadcond;
isc_boolean_t failed = ISC_FALSE;
REQUIRE(cond != NULL);
for (threadcond = ISC_LIST_HEAD(cond->threadlist);
threadcond != NULL;
threadcond = ISC_LIST_NEXT(threadcond, link)) {
if (!SetEvent(threadcond->handle[LBROADCAST]))
failed = ISC_TRUE;
}
if (failed)
return (ISC_R_UNEXPECTED);
return (ISC_R_SUCCESS);
}
isc_result_t
isc_condition_destroy(isc_condition_t *cond) {
isc_condition_thread_t *next, *threadcond;
REQUIRE(cond != NULL);
REQUIRE(cond->waiters == 0);
(void)CloseHandle(cond->events[LSIGNAL]);
threadcond = ISC_LIST_HEAD(cond->threadlist);
while (threadcond != NULL) {
next = ISC_LIST_NEXT(threadcond, link);
DEQUEUE(cond->threadlist, threadcond, link);
(void) CloseHandle(threadcond->handle[LBROADCAST]);
free(threadcond);
threadcond = next;
}
return (ISC_R_SUCCESS);
}
static isc_result_t
wait(isc_condition_t *cond, isc_mutex_t *mutex, DWORD milliseconds) {
DWORD result;
isc_result_t tresult;
isc_condition_thread_t *threadcond = NULL;
tresult = find_thread_condition(isc_thread_self(), cond, &threadcond);
if (tresult != ISC_R_SUCCESS)
return (tresult);
cond->waiters++;
LeaveCriticalSection(mutex);
result = WaitForMultipleObjects(2, threadcond->handle, FALSE,
milliseconds);
EnterCriticalSection(mutex);
cond->waiters--;
if (result == WAIT_FAILED) {
return (ISC_R_UNEXPECTED);
}
if (result == WAIT_TIMEOUT)
return (ISC_R_TIMEDOUT);
return (ISC_R_SUCCESS);
}
isc_result_t
isc_condition_wait(isc_condition_t *cond, isc_mutex_t *mutex) {
return (wait(cond, mutex, INFINITE));
}
isc_result_t
isc_condition_waituntil(isc_condition_t *cond, isc_mutex_t *mutex,
isc_time_t *t) {
DWORD milliseconds;
isc_uint64_t microseconds;
isc_time_t now;
if (isc_time_now(&now) != ISC_R_SUCCESS) {
return (ISC_R_UNEXPECTED);
}
microseconds = isc_time_microdiff(t, &now);
if (microseconds > 0xFFFFFFFFi64 * 1000)
milliseconds = 0xFFFFFFFF;
else
milliseconds = (DWORD)(microseconds / 1000);
return (wait(cond, mutex, milliseconds));
}