CloseDateLogHandle

Closes and releases resources associated with a DATE_LOG_HANDLE

Overview

CloseDateLogHandle closes and releases all resources associated with a DATE_LOG_HANDLE, including file information, synchronization objects, and shared context when all references are released. This function implements a reference-counted lifecycle pattern where the shared context is freed only when all active handles pointing to it have been closed.

Purpose

This function is the primary cleanup mechanism for date log handles. It handles:

Key Features

Function Signature

HRESULT _Success_(SUCCEEDED(return)) CloseDateLogHandle(
    _In_ _Post_invalid_ DATE_LOG_HANDLE hDateLogHandle
);

Calling Convention

Standard C calling convention with SAL annotations indicating successful return code expectation and handle invalidation after the call.

Parameters

hDateLogHandle

[In] DATE_LOG_HANDLE

Handle to a date log created by InitializeDateLogHandle() or obtained via DuplicateDateLogHandle().

  • Must be a valid handle created through the handle initialization API
  • Can represent any of the three handle types: DateLog, DateRange, or Holidays
  • After this function returns (success or failure), the handle becomes invalid
  • SAL annotation _Post_invalid_ indicates the pointer should not be dereferenced after the call

Return Values

Success Cases

S_OK (0x00000000)

The handle was closed successfully and all resources were released. Reference count was decremented, and if it reached zero, the shared context and its resources were freed.

Error Cases

Validation and Synchronization Errors

HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE) (0x80070006)

The handle is invalid, NULL, or failed validation. Additionally, this error is returned if mutex acquisition fails, indicating either:

HRESULT_FROM_WIN32(GetLastError())

System error from synchronization primitives. The handle state may be partially cleaned up.

Return Value Checking

// Recommended error checking pattern
DATE_LOG_HANDLE hDateLog = NULL;

// ... use the handle ...

HRESULT hr = CloseDateLogHandle(hDateLog);
if (SUCCEEDED(hr)) {
    printf("Handle closed successfully\n");
    hDateLog = NULL;  // Mark as invalid
} else if (hr == HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE)) {
    printf("Handle was already invalid or corrupted\n");
} else {
    printf("Close operation failed: 0x%08X\n", hr);
    // Do not attempt to use hDateLog further
}

Behavior and Operations

Execution Flow

Validate Handle
Acquire Mutex
Decrement Ref Count
Free Context if Zero
Release Resources

Step-by-Step Process

  1. Handle Validation: Calls IsValidDateLogHandle() to verify the handle structure and magic numbers
  2. Extract Context: Retrieves pointers to the DATE_LOG and DATE_CONTEXT structures
  3. Acquire Handle Mutex: Acquires pDateLog->hMutex with INFINITE timeout
  4. Enter Critical Section: Increments lInsideCount and enters critical_section
  5. Decrement Counter: Decrements pContext->uiHandles and saves the result
  6. Leave Critical Section: Leaves critical section and decrements lInsideCount
  7. Conditional Context Cleanup: If uiHandles == 0, calls FreeDateLogHandleContext()
  8. Release Mutex: Releases pDateLog->hMutex
  9. File Info Cleanup: If pDateLog->pFileInfo exists, calls FreeFileInfo()
  10. Handle Mutex Cleanup: Closes pDateLog->hMutex
  11. Handle Deallocation: Frees the DATE_LOG structure itself

Reference Counting Mechanism

The function uses a reference counting pattern where:

Thread Safety: Reference count operations are protected by both a mutex and a critical section to ensure atomic operations across thread boundaries.

Context Cleanup

When FreeDateLogHandleContext() is called (i.e., when reference count reaches zero), it:

Reference Counting Details

Lifecycle Example

// Initial creation - reference count = 1
DATE_LOG_HANDLE hLog1 = NULL;
InitializeDateLogHandle(..., &hLog1, ...);
// Internal: pContext->uiHandles = 1

// First duplication - reference count = 2
DATE_LOG_HANDLE hLog2 = NULL;
DuplicateDateLogHandle(hLog1, &hLog2);
// Internal: pContext->uiHandles = 2

// Close first handle - reference count = 1
CloseDateLogHandle(hLog1);  // Returns S_OK, context NOT freed
hLog1 = NULL;
// Internal: pContext->uiHandles = 1

// Close second handle - reference count = 0
CloseDateLogHandle(hLog2);  // Returns S_OK, context IS freed
hLog2 = NULL;
// Internal: context and all resources freed

Critical Sections and Interlocked Operations

Synchronization Primitive Purpose Protected Data
pDateLog->hMutex Protects handle-level operations Handle state and reference count updates
pContext->critical_section Protects context field modifications uiHandles counter
lInsideCount (Interlocked) Tracks threads in critical section Signals safe context cleanup time

Examples

Basic Usage

Example 1: Simple Handle Lifecycle
// Create a date log handle
DATE_LOG_HANDLE hDateLog = NULL;
HRESULT hr = InitializeDateLogHandle(
    NULL,                    // Security attributes
    DATE_LOG_MAX,            // Max size
    hHolidays,              // Holidays handle
    &hDateLog,              // Output
    HandleIsForDatelog      // Type
);

if (SUCCEEDED(hr)) {
    // Use the handle
    LoadDateLogHandle(..., hDateLog, ...);
    
    // Get some data
    DATE_ITEM_HANDLE hFirstDate = GetDateLogFirstDate(hDateLog);
    
    // When done, close it
    hr = CloseDateLogHandle(hDateLog);
    if (SUCCEEDED(hr)) {
        hDateLog = NULL;  // Mark invalid
    }
}

Multithreaded Usage

Example 2: Handle Duplication for Concurrent Access

    // Main thread creates original handle
    DATE_LOG_HANDLE hOriginal = NULL;
    InitializeDateLogHandle(..., &hOriginal, ...);

    // Worker threads duplicate for concurrent access
    DWORD WINAPI WorkerThread(LPVOID lpParam) {
        DATE_LOG_HANDLE hWorker = NULL;
    
        // Duplicate handle for thread-local use
        HRESULT hr = DuplicateDateLogHandle(hOriginal, &hWorker);
        if (SUCCEEDED(hr)) {
            // Each thread can independently query dates
            DATE_ITEM_HANDLE hDate = FindDateItemByGuid(hWorker, &guid);
        
            // Clean up when done
            CloseDateLogHandle(hWorker);
        }
        return 0;
    }
                   

    // After all worker threads complete
    CloseDateLogHandle(hOriginal);
    hOriginal = NULL;  // All resources freed
                 

Error Handling

Example 3: Robust Error Handling
DATE_LOG_HANDLE hDateLog = NULL;

    // Initialize handle
    HRESULT hr = InitializeDateLogHandle(..., &hDateLog, ...);
    if (FAILED(hr)) {
        printf("Failed to create handle: 0x%08X\n", hr);
        return hr;
    }

    // Use handle...
    __try {
        // Perform operations on hDateLog
        hr = LoadDateLogHandle(..., hDateLog, ...);
        if (FAILED(hr)) {
            printf("Failed to load data: 0x%08X\n", hr);
        }
    } __finally {
        // Ensure proper cleanup even on exception
        if (hDateLog) {
            HRESULT hrClose = CloseDateLogHandle(hDateLog);
            if (FAILED(hrClose)) {
                printf("Warning: Close failed with 0x%08X\n", hrClose);
            }
            hDateLog = NULL;
        }
    }                     
    

Notes and Important Information

Handle Invalidation

Critical: After CloseDateLogHandle() returns (whether successfully or with an error), the handle pointer must be set to NULL and never dereferenced again. The SAL annotation _Post_invalid_ enforces this at compile time with static analysis tools.

Mutex Acquisition

The function acquires mutexes with INFINITE timeout. In extreme resource contention scenarios, this could cause thread blocking. However, this is preferred over timing out to ensure proper resource cleanup.

Cascading Cleanup

When the last handle is closed and the context is freed:

Recursive Closure: Do not hold references to the holidays handle after closing the main date log, as the holidays handle is automatically closed and freed during context cleanup.

File Information Cleanup

Each handle can have associated file information (for merge operations). This is freed separately from the context, ensuring:

Performance Considerations