Duplicate a date log handle for concurrent thread access
DuplicateDateLogHandle creates a new DATE_LOG wrapper that references the same shared
DATE_CONTEXT as hSourceHandle, incrementing the context’s handle reference count
(uiHandles) under its critical section. This allows multiple threads to independently track their
own last-accessed date item and file context duplicate without contending on a single handle’s mutex.
Enables safe concurrent read access to a shared date log, range, or holidays context across multiple threads, each with its own handle instance.
HRESULT _Must_inspect_result_ _Success_(SUCCEEDED(return)) DuplicateDateLogHandle(
_In_ DATE_LOG_HANDLE hSourceHandle,
_Out_ DATE_LOG_HANDLE * lpTargetHandle
);
This matches the declaration in datelog.h.
[In] DATE_LOG_HANDLE
Handle whose shared context is to be referenced by the new duplicate.
[Out] DATE_LOG_HANDLE *
Receives the new, independently closable handle.
HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)Duplicate handle created; reference count incremented.
lpTargetHandle is NULL.
The source context is currently being freed (bIsFreeing == TRUE).
Allocation of the new handle or its lock state failed.
Check the result with FAILED()/SUCCEEDED(); the function is marked _Must_inspect_result_.
Each successful call increments the context’s uiHandles count under its critical section; each CloseDateLogHandle call decrements it. The date log core, holidays handle, and ODBC connections are freed only when the count reaches zero, so the source and its duplicates may be closed in any order.
CloseDateLogHandle() to properly decrement the reference count; the shared context is only freed once the count reaches zero.Incrementing the reference count is performed under the context’s critical section, so duplicating while other threads use or close other handles of the same context is supported. Give each thread its own handle: duplicate on the owning thread (or from a thread that owns a valid handle), pass the duplicate to the worker, and let the worker close it. The same handle should not be closed while another thread is still using it.
Because the context is shared, changes made through one handle (for example dates added by LoadDateLogHandle or MergeDateLogHandle) are visible through every duplicate, and a call such as ConvertDateLogHandle reclassifies all of them together. Use CompareDateLogHandle to test whether two handles describe equal collections.
InitializeDateLogHandle duplicates a supplied holidays handle internally using this function, which is why the caller must still close its own holidays handle.
The example builds a date log, duplicates it, hands the duplicate to a worker thread that closes it when done, and keeps using the original on the main thread. Every handle is closed exactly once.
#include <stdio.h>
#include "datelog.h"
static HRESULT BuildSampleDateLog(HOLIDAYS_HANDLE hHolidays, DATE_LOG_HANDLE *phDateLog)
{
DATE_LOG_HANDLE hDateLog = NULL;
DATE_ITEM_HANDLE hStart = NULL;
DATE_ITEM_HANDLE hEnd = NULL;
SYSTEMTIME stStart = { 0 };
SYSTEMTIME stEnd = { 0 };
FILETIME ftStart = { 0 };
FILETIME ftEnd = { 0 };
HRESULT hr = E_FAIL;
*phDateLog = NULL;
/* Tuesday 2 January 2024 through Wednesday 31 January 2024 (both market days). */
stStart.wYear = 2024; stStart.wMonth = 1; stStart.wDay = 2;
stEnd.wYear = 2024; stEnd.wMonth = 1; stEnd.wDay = 31;
if (!SystemTimeToFileTime(&stStart, &ftStart) || !SystemTimeToFileTime(&stEnd, &ftEnd)) {
return HRESULT_FROM_WIN32(GetLastError());
}
hr = InitializeDateLogHandle(NULL, DATE_LOG_MAX, hHolidays, &hDateLog, HandleIsForDatelog);
if (FAILED(hr)) {
return hr;
}
hr = InitializeDateItem(hDateLog, &ftStart, NULL, &hStart);
if (SUCCEEDED(hr)) {
hr = InitializeDateItem(hDateLog, &ftEnd, NULL, &hEnd);
}
if (SUCCEEDED(hr)) {
/* No file context: generate the contiguous range between hStart and hEnd. */
hr = LoadDateLogHandle(NULL, hStart, hEnd, hDateLog, NULL, 0);
}
FreeDateItem(hEnd);
FreeDateItem(hStart);
if (FAILED(hr)) {
CloseDateLogHandle(hDateLog);
return hr;
}
*phDateLog = hDateLog;
return S_OK;
}
/* Worker thread: owns its duplicate handle and closes it when finished. */
static DWORD WINAPI WorkerThread(LPVOID pvParam)
{
DATE_LOG_HANDLE hMine = (DATE_LOG_HANDLE)pvParam;
DWORD dwLength = 0;
HRESULT hr = E_FAIL;
dwLength = GetDateCollectionLength(hMine);
printf("Worker sees %lu date items.\n", (unsigned long)dwLength);
hr = CloseDateLogHandle(hMine);
return FAILED(hr) ? 1 : 0;
}
int main(void)
{
HOLIDAYS_HANDLE hHolidays = NULL;
DATE_LOG_HANDLE hDateLog = NULL;
DATE_LOG_HANDLE hCopy = NULL;
HANDLE hThread = NULL;
DWORD dwExit = 0;
HRESULT hr = E_FAIL;
int rc = 1;
/* Holidays handle. Populate it with your market holiday data (see
LoadDateLogHandle) before building date logs; that step is application specific. */
hr = InitializeDateLogHandle(NULL, DATE_LOG_MAX, NULL, &hHolidays, HandleIsForHolidays);
if (FAILED(hr)) {
printf("InitializeDateLogHandle failed for holidays: 0x%08lX\n", (unsigned long)hr);
return 1;
}
hr = BuildSampleDateLog(hHolidays, &hDateLog);
if (FAILED(hr)) {
printf("BuildSampleDateLog failed: 0x%08lX\n", (unsigned long)hr);
goto cleanup;
}
/* Duplicate on the owning thread; the new handle shares the context
(reference count is now 2) but has its own mutex and bookkeeping. */
hr = DuplicateDateLogHandle(hDateLog, &hCopy);
if (FAILED(hr)) {
printf("DuplicateDateLogHandle failed: 0x%08lX\n", (unsigned long)hr);
goto cleanup;
}
hThread = CreateThread(NULL, 0, WorkerThread, hCopy, 0, NULL);
if (hThread == NULL) {
printf("CreateThread failed: %lu\n", GetLastError());
/* The worker never started, so the duplicate is still ours to close. */
if (FAILED(CloseDateLogHandle(hCopy))) {
rc = 1;
}
hCopy = NULL;
goto cleanup;
}
hCopy = NULL; /* ownership moved to the worker thread */
/* Meanwhile the original handle remains usable on this thread. */
printf("Main sees %lu date items.\n", (unsigned long)GetDateCollectionLength(hDateLog));
WaitForSingleObject(hThread, INFINITE);
if (GetExitCodeThread(hThread, &dwExit) && dwExit == 0) {
rc = 0;
}
CloseHandle(hThread);
cleanup:
if (hDateLog != NULL && FAILED(CloseDateLogHandle(hDateLog))) {
rc = 1;
}
if (FAILED(CloseDateLogHandle(hHolidays))) {
rc = 1;
}
return rc;
}