// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include #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); } }