src/clds_hazard_pointers_thread_helper.c (157 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 "windows.h" // Thread Local Storage doesn't have a PAL yet
#include "macro_utils/macro_utils.h"
#include "c_logging/logger.h"
#include "c_pal/gballoc_hl.h"
#include "c_pal/gballoc_hl_redirect.h"
#include "c_pal/log_critical_and_terminate.h"
#include "c_util/thread_notifications_dispatcher.h"
#include "thread_notifications_lackey_dll/thread_notifications_lackey_dll.h"
#include "clds/clds_hazard_pointers.h"
#include "clds/clds_hazard_pointers_thread_helper.h"
typedef struct CLDS_HAZARD_POINTERS_THREAD_HELPER_TAG
{
CLDS_HAZARD_POINTERS_HANDLE hazard_pointers;
DWORD tls_slot;
TCALL_DISPATCHER_TARGET_HANDLE(THREAD_NOTIFICATION_CALL) thread_notification_target_handle;
TCALL_DISPATCHER(THREAD_NOTIFICATION_CALL) call_dispatcher;
} CLDS_HAZARD_POINTERS_THREAD_HELPER;
static void clds_hazard_pointers_thread_helper_thread_notification(void* context, THREAD_NOTIFICATIONS_LACKEY_DLL_REASON reason)
{
if (context == NULL)
{
/* Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_005: [ If context is NULL, clds_hazard_pointers_thread_helper_thread_notification shall terminate the process. ]*/
LogCriticalAndTerminate("Unexpected reason: void* context=%p, THREAD_NOTIFICATIONS_LACKEY_DLL_REASON reason=%" PRI_MU_ENUM "",
context, MU_ENUM_VALUE(THREAD_NOTIFICATIONS_LACKEY_DLL_REASON, reason));
}
else
{
switch (reason)
{
default:
/* Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_012: [ If reason is any other value, clds_hazard_pointers_thread_helper_thread_notification shall return. ]*/
LogError("Unexpected reason: void* context=%p, THREAD_NOTIFICATIONS_LACKEY_DLL_REASON reason=%" PRI_MU_ENUM "",
context, MU_ENUM_VALUE(THREAD_NOTIFICATIONS_LACKEY_DLL_REASON, reason));
break;
case THREAD_NOTIFICATIONS_LACKEY_DLL_REASON_THREAD_ATTACH:
/* Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_006: [ If reason is THREAD_NOTIFICATIONS_LACKEY_DLL_REASON_THREAD_ATTACH, clds_hazard_pointers_thread_helper_thread_notification shall return. ]*/
break;
case THREAD_NOTIFICATIONS_LACKEY_DLL_REASON_THREAD_DETACH:
{
CLDS_HAZARD_POINTERS_THREAD_HELPER_HANDLE hazard_pointers_helper = context;
/* Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_007: [ If reason is THREAD_NOTIFICATIONS_LACKEY_DLL_REASON_THREAD_DETACH, clds_hazard_pointers_thread_helper_thread_notification shall call TlsGetValue obtain the thread local value for the slot created in the clds_hazard_pointers_thread_create. ]*/
CLDS_HAZARD_POINTERS_THREAD_HANDLE hp_thread_handle = TlsGetValue(hazard_pointers_helper->tls_slot);
/* Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_009: [ If the thread local stored value is not NULL: ]*/
if (hp_thread_handle != NULL)
{
/* Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_010: [ clds_hazard_pointers_thread_helper_thread_notification shall set the value in the slot to NULL by calling TlsSetValue. ]*/
if (!TlsSetValue(hazard_pointers_helper->tls_slot, NULL))
{
LogError("TlsSetValue(hazard_pointers_helper->tls_slot=%" PRIu32 ", NULL) failed", hazard_pointers_helper->tls_slot);
}
/* Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_011: [ clds_hazard_pointers_thread_helper_thread_notification shall call clds_hazard_pointers_unregister_thread with the argument being the obtained thread local value. ]*/
clds_hazard_pointers_unregister_thread(hp_thread_handle);
}
else
{
/* Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_008: [ If the thread local stored value is NULL, clds_hazard_pointers_thread_helper_thread_notification shall return. ]*/
}
break;
}
}
}
}
IMPLEMENT_MOCKABLE_FUNCTION(, CLDS_HAZARD_POINTERS_THREAD_HELPER_HANDLE, clds_hazard_pointers_thread_helper_create, CLDS_HAZARD_POINTERS_HANDLE, hazard_pointers)
{
CLDS_HAZARD_POINTERS_THREAD_HELPER_HANDLE result;
if (hazard_pointers == NULL)
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_001: [ If hazard_pointers is NULL then clds_hazard_pointers_thread_helper_create shall fail and return NULL. ]*/
LogError("Invalid args: CLDS_HAZARD_POINTERS_HANDLE hazard_pointers = %p", hazard_pointers);
result = NULL;
}
else
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_002: [ clds_hazard_pointers_thread_helper_create shall allocate memory for the helper. ]*/
result = malloc(sizeof(CLDS_HAZARD_POINTERS_THREAD_HELPER));
if (result == NULL)
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_005: [ If there are any errors then clds_hazard_pointers_thread_helper_create shall fail and return NULL. ]*/
LogError("malloc CLDS_HAZARD_POINTERS_THREAD_HELPER failed");
}
else
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_003: [ clds_hazard_pointers_thread_helper_create shall allocate the thread local storage slot for the hazard pointers by calling TlsAlloc. ]*/
result->tls_slot = TlsAlloc();
if (result->tls_slot == TLS_OUT_OF_INDEXES)
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_005: [ If there are any errors then clds_hazard_pointers_thread_helper_create shall fail and return NULL. ]*/
LogError("TlsAlloc failed");
}
else
{
/* Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_001: [ clds_hazard_pointers_thread_helper_create shall obtain a TCALL_DISPATCHER(THREAD_NOTIFICATION_CALL) by calling thread_notifications_dispatcher_get_call_dispatcher. ]*/
TCALL_DISPATCHER(THREAD_NOTIFICATION_CALL) call_dispatcher = thread_notifications_dispatcher_get_call_dispatcher();
if (call_dispatcher == NULL)
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_005: [ If there are any errors then clds_hazard_pointers_thread_helper_create shall fail and return NULL. ]*/
LogError("thread_notifications_dispatcher_get_call_dispatcher failed");
}
else
{
TCALL_DISPATCHER_INITIALIZE_MOVE(THREAD_NOTIFICATION_CALL)(&result->call_dispatcher, &call_dispatcher);
/* Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_002: [ clds_hazard_pointers_thread_helper_create shall register clds_hazard_pointers_thread_helper_thread_notification call target with the TCALL_DISPATCHER(THREAD_NOTIFICATION_CALL). ]*/
result->thread_notification_target_handle = TCALL_DISPATCHER_REGISTER_TARGET(THREAD_NOTIFICATION_CALL)(result->call_dispatcher, clds_hazard_pointers_thread_helper_thread_notification, result);
if (result->thread_notification_target_handle == NULL)
{
LogError("thread_notifications_register_notification failed");
}
else
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_004: [ clds_hazard_pointers_thread_helper_create shall succeed and return the helper. ]*/
result->hazard_pointers = hazard_pointers;
goto all_ok;
//TCALL_DISPATCHER_UNREGISTER_TARGET(THREAD_NOTIFICATION_CALL)(result->call_dispatcher, result->thread_notification_target_handle);;
}
TCALL_DISPATCHER_ASSIGN(THREAD_NOTIFICATION_CALL)(&result->call_dispatcher, NULL);
}
(void)TlsFree(result->tls_slot);
}
free(result);
result = NULL;
}
}
all_ok:
return result;
}
IMPLEMENT_MOCKABLE_FUNCTION(, void, clds_hazard_pointers_thread_helper_destroy, CLDS_HAZARD_POINTERS_THREAD_HELPER_HANDLE, hazard_pointers_helper)
{
if (hazard_pointers_helper == NULL)
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_006: [ If hazard_pointers_helper is NULL then clds_hazard_pointers_thread_helper_destroy shall return. ]*/
LogError("Invalid args: CLDS_HAZARD_POINTERS_THREAD_HELPER_HANDLE hazard_pointers_helper = %p", hazard_pointers_helper);
}
else
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_004: [ clds_hazard_pointers_thread_helper_destroy shall unregister the call target by calling TCALL_DISPATCHER_UNREGISTER_TARGET(THREAD_NOTIFICATION_CALL). ]*/
TCALL_DISPATCHER_UNREGISTER_TARGET(THREAD_NOTIFICATION_CALL)(hazard_pointers_helper->call_dispatcher, hazard_pointers_helper->thread_notification_target_handle);
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_01_003: [ clds_hazard_pointers_thread_helper_destroy shall release its reference to the TCALL_DISPATCHER(THREAD_NOTIFICATION_CALL). ]*/
TCALL_DISPATCHER_ASSIGN(THREAD_NOTIFICATION_CALL)(&hazard_pointers_helper->call_dispatcher, NULL);
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_007: [ clds_hazard_pointers_thread_helper_destroy shall free the thread local storage slot by calling TlsFree. ]*/
(void)TlsFree(hazard_pointers_helper->tls_slot);
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_008: [ clds_hazard_pointers_thread_helper_destroy shall free the helper. ]*/
free(hazard_pointers_helper);
}
}
IMPLEMENT_MOCKABLE_FUNCTION(, CLDS_HAZARD_POINTERS_THREAD_HANDLE, clds_hazard_pointers_thread_helper_get_thread, CLDS_HAZARD_POINTERS_THREAD_HELPER_HANDLE, hazard_pointers_helper)
{
CLDS_HAZARD_POINTERS_THREAD_HANDLE result;
if (hazard_pointers_helper == NULL)
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_009: [ If hazard_pointers_helper is NULL then clds_hazard_pointers_thread_helper_get_thread shall fail and return NULL. ]*/
LogError("Invalid args: CLDS_HAZARD_POINTERS_THREAD_HELPER_HANDLE hazard_pointers_helper = %p", hazard_pointers_helper);
result = NULL;
}
else
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_010: [ clds_hazard_pointers_thread_helper_get_thread shall get the thread local handle by calling TlsGetValue. ]*/
result = TlsGetValue(hazard_pointers_helper->tls_slot);
if (result == NULL)
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_011: [ If no thread local handle exists then: ]*/
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_012: [ clds_hazard_pointers_thread_helper_get_thread shall create one by calling clds_hazard_pointers_register_thread. ]*/
result = clds_hazard_pointers_register_thread(hazard_pointers_helper->hazard_pointers);
if (result == NULL)
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_015: [ If there are any errors then clds_hazard_pointers_thread_helper_get_thread shall fail and return NULL. ]*/
LogError("Cannot create clds hazard pointers thread");
}
else
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_013: [ clds_hazard_pointers_thread_helper_get_thread shall store the new handle by calling TlsSetValue. ]*/
if (!TlsSetValue(hazard_pointers_helper->tls_slot, result))
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_015: [ If there are any errors then clds_hazard_pointers_thread_helper_get_thread shall fail and return NULL. ]*/
LogError("Cannot set Tls slot value");
}
else
{
/*Codes_SRS_CLDS_HAZARD_POINTERS_THREAD_HELPER_42_014: [ clds_hazard_pointers_thread_helper_get_thread shall return the thread local handle. ]*/
goto all_ok;
}
clds_hazard_pointers_unregister_thread(result);
result = NULL;
}
}
}
all_ok:
return result;
}