ClRcT clJobQueueStatsGet(ClJobQueueT *hdl, ClJobQueueWalkCallbackT cb, ClPtrT arg, ClJobQueueUsageT *pJobQueueUsage)
{
    ClRcT rc = CL_OK;
    ClJobQueueWalkArgT walkArg = {0};
    if(!hdl || !pJobQueueUsage) return CL_JOBQUEUE_RC(CL_ERR_INVALID_PARAMETER);
    if(!(hdl->flags & CreatedQueue)) return CL_JOBQUEUE_RC(CL_ERR_INVALID_STATE);
    JQ_PFX(hdl);
    rc = clQueueSizeGet(hdl->queue, &pJobQueueUsage->numMsgs);
    if(rc != CL_OK)
    {
        pJobQueueUsage->numMsgs = 0;
        goto out;
    }
    rc = clTaskPoolStatsGet(hdl->pool, &pJobQueueUsage->taskPoolUsage);
    if(rc != CL_OK)
        goto out;

    if(cb)
    {
        walkArg.cb = cb;
        walkArg.cbArg = arg;
        rc = clQueueWalk(hdl->queue, jobQueueWalkCallback, &walkArg);
    }

    out:
    JQ_SFX(hdl);
    return rc;
}
static ClRcT clJobQueuePushConditional(ClJobQueueT* hdl, ClCallbackT job, ClPtrT data, ClBoolT isEmpty)
{
    ClRcT rc = CL_OK;
    ClJobT* jb;

    if(!(hdl->flags & CreatedQueue)) return CL_JOBQUEUE_RC(CL_ERR_INVALID_STATE);

    JQ_PFX(hdl);

    if (!(hdl->flags & Running)) { rc = CL_JOBQUEUE_RC(CL_ERR_INVALID_STATE); goto out; }

    if(isEmpty)
    {
        ClUint32T queueSize = 0;
        (void)clQueueSizeGet(hdl->queue, &queueSize);
        if(queueSize) 
            goto out;
    }

    jb = getJob(hdl, job,data);
  
    if (!jb) { rc = CL_JOBQUEUE_RC(CL_ERR_NO_MEMORY); goto out; }

    rc = clQueueNodeInsert(hdl->queue,jb);

    if (rc != CL_OK) releaseJob(hdl,jb);
    else clTaskPoolWake(hdl->pool); /* Wake any idle pool tasks because there's work on the q (the idle callback handles the dequeuing) */
  
    out:
    JQ_SFX(hdl);
    return rc;
}
/* Handle destructor function. */
static void clDispatchHandleDestructor(void* cbArgs)
{
    ClRcT   rc = CL_OK;
    ClDispatchDbEntryT* thisDbEntry = NULL;
    ClUint32T   queueSize = 0;
    ClDispatchCbQueueDataT*   queueData = NULL;

    if (cbArgs == NULL)
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                ("Handle destructor is called with NULL pointer"));
        return;
    }

    thisDbEntry = (ClDispatchDbEntryT*)cbArgs;

    /* Lock the queue mutex */
    rc = clOsalMutexLock(thisDbEntry->dispatchMutex);
    if (rc != CL_OK)
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                ("Unable to lock dispatch Mutex in Handle destructor callback"));
        return;
    }

    /*
     * Before deleting the queue you need to flush the queue.
     * Even though clQueueDelete will flush the node, we need
     * handle it explicitly so that we can invoke
     * ClDispatchQueueDestroyCallbackT registered during Register
     * which will deallocate the memory for the callback arguments.
     */
    rc = clQueueSizeGet(thisDbEntry->cbQueue, &queueSize);
    if (rc != CL_OK)
    {
        goto proceed_other_functions;
    }

    while (queueSize != 0)
    {
        rc = clQueueNodeDelete(thisDbEntry->cbQueue, (ClQueueDataT*)&queueData);
        if (rc != CL_OK)
        {
            goto proceed_other_functions;
        }
        CL_ASSERT(queueData != NULL);

        /* Invoke the queue destroy callback function */
        thisDbEntry->queueDestroyCallback(queueData->callbackType,
                                          queueData->callbackArgs);

        rc = clQueueSizeGet(thisDbEntry->cbQueue, &queueSize);
        if (rc != CL_OK)
        {
            goto proceed_other_functions;
        }
    }

proceed_other_functions:
    /* 
     * Delete the queue. This will also flush the queue. So all the
     * pending callbacks will be flushed.
     */
    rc = clQueueDelete(&thisDbEntry->cbQueue);
    if (rc != CL_OK)
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                ("Unable to delete the queue. Rc = 0x%x",rc));
    }
        
    /* Delete the pipe */
    errno = 0;
    if ((close(thisDbEntry->readFd)) < 0)
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                ("Unable to close write fd of the pipe:%s",strerror(errno)));
    }

    errno = 0;
    if ((close(thisDbEntry->writeFd)) < 0)
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                ("Unable to close read fd of the pipe:%s",strerror(errno)));
    }


    /* Delete the mutex */
    rc = clOsalMutexUnlock(thisDbEntry->dispatchMutex);
    if (rc != CL_OK)
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                ("Failed to unlock the dispatch mutex"));
    }
    rc = clOsalMutexDelete(thisDbEntry->dispatchMutex);
    if (rc != CL_OK)
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                ("Failed to delete the dispatch mutex"));
    }


    return;
}
/*
 * clDispatchCbDispatch will dequeue the pending callback and invokes the
 * service client specific generic callback. The service client should make
 * sure that it handles the invocation of the associated callback.
 */
ClRcT   clDispatchCbDispatch(
        CL_IN   ClHandleT           dispatchHandle,
        CL_IN   ClDispatchFlagsT     dispatchFlag)
{
    ClRcT   rc = CL_OK;
    ClDispatchDbEntryT* thisDbEntry = NULL;
    ClUint32T   queueSize = 0;
    ClDispatchCbQueueDataT*   queueData = NULL;
    ClCharT ch = pipeNotifyChar;

    if ((dispatchFlag != CL_DISPATCH_ONE) &&
            (dispatchFlag != CL_DISPATCH_ALL) &&
            (dispatchFlag != CL_DISPATCH_BLOCKING))
    {
        return CL_ERR_INVALID_PARAMETER;
    }

    CHECK_LIB_INIT;

    rc = clHandleCheckout(databaseHandle, dispatchHandle, (void *)&thisDbEntry);
    if (rc != CL_OK)
    {
        return CL_ERR_INVALID_HANDLE;
    }
    CL_ASSERT(thisDbEntry != NULL);

    /* Lock the mutex */
    rc = clOsalMutexLock(thisDbEntry->dispatchMutex);
    if (rc != CL_OK)
    {
        goto error_return;
    }

    if (thisDbEntry->shouldDelete == CL_TRUE)
    {
        rc = CL_ERR_INVALID_HANDLE;
        goto error_unlock_return;
    }

    switch (dispatchFlag)
    {
        case CL_DISPATCH_ONE:
            {
                rc = clQueueSizeGet(thisDbEntry->cbQueue, &queueSize);
                if (rc != CL_OK)
                {
                    goto error_unlock_return;
                }

                if (queueSize < 1)
                {
                    /* Dont return any error. So rc = CL_OK */
                    goto error_unlock_return;
                }

                /* Dequeue the node */
                rc = clQueueNodeDelete(thisDbEntry->cbQueue, (ClQueueDataT*)&queueData);
                if (rc != CL_OK)
                {
                    goto error_unlock_return;
                }
                CL_ASSERT(queueData != NULL);

                /* Read a character from the pipe */
                errno = 0;
                if ((read(thisDbEntry->readFd, (void*)&ch, 1)) < 1)
                {
                    CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                            ("read error on pipe: %s",strerror(errno)));
                }
                            
                rc = clOsalMutexUnlock(thisDbEntry->dispatchMutex);
                if (rc != CL_OK)
                {
                    goto error_return;
                }

                /* Invoke the generic callback */
                thisDbEntry->svcCallback(thisDbEntry->svcInstanceHandle,
                                         queueData->callbackType,
                                         queueData->callbackArgs);
                clHeapFree(queueData);
            }
            /* Dont do break, as we have already unlocked the mutex */
            goto error_return;
        case CL_DISPATCH_ALL:
            {
                rc = clQueueSizeGet(thisDbEntry->cbQueue, &queueSize);
                if (rc != CL_OK)
                {
                    goto error_unlock_return;
                }

                while (queueSize != 0)
                {
                    rc = clQueueNodeDelete(thisDbEntry->cbQueue, (ClQueueDataT*)&queueData);
                    if (rc != CL_OK)
                    {
                        goto error_unlock_return;
                    }
                    CL_ASSERT(queueData != NULL);

                    /* Read the byte */
                    errno = 0;
                    if ((read(thisDbEntry->readFd,(void*)&ch,1)) < 1)
                    {
                        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                                ("Read error on the pipe: %s",strerror(errno)));
                    }

                    rc = clOsalMutexUnlock(thisDbEntry->dispatchMutex);
                    if (rc != CL_OK)
                    {
                        goto error_return;
                    }

                    /* Invoke the generic callback */
                    thisDbEntry->svcCallback(thisDbEntry->svcInstanceHandle,
                                             queueData->callbackType,
                                             queueData->callbackArgs);

                    /* Free the queueData */
                    clHeapFree(queueData);

                    /* Get the lock again */
                    rc = clOsalMutexLock(thisDbEntry->dispatchMutex);
                    if (rc != CL_OK)
                    {
                        goto error_return;
                    }

                    if (thisDbEntry->shouldDelete == CL_TRUE)
                    {
                        rc = CL_ERR_INVALID_HANDLE;
                        goto error_unlock_return;
                    }

                    rc = clQueueSizeGet(thisDbEntry->cbQueue, &queueSize);
                    if (rc != CL_OK)
                    {
                        goto error_unlock_return;
                    }
                } /* End of while */

            }
            /* Just break, as we need to unlock and return */
            break;
        case CL_DISPATCH_BLOCKING:
            {
                while (1)
                {
                    rc = clOsalMutexUnlock(thisDbEntry->dispatchMutex);
                    if (rc != CL_OK)
                    {
                        goto error_return;
                    }

                    /* Block on the readFd */
                    errno = 0;
                    if ((read(thisDbEntry->readFd,(void*)&ch,1)) < 1)
                    {
                        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                                ("Read error on the pipe: %s",strerror(errno)));
                        /* 
                         * This might be because finalize has been done and write fd
                         * is closed. So return
                         */
                        goto error_return;
                    }

                    rc = clOsalMutexLock(thisDbEntry->dispatchMutex);
                    if (rc != CL_OK)
                    {
                        goto error_return;
                    }

                    if (thisDbEntry->shouldDelete == CL_TRUE)
                    {
                        rc = CL_OK;
                        goto error_unlock_return;
                    }

                    rc = clQueueSizeGet(thisDbEntry->cbQueue, &queueSize);
                    if (rc != CL_OK)
                    {
                        goto error_unlock_return;
                    }

                    if (queueSize < 1)
                    {
                        /* 
                         * This would happen when read has come out due
                         * to signal
                         */
                        continue;
                    }

                    /* Dequeue the node */
                    rc = clQueueNodeDelete(thisDbEntry->cbQueue, (ClQueueDataT*)&queueData);
                    if (rc != CL_OK)
                    {
                        goto error_unlock_return;
                    }
                    CL_ASSERT(queueData != NULL);

                    rc = clOsalMutexUnlock(thisDbEntry->dispatchMutex);
                    if (rc != CL_OK)
                    {
                        goto error_return;
                    }

                    /* Invoke the generic callback */
                    thisDbEntry->svcCallback(thisDbEntry->svcInstanceHandle,
                            queueData->callbackType,
                            queueData->callbackArgs);

                    /* Free the queueData */
                    clHeapFree(queueData);

                    /* Get the lock again */
                    rc = clOsalMutexLock(thisDbEntry->dispatchMutex);
                    if (rc != CL_OK)
                    {
                        goto error_return;
                    }

                    if (thisDbEntry->shouldDelete == CL_TRUE)
                    {
                        rc = CL_ERR_INVALID_HANDLE;
                        goto error_unlock_return;
                    }
                }
                break;
            }
    } /* End of switch */

error_unlock_return:
    if ((clOsalMutexUnlock(thisDbEntry->dispatchMutex)) != CL_OK)
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                ("Mutex Unlock failed\n"));
    }

error_return:
    if ((clHandleCheckin(databaseHandle, dispatchHandle)) != CL_OK)
    {
        CL_DEBUG_PRINT(CL_DEBUG_ERROR,
                ("clHandleCheckin failed"));
    }

    return rc;
}