adapters/condition_win32.c (115 lines of code) (raw):
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdlib.h>
#include "azure_c_shared_utility/condition.h"
#include "windows.h"
#include "azure_c_shared_utility/xlogging.h"
#include "azure_c_shared_utility/gballoc.h"
MU_DEFINE_ENUM_STRINGS(COND_RESULT, COND_RESULT_VALUES);
typedef struct CONDITION_TAG
{
volatile LONG waiting_thread_count;
HANDLE event_handle;
}
CONDITION;
COND_HANDLE Condition_Init(void)
{
// Codes_SRS_CONDITION_18_002: [ Condition_Init shall create and return a CONDITION_HANDLE ]
CONDITION* cond = (CONDITION*)malloc(sizeof(CONDITION));
// Codes_SRS_CONDITION_18_008: [ Condition_Init shall return NULL if it fails to allocate the CONDITION_HANDLE ]
if (cond != NULL)
{
(void)memset(cond, 0, sizeof(CONDITION));
cond->event_handle = CreateEvent(NULL, FALSE, FALSE, NULL);
if (cond->event_handle == NULL)
{
LogError("CreateEvent failed with error %d", GetLastError());
free(cond);
cond = NULL;
}
else
{
/* Needed to emulate pthread_signal as we only signal the event when there are waiting threads */
cond->waiting_thread_count = 0;
}
}
else
{
LogError("Failed to allocate condition handle");
}
return (COND_HANDLE)cond;
}
COND_RESULT Condition_Post(COND_HANDLE handle)
{
COND_RESULT result;
if (handle == NULL)
{
LogError("Null argument handle passed to Condition_Post");
// Codes_SRS_CONDITION_18_001: [ Condition_Post shall return COND_INVALID_ARG if handle is NULL ]
result = COND_INVALID_ARG;
}
else
{
CONDITION* cond = (CONDITION*)handle;
/* Emulate pthreads signalling, by only unblocking *one* waiting thread if there is one waiting */
if (cond->waiting_thread_count == 0 || SetEvent(cond->event_handle))
{
// Codes_SRS_CONDITION_18_003: [ Condition_Post shall return COND_OK if it succcessfully posts the condition ]
result = COND_OK;
}
else
{
LogError("Failed SetEvent call with error %d", GetLastError());
result = COND_ERROR;
}
}
return result;
}
COND_RESULT Condition_Wait(COND_HANDLE handle, LOCK_HANDLE lock, int timeout_milliseconds)
{
COND_RESULT result;
// Codes_SRS_CONDITION_18_004: [ Condition_Wait shall return COND_INVALID_ARG if handle is NULL ]
// Codes_SRS_CONDITION_18_005: [ Condition_Wait shall return COND_INVALID_ARG if lock is NULL and timeout_milliseconds is 0 ]
// Codes_SRS_CONDITION_18_006: [ Condition_Wait shall return COND_INVALID_ARG if lock is NULL and timeout_milliseconds is not 0 ]
if (handle == NULL || lock == NULL)
{
result = COND_INVALID_ARG;
}
else
{
CONDITION* cond = (CONDITION*)handle;
/* Increment the waiting thread count, unlock the lock and wait */
cond->waiting_thread_count++;
if (Unlock(lock) == LOCK_OK)
{
DWORD wait_result;
// Codes_SRS_CONDITION_18_013: [ Condition_Wait shall accept relative timeouts ]
wait_result = WaitForSingleObject(cond->event_handle, timeout_milliseconds == 0 ? INFINITE : timeout_milliseconds);
/* If we unlocked ok, it means the lock handle is valid, lock must succeed since it wraps EnterCriticalSection */
(void)Lock(lock);
if (wait_result != WAIT_OBJECT_0 && wait_result != WAIT_TIMEOUT)
{
LogError("Failed wait, wait returned with %x", wait_result);
/* cond might be freed at this point, just return error and do not touch condition */
result = COND_ERROR;
}
else
{
/* To handle the chance of a race condition reset the event again when there are no more waiting threads */
if (--cond->waiting_thread_count == 0)
{
(void)ResetEvent(cond->event_handle);
}
if (wait_result == WAIT_TIMEOUT)
{
// Codes_SRS_CONDITION_18_011: [ Condition_Wait shall return COND_TIMEOUT if the condition is NOT triggered and timeout_milliseconds is not 0 ]
result = COND_TIMEOUT;
}
else
{
// Codes_SRS_CONDITION_18_012: [ Condition_Wait shall return COND_OK if the condition is triggered and timeout_milliseconds is not 0 ]
result = COND_OK;
}
}
}
else
{
cond->waiting_thread_count--;
LogError("Invalid lock passed which failed to unlock");
result = COND_ERROR;
}
}
return result;
}
void Condition_Deinit(COND_HANDLE handle)
{
// Codes_SRS_CONDITION_18_007: [ Condition_Deinit will not fail if handle is NULL ]
// Codes_SRS_CONDITION_18_009: [ Condition_Deinit will deallocate handle if it is not NULL
if (handle != NULL)
{
CONDITION* cond = (CONDITION*)handle;
(void)CloseHandle(cond->event_handle);
cond->event_handle = INVALID_HANDLE_VALUE;
free(cond);
}
}