Closes and releases resources associated with a DATE_LOG_HANDLE
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.
This function is the primary cleanup mechanism for date log handles. It handles:
HRESULT _Success_(SUCCEEDED(return)) CloseDateLogHandle(
_In_ _Post_invalid_ DATE_LOG_HANDLE hDateLogHandle
);
Standard C calling convention with SAL annotations indicating successful return code expectation and handle invalidation after the call.
[In] DATE_LOG_HANDLE
Handle to a date log created by InitializeDateLogHandle() or obtained via DuplicateDateLogHandle().
_Post_invalid_ indicates the pointer should not be dereferenced after the callThe 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.
The handle is invalid, NULL, or failed validation. Additionally, this error is returned if mutex acquisition fails, indicating either:
System error from synchronization primitives. The handle state may be partially cleaned up.
// 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
}
IsValidDateLogHandle() to verify the handle structure and magic numberspDateLog->hMutex with INFINITE timeoutlInsideCount and enters critical_sectionpContext->uiHandles and saves the resultlInsideCountuiHandles == 0, calls FreeDateLogHandleContext()pDateLog->hMutexpDateLog->pFileInfo exists, calls FreeFileInfo()pDateLog->hMutexThe function uses a reference counting pattern where:
uiHandles
When FreeDateLogHandleContext() is called (i.e., when reference count reaches zero), it:
lInsideCount == 0)// 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
| 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 |
// 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
}
}
// 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
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;
}
}
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.
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.
When the last handle is closed and the context is freed:
Each handle can have associated file information (for merge operations). This is freed separately from the context, ensuring: