STATUS freeKinesisVideoClientInternal()

in src/client/src/Client.c [1453:1572]


STATUS freeKinesisVideoClientInternal(PKinesisVideoClient pKinesisVideoClient, STATUS failStatus)
{
    STATUS retStatus = STATUS_SUCCESS, freeStreamStatus = STATUS_SUCCESS, freeStateMachineStatus = STATUS_SUCCESS, freeHeapStatus = STATUS_SUCCESS;
    UINT32 i = 0;
    BOOL locked = FALSE;

    // Call is idempotent
    CHK(pKinesisVideoClient != NULL && !pKinesisVideoClient->base.shutdown, retStatus);

    pKinesisVideoClient->base.shutdown = TRUE;

    // Lock the streamListLock for iteration
    if (IS_VALID_MUTEX_VALUE(pKinesisVideoClient->base.streamListLock)) {
        pKinesisVideoClient->clientCallbacks.lockMutexFn(pKinesisVideoClient->clientCallbacks.customData, pKinesisVideoClient->base.streamListLock);
        locked = TRUE;
    }

    // Call stream shutdowns first
    for (i = 0; i < pKinesisVideoClient->deviceInfo.streamCount; i++) {
        if (NULL != pKinesisVideoClient->streams[i]) {
            // Call is idempotent so NULL is OK
            shutdownStream(pKinesisVideoClient->streams[i], FALSE);
        }
    }

    // unlock the streamListLock after iteration
    if (locked) {
        pKinesisVideoClient->clientCallbacks.unlockMutexFn(pKinesisVideoClient->clientCallbacks.customData, pKinesisVideoClient->base.streamListLock);
        locked = FALSE;
    }

    // Shutdown client
    pKinesisVideoClient->clientCallbacks.clientShutdownFn(pKinesisVideoClient->clientCallbacks.customData, TO_CLIENT_HANDLE(pKinesisVideoClient));

    // After the shutdown
    semaphoreLock(pKinesisVideoClient->base.shutdownSemaphore);
    semaphoreWaitUntilClear(pKinesisVideoClient->base.shutdownSemaphore, CLIENT_SHUTDOWN_SEMAPHORE_TIMEOUT);

    // Need to stop scheduling callbacks
    // This must happen outside the streamslist lock because the callbacks acquire that lock
    // and shutdown won't return until callbacks have completed so we'll deadlock
    if (IS_VALID_TIMER_QUEUE_HANDLE(pKinesisVideoClient->timerQueueHandle)) {
        timerQueueShutdown(pKinesisVideoClient->timerQueueHandle);
    }

    // Lock the streamListLock for iteration
    if (IS_VALID_MUTEX_VALUE(pKinesisVideoClient->base.streamListLock)) {
        pKinesisVideoClient->clientCallbacks.lockMutexFn(pKinesisVideoClient->clientCallbacks.customData, pKinesisVideoClient->base.streamListLock);
        locked = TRUE;
    }

    // Release the underlying objects
    for (i = 0; i < pKinesisVideoClient->deviceInfo.streamCount; i++) {
        if (NULL != pKinesisVideoClient->streams[i]) {
            // Call is idempotent so NULL is OK
            retStatus = freeStream(pKinesisVideoClient->streams[i]);
            freeStreamStatus = STATUS_FAILED(retStatus) ? retStatus : freeStreamStatus;
        }
    }

    // unlock and free the streamListLock
    if (locked) {
        pKinesisVideoClient->clientCallbacks.unlockMutexFn(pKinesisVideoClient->clientCallbacks.customData, pKinesisVideoClient->base.streamListLock);
        pKinesisVideoClient->clientCallbacks.freeMutexFn(pKinesisVideoClient->clientCallbacks.customData, pKinesisVideoClient->base.streamListLock);
        locked = FALSE;
    }

    // Release the state machine
    freeStateMachineStatus = freeStateMachine(pKinesisVideoClient->base.pStateMachine);

    // Release the condition variable if set
    if (IS_VALID_CVAR_VALUE(pKinesisVideoClient->base.ready)) {
        // Broadcast in any case
        pKinesisVideoClient->clientCallbacks.broadcastConditionVariableFn(pKinesisVideoClient->clientCallbacks.customData,
                                                                          pKinesisVideoClient->base.ready);

        // Free the condition variable
        pKinesisVideoClient->clientCallbacks.freeConditionVariableFn(pKinesisVideoClient->clientCallbacks.customData,
                                                                     pKinesisVideoClient->base.ready);
        pKinesisVideoClient->base.ready = INVALID_CVAR_VALUE;
    }

    if (IS_VALID_MUTEX_VALUE(pKinesisVideoClient->base.lock)) {
        pKinesisVideoClient->clientCallbacks.freeMutexFn(pKinesisVideoClient->clientCallbacks.customData, pKinesisVideoClient->base.lock);
    }

    if (IS_VALID_SEMAPHORE_HANDLE(pKinesisVideoClient->base.shutdownSemaphore)) {
        semaphoreFree(&pKinesisVideoClient->base.shutdownSemaphore);
    }

    if (IS_VALID_TIMER_QUEUE_HANDLE(pKinesisVideoClient->timerQueueHandle)) {
        timerQueueFree(&pKinesisVideoClient->timerQueueHandle);
    }

    // Reset the stored allocators if replaced
    if (STATUS_SUCCEEDED(failStatus) && pKinesisVideoClient->deviceInfo.storageInfo.storageType == DEVICE_STORAGE_TYPE_IN_MEM_CONTENT_STORE_ALLOC) {
        globalMemAlloc = pKinesisVideoClient->storedMemAlloc;
        globalMemAlignAlloc = pKinesisVideoClient->storedMemAlignAlloc;
        globalMemCalloc = pKinesisVideoClient->storedMemCalloc;
        globalMemFree = pKinesisVideoClient->storedMemFree;

        // Reset the singleton instance
        gKinesisVideoClient = NULL;
    }

    // Release the heap
    if (pKinesisVideoClient->pHeap) {
        heapDebugCheckAllocator(pKinesisVideoClient->pHeap, TRUE);
        freeHeapStatus = heapRelease(pKinesisVideoClient->pHeap);
    }

    DLOGD("Total allocated memory %" PRIu64, pKinesisVideoClient->totalAllocationSize);

    // Release the object
    MEMFREE(pKinesisVideoClient);

CleanUp:

    return retStatus | freeStreamStatus | freeHeapStatus | freeStateMachineStatus;
}