ConvertToDateLogHandle

Convert and verify a DATE_RANGE_HANDLE back to a DATE_LOG_HANDLE

Overview

ConvertToDateLogHandle verifies and converts a DATE_RANGE_HANDLE back to a DATE_LOG_HANDLE by performing comprehensive validation of the backing file and its contents. This function is typically called as the final step of a merge operation to ensure data integrity before the handle is ready for production use.

Purpose

After merging date ranges into a date log using MergeDateLogHandle(), the destination handle remains in the "range" state (with magic __RANGE_LOG_MAGIC__) until this function successfully converts it to the "log" state (__DATE_LOG_MAGIC__). The conversion involves:

Key Features

Function Signature

HRESULT _Must_inspect_result_ _Success_(SUCCEEDED(return)) ConvertToDateLogHandle(
    _Inout_ _Pre_valid_ DATE_RANGE_HANDLE hDateRangeHandle,
    _In_opt_ DATE_ITEM_HANDLE hLastDate
);

Calling Convention

Standard C calling convention with SAL annotations indicating the function requires result inspection and modifies the handle in-place on success.

Parameters

hDateRangeHandle

[In, Out] DATE_RANGE_HANDLE

Handle to a DATE_RANGE that will be converted to a DATE_LOG_HANDLE.

  • Must be a valid DATE_RANGE_HANDLE created with HandleIsForRange type
  • Must currently have magic number set to __RANGE_LOG_MAGIC__
  • Must have associated file information (pFileInfo) for backing file access
  • On success, the magic number is updated to __DATE_LOG_MAGIC__
  • On failure, the magic number remains __RANGE_LOG_MAGIC__ (unconverted state)
  • Precondition: Must be pre-validated and not NULL
hLastDate

[In, Optional] DATE_ITEM_HANDLE

Optional reference date item that must be present in the re-parsed file contents.

  • If provided (not NULL), the function verifies this date exists in the converted log
  • If not provided (NULL), this verification step is skipped
  • Typically the last date that was added during the merge operation
  • Used to ensure merge operation did not lose critical date items
  • If the verification fails, the function returns ERROR_INVALID_HANDLE

Return Values

Success Cases

S_OK (0x00000000)

The DATE_RANGE_HANDLE was successfully converted to a DATE_LOG_HANDLE. The magic number has been updated to __DATE_LOG_MAGIC__ and the handle is now ready for use as a date log. All verification checks passed, including the optional last date verification if hLastDate was provided.

Error Cases

Validation and Pre-condition Errors

HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) (0x80070057)

The provided handle is not a valid DATE_RANGE_HANDLE or the file information is invalid. The handle magic number is incorrect for this operation.

HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE) (0x80070006)

Multiple failure scenarios:

Synchronization Errors

HRESULT_FROM_WIN32(GetLastError())

System error from mutex acquisition. May indicate deadlock or resource exhaustion.

File Parsing Errors

See return codes from ParseDateLogFiles() and InitializeDateLogHandle() including:

ERROR_OUTOFMEMORY
ERROR_FILE_NOT_FOUND
ERROR_FILE_CORRUPT

Return Value Checking

// Recommended error checking pattern
HRESULT hr = ConvertToDateLogHandle(hDateRange, hLastDate);

if (SUCCEEDED(hr)) {
    // Conversion successful - handle is now a DATE_LOG_HANDLE
    printf("Range successfully converted to log\n");
    // Now safe to use as DATE_LOG_HANDLE
} else if (hr == HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE)) {
    // Last date verification failed or handle invalid
    printf("Conversion failed: handle or date verification failed\n");
    // Handle remains as DATE_RANGE_HANDLE
} else {
    // Other error - investigate
    printf("Conversion failed: 0x%08X\n", hr);
}

Behavior and Operations

Execution Flow

Validate Range
Acquire Mutexes
Create Temp Handle
Re-parse File
Verify Date
Update Magic

Validation Phase

  1. Verify input DATE_RANGE_HANDLE is valid via IsValidDateRangeHandle()
  2. Guard the context pointer (must not be NULL)
  3. Guard the core pointer (must not be NULL)
  4. Verify file information is valid via IsValidFileInfo()

Lock Acquisition Phase

Acquires two mutexes in this order:

  1. Handle Mutex: pDateLog->hMutex with INFINITE timeout
  2. Context Mutex: pDateLog->pContext->hMutex with INFINITE timeout
Lock Ordering: Always acquire handle mutex before context mutex. This strict ordering prevents deadlocks when multiple threads operate on the same handles.

Verification Phase (Inside Lock Protection)

  1. Create temporary DATE_LOG_HANDLE using InitializeDateLogHandle() with same configuration
  2. Re-parse backing file via ParseDateLogFiles() with PARSE_VERIFY_ENABLE_FLAG
  3. If hLastDate provided, search for it in re-parsed data using SearchDateItem()
  4. If date not found, return ERROR_INVALID_HANDLE (verification failed)

State Update Phase (Atomic)

  1. Only if all verifications succeeded, update: pDateLog->pContext->dwMagic = __DATE_LOG_MAGIC__
  2. This is the atomic transition point from range to log state

Cleanup Phase

  1. Clean up temporary DATE_LOG_HANDLE via CloseDateLogHandle()
  2. If cleanup fails but main operation succeeded, preserve success
  3. If cleanup fails after main operation already failed, preserve original error
  4. Release context mutex (in __finally block)
  5. Release handle mutex (in __finally block)

Synchronization and Thread Safety

Lock Protection Strategy

The function uses structured exception handling (__try/__finally) to ensure mutexes are released even if verification fails:

BOOL bHandleMutex = FALSE;
BOOL bContextMutex = FALSE;

// Acquire handle mutex first
dwWaitResult = WaitForSingleObject(pDateLog->hMutex, INFINITE);
if (dwWaitResult != WAIT_OBJECT_0) {
    return HRESULT_FROM_WIN32(GetLastError());
}
bHandleMutex = TRUE;

__try {
    // Acquire context mutex
    dwWaitResult = WaitForSingleObject(pDateLog->pContext->hMutex, INFINITE);
    if (dwWaitResult != WAIT_OBJECT_0) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        __leave;
    }
    bContextMutex = TRUE;
    
    // ... perform verification ...
    
} __finally {
    // Always release in reverse order
    if (bContextMutex) {
        ReleaseMutex(pDateLog->pContext->hMutex);
    }
    if (bHandleMutex) {
        ReleaseMutex(pDateLog->hMutex);
    }
}

Deadlock Prevention

Strategy Implementation
Consistent Lock Ordering Always acquire handle mutex before context mutex
Reverse Release Order Release context mutex before handle mutex
Exception Safety Use __finally to ensure release even on error
Single Lock Timeout INFINITE timeout allows completion without false timeouts

Isolated Verification

The temporary handle created during verification is completely isolated from the original range handle:

Examples

Post-Merge Conversion

Example 1: Convert After Merge Operation
// Merge source range into destination log
DATE_LOG_HANDLE hDestLog = NULL;
DATE_RANGE_HANDLE hSourceRange = NULL;

// ... initialize and populate handles ...

// Perform merge operation
HRESULT hr = MergeDateLogHandle(
    hDestLog,
    hSourceRange,
    DATE_RANGE_MERGE_FAVOR_DEST
);

if (SUCCEEDED(hr)) {
    // After merge, destination is still in RANGE state
    // Get the last date to verify
    DATE_ITEM_HANDLE hLastDate = GetDateLogLastDate(hDestLog);
    
    // Convert to LOG state with verification
    hr = ConvertToDateLogHandle(
        (DATE_RANGE_HANDLE)hDestLog,
        hLastDate
    );
    
    if (SUCCEEDED(hr)) {
        // Now safe to use as DATE_LOG_HANDLE for database insertion
        printf("Merge complete and verified\n");
    } else {
        printf("Verification failed, data integrity issue\n");
    }
    
    if (hLastDate) {
        FreeDateItem(hLastDate);
    }
}

Verification Without Specific Date

Example 2: Convert Without Last Date Verification
// Convert range to log without verifying a specific date
// This skips the SearchDateItem verification step

HRESULT hr = ConvertToDateLogHandle(
    hDateRange,
    NULL  // No specific date to verify
);

if (SUCCEEDED(hr)) {
    // File was re-parsed successfully
    // Range is now converted to LOG state
} else {
    // File parsing failed
    // Range remains unconverted
}

Error Handling and Retry

Example 3: Robust Conversion with Recovery
DATE_RANGE_HANDLE hRange = NULL;
HRESULT hr = S_OK;
int retryCount = 0;
const int MAX_RETRIES = 3;

while (retryCount < MAX_RETRIES) {
    DATE_ITEM_HANDLE hLastDate = GetDateLogLastDate(hRange);
    
    hr = ConvertToDateLogHandle(hRange, hLastDate);
    
    if (SUCCEEDED(hr)) {
        printf("Conversion successful on attempt %d\n", retryCount + 1);
        break;
    }
    
    if (hLastDate) {
        FreeDateItem(hLastDate);
    }
    
    retryCount++;
    if (retryCount < MAX_RETRIES) {
        printf("Conversion failed, retrying (%d/%d)...\n", retryCount, MAX_RETRIES);
        Sleep(100);  // Brief delay before retry
    }
}

if (FAILED(hr)) {
    printf("Conversion failed after %d attempts\n", MAX_RETRIES);
}

Notes and Important Information

Magic Number Semantics

Critical: The magic number is updated to __DATE_LOG_MAGIC__ ONLY if all verifications succeed. If any step fails, the magic number remains __RANGE_LOG_MAGIC__, allowing the caller to retry the conversion or handle the error gracefully.

File Integrity

The re-parsing of the backing file serves two purposes:

Performance Impact: Re-parsing the entire file can be expensive for large date logs. Consider the performance implications when calling this function frequently.

Temporary Handle Cleanup

The temporary handle created during verification is automatically cleaned up via CloseDateLogHandle(). If cleanup fails:

When to Use Last Date Parameter

Provide the hLastDate parameter in these scenarios:

Set hLastDate to NULL when:

State Transitions

Before Conversion After Successful Conversion After Failed Conversion
Magic: __RANGE_LOG_MAGIC__ Magic: __DATE_LOG_MAGIC__ Magic: __RANGE_LOG_MAGIC__ (unchanged)
Can only use with MergeDateLogHandle Can use as DATE_LOG_HANDLE Must retry or free the handle