adapters/srw_lock.c (268 lines of code) (raw):
// Copyright (C) Microsoft Corporation. All rights reserved.
#include <inttypes.h>
#include <stdbool.h>
#include "windows.h"
#include "azure_c_shared_utility/xlogging.h"
#include "azure_c_shared_utility/gballoc.h"
#include "azure_c_shared_utility/timer.h"
#include "azure_c_shared_utility/string_utils.h"
#include "azure_c_shared_utility/srw_lock.h"
/*
vocabulary:
blaBlaCount = values of counter for event blabla
blablaCounts = sum of individual blablaCountEnd-blablaCountBegin (always a sum of durations of Counts)
time = blablaCounts/freq
*/
#define TIME_BETWEEN_STATISTICS_LOG 600 /*in seconds, so every 10 minutes*/
typedef struct SRW_LOCK_HANDLE_DATA_TAG
{
SRWLOCK lock;
LARGE_INTEGER freq;
volatile LONG64 nCalls_AcquireSRWLockExclusive; /*number of calls to AcquireSRWLockExclusive*/
volatile LONG64 totalCounts_AcquireSRWLockExclusive; /*how many counts were spent taking the lock (that is, just before and after AcquireSRWLockExclusive call*/
volatile LONG64 totalCounts_ReleaseSRWLockExclusive; /*how many counts were spent releasing the exclusive SRW that is, just before and after ReleaseSRWLockExclusive call*/
volatile LONG64 lastCount_AcquireSRWLockExclusive; /*last time the lock was taken exclusively*/
volatile LONG64 totalCountsBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive; /*how much time the lock was taken in total in exclusive mode*/
volatile LONG64 nCalls_AcquireSRWLockShared; /*number of calls to AcquireSRWLockShared*/
volatile LONG64 totalCounts_AcquireSRWLockShared; /*how many counts were spent taking the lock (that is, just before and after AcquireSRWLockShared */
volatile LONG64 totalCounts_ReleaseSRWLockShared; /*how many counts were spent releasing the shared SRW that is, just before and after ReleaseSRWLockShared call*/
volatile LONG nSharedReaders; /*if lock_shared is granted, then there are 1 more readers... when readers gets to 0, the shared "locked" count can be updated*/
volatile LONG64 firstCount_AcquireSRWLockShared; /*first time the lock was taken in shared mode*/
volatile LONG64 totalCountsBetween_AcquireSRWLockShared_and_ReleaseSRWLockShared; /*how much time the lock was taken from the first granted shared access to the last released shared access*/
LARGE_INTEGER handleCreateCount; /*when the handle was created*/
TIMER_HANDLE timer;
char* lockName;
bool doStatistics;
}SRW_LOCK_HANDLE_DATA;
static void LogStatistics(SRW_LOCK_HANDLE handle, const char* reason)
{
LARGE_INTEGER now;
(void)QueryPerformanceCounter(&now);
/*print statistics*/
LogInfo("srw_lock_statistics reason:%s SRW_LOCK_HANDLE handle %p lock_name=%s\n"
"freq=%" PRId64 ",\n"
"nCalls_AcquireSRWLockExclusive=%" PRId64 ",\n"
"totalCounts_AcquireSRWLockExclusive=%" PRId64 ",\n"
"totalCounts_ReleaseSRWLockExclusive=%" PRId64 ",\n"
"totalCountsBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive=%" PRId64 ",\n"
"nCalls_AcquireSRWLockShared=%" PRId64 ",\n"
"totalCounts_AcquireSRWLockShared=%" PRId64 ",\n"
"totalCounts_ReleaseSRWLockShared=%" PRId64 ",\n"
"totalCountsBetween_AcquireSRWLockShared_and_ReleaseSRWLockShared=%" PRId64 ",\n"
"totalCountsBetween_Create_and_Now=%" PRId64 ",\n"
"totalTimeBetween_Create_and_Now[s]=%.2f, \n"
"totalTimeBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive[s]=%.2f, \n"
"totalTimeBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive[%%]=%.2f, \n"
"averageTimeBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive[us]=%.2f, \n"
"totalTimeBetween_AcquireSRWLockShared_and_ReleaseSRWLockShared[s]=%.2f, \n"
"totalTimeBetween_AcquireSRWLockShared_and_ReleaseSRWLockShared[%%]=%.2f",
reason,
handle,
handle->lockName,
handle->freq.QuadPart,
InterlockedAdd64(&handle->nCalls_AcquireSRWLockExclusive, 0),
InterlockedAdd64(&handle->totalCounts_AcquireSRWLockExclusive, 0),
InterlockedAdd64(&handle->totalCounts_ReleaseSRWLockExclusive, 0),
InterlockedAdd64(&handle->totalCountsBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive, 0),
InterlockedAdd64(&handle->nCalls_AcquireSRWLockShared, 0),
InterlockedAdd64(&handle->totalCounts_AcquireSRWLockShared, 0),
InterlockedAdd64(&handle->totalCounts_ReleaseSRWLockShared, 0),
InterlockedAdd64(&handle->totalCountsBetween_AcquireSRWLockShared_and_ReleaseSRWLockShared, 0),
/*"totalTimeBetween_Create_and_Now[s]*/
now.QuadPart - handle->handleCreateCount.QuadPart,
(double)(now.QuadPart -handle->handleCreateCount.QuadPart)/ (double)handle->freq.QuadPart,
(double)InterlockedAdd64(&handle->totalCountsBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive, 0) / (double)handle->freq.QuadPart,
(double)InterlockedAdd64(&handle->totalCountsBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive, 0) / (double)(now.QuadPart - handle->handleCreateCount.QuadPart) *100,
(InterlockedAdd64(&handle->nCalls_AcquireSRWLockExclusive, 0)>0?(double)InterlockedAdd64(&handle->totalCountsBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive, 0)*1000000 / (double)(now.QuadPart - handle->handleCreateCount.QuadPart) / InterlockedAdd64(&handle->nCalls_AcquireSRWLockExclusive, 0):-1),
(double)InterlockedAdd64(&handle->totalCountsBetween_AcquireSRWLockShared_and_ReleaseSRWLockShared , 0) / (double)handle->freq.QuadPart,
(double)InterlockedAdd64(&handle->totalCountsBetween_AcquireSRWLockShared_and_ReleaseSRWLockShared, 0) / (double)(now.QuadPart - handle->handleCreateCount.QuadPart) * 100
);
}
SRW_LOCK_HANDLE srw_lock_create(bool do_statistics, const char* lock_name)
{
SRW_LOCK_HANDLE result;
/*Codes_SRS_SRW_LOCK_02_001: [ srw_lock_create shall allocate memory for SRW_LOCK_HANDLE. ]*/
result = calloc(1, sizeof(SRW_LOCK_HANDLE_DATA));
if (result == NULL)
{
/*return as is*/
LogError("failure in malloc(sizeof(SRW_LOCK_HANDLE_DATA)=%zu)", sizeof(SRW_LOCK_HANDLE_DATA));
}
else
{
/*Codes_SRS_SRW_LOCK_02_023: [ If do_statistics is true then srw_lock_create shall copy lock_name. ]*/
if (do_statistics &&
((result->lockName= sprintf_char("%s", MU_P_OR_NULL(lock_name))) == NULL)
)
{
LogError("failure in sprintf_char(\"%%s\", lock_name=%s)", MU_P_OR_NULL(lock_name));
}
else
{
/*Codes_SRS_SRW_LOCK_02_024: [ If do_statistics is true then srw_lock_create shall create a new TIMER_HANDLE by calling timer_create. ]*/
if(
do_statistics &&
((result->timer = timer_create())==NULL)
)
{
LogError("failure in timer_create()");
}
else
{
result->doStatistics = do_statistics;
/*Codes_SRS_SRW_LOCK_02_015: [ srw_lock_create shall call InitializeSRWLock. ]*/
InitializeSRWLock(&result->lock);
(void)QueryPerformanceFrequency(&result->freq);
(void)InterlockedExchange64(&result->nCalls_AcquireSRWLockExclusive, 0);
(void)InterlockedExchange64(&result->nCalls_AcquireSRWLockShared, 0);
(void)InterlockedExchange64(&result->totalCounts_AcquireSRWLockExclusive, 0);
(void)InterlockedExchange64(&result->totalCounts_ReleaseSRWLockExclusive, 0);
(void)InterlockedExchange64(&result->totalCounts_AcquireSRWLockShared, 0);
(void)InterlockedExchange64(&result->totalCounts_ReleaseSRWLockShared, 0);
(void)InterlockedExchange64(&result->totalCountsBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive, 0);
(void)InterlockedExchange(&result->nSharedReaders, 0);
(void)InterlockedExchange64(&result->totalCountsBetween_AcquireSRWLockShared_and_ReleaseSRWLockShared, 0);
(void)QueryPerformanceCounter(&result->handleCreateCount);
if (do_statistics)
{
LogInfo("srw_lock_create returns %p for lock_name=%s", result, MU_P_OR_NULL(lock_name));
}
/*Codes_SRS_SRW_LOCK_02_003: [ srw_lock_create shall succeed and return a non-NULL value. ]*/
goto allOk;
//timer_destroy(result->timer);
}
/*Codes_SRS_SRW_LOCK_02_004: [ If there are any failures then srw_lock_create shall fail and return NULL. ]*/
do_statistics ? free(result->lockName) : (void)0;
}
free(result);
result = NULL;
}
allOk:;
return result;
}
void srw_lock_acquire_exclusive(SRW_LOCK_HANDLE handle)
{
/*Codes_SRS_SRW_LOCK_02_022: [ If handle is NULL then srw_lock_acquire_exclusive shall return. ]*/
if (handle == NULL)
{
LogError("invalid argument SRW_LOCK_HANDLE handle=%p", handle);
}
else
{
if (!handle->doStatistics)
{
AcquireSRWLockExclusive(&handle->lock);
}
else
{
LARGE_INTEGER start, stop;
(void)QueryPerformanceCounter(&start);
/*Codes_SRS_SRW_LOCK_02_006: [ srw_lock_acquire_exclusive shall call AcquireSRWLockExclusive. ]*/
AcquireSRWLockExclusive(&handle->lock);
(void)QueryPerformanceCounter(&stop); /*measure acquire time*/
(void)InterlockedIncrement64(&handle->nCalls_AcquireSRWLockExclusive);
(void)InterlockedAdd64(&handle->totalCounts_AcquireSRWLockExclusive, stop.QuadPart - start.QuadPart);
/*piggyback on the lock itself to print the statistics "until now" - note - does not include the current Lock call*/
/*Codes_SRS_SRW_LOCK_02_025: [ If do_statistics is true and if the timer created has recorded more than TIME_BETWEEN_STATISTICS_LOG seconds then statistics will be logged and the timer shall be started again. ]*/
if (timer_get_elapsed(handle->timer) >= TIME_BETWEEN_STATISTICS_LOG)
{
timer_start(handle->timer);
LogStatistics(handle, "periodic logging almost every " MU_TOSTRING(TIME_BETWEEN_STATISTICS_LOG) " seconds");
}
(void)InterlockedExchange64(&handle->lastCount_AcquireSRWLockExclusive, stop.QuadPart);
}
}
}
void srw_lock_release_exclusive(SRW_LOCK_HANDLE handle)
{
/*Codes_SRS_SRW_LOCK_02_009: [ If handle is NULL then srw_lock_release_exclusive shall return. ]*/
if (handle == NULL)
{
LogError("invalid argument SRW_LOCK_HANDLE handle=%p", handle);
}
else
{
if (!handle->doStatistics)
{
#ifdef _MSC_VER
#pragma warning(disable:26110) // Warning C26110: Caller failing to hold lock 'handle->lock' before calling function 'ReleaseSRWLockExclusive'.
#endif
ReleaseSRWLockExclusive(&handle->lock);
#ifdef _MSC_VER
#pragma warning (default:26110)
#endif
}
else
{
LARGE_INTEGER start, stop;
LARGE_INTEGER lastAcquireCount_exclusive_copy;
lastAcquireCount_exclusive_copy.QuadPart = InterlockedAdd64(&handle->lastCount_AcquireSRWLockExclusive, 0);
(void)QueryPerformanceCounter(&start);
/*Codes_SRS_SRW_LOCK_02_010: [ srw_lock_release_exclusive shall call ReleaseSRWLockExclusive. ]*/
#ifdef _MSC_VER
#pragma warning(disable:26110) // Warning C26110: Caller failing to hold lock 'handle->lock' before calling function 'ReleaseSRWLockExclusive'.
#endif
ReleaseSRWLockExclusive(&handle->lock);
#ifdef _MSC_VER
#pragma warning (default:26110)
#endif
(void)QueryPerformanceCounter(&stop); /*measure release time*/
(void)InterlockedAdd64(&handle->totalCounts_ReleaseSRWLockExclusive, (stop.QuadPart - start.QuadPart));
(void)InterlockedAdd64(&handle->totalCountsBetween_AcquireSRWLockExclusive_and_ReleaseSRWLockExclusive, (stop.QuadPart - lastAcquireCount_exclusive_copy.QuadPart));
}
}
}
void srw_lock_acquire_shared(SRW_LOCK_HANDLE handle)
{
/*Codes_SRS_SRW_LOCK_02_017: [ If handle is NULL then srw_lock_acquire_shared shall return. ]*/
if (handle == NULL)
{
LogError("invalid argument SRW_LOCK_HANDLE handle=%p", handle);
}
else
{
if (!handle->doStatistics)
{
AcquireSRWLockShared(&handle->lock);
}
else
{
LARGE_INTEGER start, stop;
(void)QueryPerformanceCounter(&start);
/*Codes_SRS_SRW_LOCK_02_018: [ srw_lock_acquire_shared shall call AcquireSRWLockShared. ]*/
AcquireSRWLockShared(&handle->lock);
(void)QueryPerformanceCounter(&stop); /*measure acquire time*/
/*there is one more shared reader*/
/*is this the first reader?*/
if (InterlockedIncrement(&handle->nSharedReaders) == 1)
{
(void)InterlockedExchange64(&handle->firstCount_AcquireSRWLockShared, stop.QuadPart);
}
(void)InterlockedIncrement64(&handle->nCalls_AcquireSRWLockShared);
(void)InterlockedAdd64(&handle->totalCounts_AcquireSRWLockShared, stop.QuadPart - start.QuadPart);
/*Codes_SRS_SRW_LOCK_02_026: [ If do_statistics is true and the timer created has recorded more than TIME_BETWEEN_STATISTICS_LOG seconds then statistics will be logged and the timer shall be started again. ]*/
if (timer_get_elapsed(handle->timer) >= TIME_BETWEEN_STATISTICS_LOG)
{
timer_start(handle->timer);
LogStatistics(handle, "periodic logging almost every " MU_TOSTRING(TIME_BETWEEN_STATISTICS_LOG) " seconds");
}
}
}
}
void srw_lock_release_shared(SRW_LOCK_HANDLE handle)
{
/*Codes_SRS_SRW_LOCK_02_020: [ If handle is NULL then srw_lock_release_shared shall return. ]*/
if (handle == NULL)
{
LogError("invalid argument SRW_LOCK_HANDLE handle=%p", handle);
}
else
{
if (!handle->doStatistics)
{
ReleaseSRWLockShared(&handle->lock);
}
else
{
LARGE_INTEGER start, stop;
LARGE_INTEGER firstCount_AcquireSRWLockShared_copy;
firstCount_AcquireSRWLockShared_copy.QuadPart = InterlockedAdd64(&handle->firstCount_AcquireSRWLockShared, 0);
(void)QueryPerformanceCounter(&start);
/*Codes_SRS_SRW_LOCK_02_021: [ srw_lock_release_shared shall call ReleaseSRWLockShared. ]*/
ReleaseSRWLockShared(&handle->lock);
(void)QueryPerformanceCounter(&stop); /*measure release time*/
if (InterlockedDecrement(&handle->nSharedReaders) == 0)
{
(void)InterlockedAdd64(&handle->totalCountsBetween_AcquireSRWLockShared_and_ReleaseSRWLockShared, (stop.QuadPart - firstCount_AcquireSRWLockShared_copy.QuadPart));
}
(void)InterlockedAdd64(&handle->totalCounts_ReleaseSRWLockShared, (stop.QuadPart - start.QuadPart));
}
}
}
void srw_lock_destroy(SRW_LOCK_HANDLE handle)
{
/*Codes_SRS_SRW_LOCK_02_011: [ If handle is NULL then srw_lock_destroy shall return. ]*/
if (handle == NULL)
{
LogError("invalid arguments SRW_LOCK_HANDLE handle=%p", handle);
}
else
{
if (handle->doStatistics)
{
LogStatistics(handle, "srw_lock_destroy was called");
timer_destroy(handle->timer);
free(handle->lockName);
}
/*Codes_SRS_SRW_LOCK_02_012: [ srw_lock_destroy shall free all used resources. ]*/
/*there's no deinit for a SRW, since it is a static construct*/
free(handle);
}
}