DuplicateDateLogHandle

Duplicate a date log handle for concurrent thread access

Overview

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.

Purpose

Enables safe concurrent read access to a shared date log, range, or holidays context across multiple threads, each with its own handle instance.

Key Features

Function Signature


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.

Parameters

hSourceHandle

[In] DATE_LOG_HANDLE

Handle whose shared context is to be referenced by the new duplicate.

  • Type: a valid date log, date range, or holidays handle
  • NULL: not documented as accepted; always pass a valid handle
  • Ownership: unchanged; the source handle is not consumed and must still be closed by its owner
  • Lifetime: the source must remain valid for the duration of the call. After a successful call the duplicate keeps the shared context alive independently of the source
lpTargetHandle

[Out] DATE_LOG_HANDLE *

Receives the new, independently closable handle.

  • NULL: not allowed; a NULL pointer returns HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)
  • Ownership: on success the caller owns the returned handle and must release it with CloseDateLogHandle
  • On failure: the documentation does not specify the value left in the output; treat it as invalid and do not close it

Return Values

S_OK

Duplicate handle created; reference count incremented.

HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)

lpTargetHandle is NULL.

HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE)

The source context is currently being freed (bIsFreeing == TRUE).

HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY)

Allocation of the new handle or its lock state failed.

Check the result with FAILED()/SUCCEEDED(); the function is marked _Must_inspect_result_.

Remarks

Handle Lifetime and Reference Counting

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.

The returned handle MUST be released via CloseDateLogHandle() to properly decrement the reference count; the shared context is only freed once the count reaches zero.

Thread Safety

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.

Shared State

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.

Internal Use

InitializeDateLogHandle duplicates a supplied holidays handle internally using this function, which is why the caller must still close its own holidays handle.

Sample Code

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;
}

See Also