int VDIoBackendMemDestroy(PVDIOBACKENDMEM pIoBackend)
{
    ASMAtomicXchgBool(&pIoBackend->fRunning, false);
    vdIoBackendMemThreadPoke(pIoBackend);

    RTThreadWait(pIoBackend->hThreadIo, RT_INDEFINITE_WAIT, NULL);
    RTSemEventDestroy(pIoBackend->EventSem);
    RTCircBufDestroy(pIoBackend->pRequestRing);
    RTMemFree(pIoBackend);

    return VINF_SUCCESS;
}
int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk,
                           VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer,
                           PRTSGBUF pSgBuf, PFNVDIOCOMPLETE pfnComplete, void *pvUser)
{
    PVDIOBACKENDREQ pReq = NULL;
    PPVDIOBACKENDREQ ppReq = NULL;
    size_t cbData;
    unsigned cSegs = 0;

    LogFlowFunc(("Queuing request\n"));

    if (enmTxDir != VDIOTXDIR_FLUSH)
        RTSgBufSegArrayCreate(pSgBuf, NULL, &cSegs, cbTransfer);

    pReq = (PVDIOBACKENDREQ)RTMemAlloc(RT_OFFSETOF(VDIOBACKENDREQ, aSegs[cSegs]));
    if (!pReq)
        return VERR_NO_MEMORY;

    RTCircBufAcquireWriteBlock(pIoBackend->pRequestRing, sizeof(PVDIOBACKENDREQ), (void **)&ppReq, &cbData);
    if (!pReq)
    {
        RTMemFree(pReq);
        return VERR_NO_MEMORY;
    }

    Assert(cbData == sizeof(PVDIOBACKENDREQ));
    pReq->enmTxDir    = enmTxDir;
    pReq->cbTransfer  = cbTransfer;
    pReq->off         = off;
    pReq->pMemDisk    = pMemDisk;
    pReq->pfnComplete = pfnComplete;
    pReq->pvUser      = pvUser;
    if (enmTxDir != VDIOTXDIR_FLUSH)
    {
        RTSgBufSegArrayCreate(pSgBuf, &pReq->aSegs[0], &cSegs, cbTransfer);
        RTSgBufInit(&pReq->SgBuf, pReq->aSegs, cSegs);
    }

    *ppReq = pReq;
    RTCircBufReleaseWriteBlock(pIoBackend->pRequestRing, sizeof(PVDIOBACKENDREQ));
    uint32_t cReqsWaiting = ASMAtomicIncU32(&pIoBackend->cReqsWaiting);
    if (cReqsWaiting == 1)
        vdIoBackendMemThreadPoke(pIoBackend);

    return VINF_SUCCESS;
}
int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk,
                           VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, PCRTSGSEG paSegs,
                           unsigned cSegs, PFNVDIOCOMPLETE pfnComplete, void *pvUser)
{
    PVDIOBACKENDREQ pReq = NULL;
    PPVDIOBACKENDREQ ppReq = NULL;
    size_t cbData;

    LogFlowFunc(("Queuing request\n"));

    pReq = (PVDIOBACKENDREQ)RTMemAlloc(RT_OFFSETOF(VDIOBACKENDREQ, aSegs[cSegs]));
    if (!pReq)
        return VERR_NO_MEMORY;

    RTCircBufAcquireWriteBlock(pIoBackend->pRequestRing, sizeof(PVDIOBACKENDREQ), (void **)&ppReq, &cbData);
    if (!pReq)
    {
        RTMemFree(pReq);
        return VERR_NO_MEMORY;
    }

    Assert(cbData == sizeof(PVDIOBACKENDREQ));
    pReq->enmTxDir    = enmTxDir;
    pReq->cbTransfer  = cbTransfer;
    pReq->off         = off;
    pReq->pMemDisk    = pMemDisk;
    pReq->cSegs       = cSegs;
    pReq->pfnComplete = pfnComplete;
    pReq->pvUser      = pvUser;
    for (unsigned i = 0; i < cSegs; i++)
    {
        pReq->aSegs[i].pvSeg = paSegs[i].pvSeg;
        pReq->aSegs[i].cbSeg = paSegs[i].cbSeg;
    }

    *ppReq = pReq;
    RTCircBufReleaseWriteBlock(pIoBackend->pRequestRing, sizeof(PVDIOBACKENDREQ));
    uint32_t cReqsWaiting = ASMAtomicIncU32(&pIoBackend->cReqsWaiting);
    if (cReqsWaiting == 1)
        vdIoBackendMemThreadPoke(pIoBackend);

    return VINF_SUCCESS;
}