Index: /trunk/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp	(revision 29442)
+++ /trunk/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp	(revision 29443)
@@ -61,15 +61,19 @@
     DRVDISKAIOTXDIR enmTxDir;
     /** Start offset. */
-    uint64_t  off;
+    uint64_t        off;
     /** Transfer size. */
-    size_t    cbTransfer;
+    size_t          cbTransfer;
     /** Segment array. */
-    PCRTSGSEG paSeg;
+    PCRTSGSEG       paSeg;
     /** Number of array entries. */
-    unsigned  cSeg;
+    unsigned        cSeg;
     /** User argument */
-    void     *pvUser;
+    void           *pvUser;
     /** Slot in the array. */
-    unsigned  iSlot;
+    unsigned        iSlot;
+    /** Start timestamp */
+    uint64_t        tsStart;
+    /** Completion timestamp. */
+    uint64_t        tsComplete;
 } DRVDISKAIOREQ, *PDRVDISKAIOREQ;
 
@@ -162,4 +166,13 @@
     /** Next free slot in the array */
     volatile unsigned       iNextFreeSlot;
+
+    /** Flag whether we check for requests completing twice. */
+    bool                    fCheckDoubleCompletion;
+    /** Number of requests we go back. */
+    unsigned                cEntries;
+    /** Array of completed but still observed requests. */
+    PDRVDISKAIOREQ          *papIoReq;
+    /** Current entry in the array. */
+    unsigned                iEntry;
 } DRVDISKINTEGRITY, *PDRVDISKINTEGRITY;
 
@@ -189,7 +202,48 @@
         pIoReq->cSeg       = cSeg;
         pIoReq->pvUser     = pvUser;
+        pIoReq->iSlot      = 0;
+        pIoReq->tsStart    = RTTimeSystemMilliTS();
+        pIoReq->tsComplete = 0;
     }
 
     return pIoReq;
+}
+
+/**
+ * Free a async I/O request.
+ *
+ * @returns nothing.
+ * @param   pThis     Disk driver.
+ * @param   pIoReq    The I/O request to free.
+ */
+static void drvdiskintIoReqFree(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
+{
+    if (pThis->fCheckDoubleCompletion)
+    {
+        /* Search if the I/O request completed already. */
+        for (unsigned i = 0; i < pThis->cEntries; i++)
+        {
+            if (RT_UNLIKELY(pThis->papIoReq[i] == pIoReq))
+            {
+                RTMsgError("Request %#p completed already!\n", pIoReq);
+                RTMsgError("Start timestamp %llu Completion timestamp %llu (completed after %llu ms)\n",
+                           pIoReq->tsStart, pIoReq->tsComplete, pIoReq->tsComplete - pIoReq->tsStart);
+                RTAssertDebugBreak();
+            }
+        }
+
+        pIoReq->tsComplete = RTTimeSystemMilliTS();
+        Assert(!pThis->papIoReq[pThis->iEntry]);
+        pThis->papIoReq[pThis->iEntry] = pIoReq;
+
+        pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
+        if (pThis->papIoReq[pThis->iEntry])
+        {
+            RTMemFree(pThis->papIoReq[pThis->iEntry]);
+            pThis->papIoReq[pThis->iEntry] = NULL;
+        }
+    }
+    else
+        RTMemFree(pIoReq);
 }
 
@@ -414,5 +468,5 @@
 
     Assert(!pReqActive->pIoReq);
-    pReqActive->tsStart = RTTimeSystemMilliTS();
+    pReqActive->tsStart = pIoReq->tsStart;
     pReqActive->pIoReq  = pIoReq;
     pIoReq->iSlot = pThis->iNextFreeSlot;
@@ -705,6 +759,9 @@
     }
 
-    rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pIoReq->pvUser, rcReq);
-    RTMemFree(pIoReq);
+    void *pvUserComplete = pIoReq->pvUser;
+
+    drvdiskintIoReqFree(pThis, pIoReq);
+
+    rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pvUserComplete, rcReq);
 
     return rc;
@@ -758,4 +815,15 @@
         RTSemEventSignal(pThis->SemEvent);
         RTSemEventDestroy(pThis->SemEvent);
+    }
+
+    if (pThis->fCheckDoubleCompletion)
+    {
+        /* Free all requests */
+        while (pThis->papIoReq[pThis->iEntry])
+        {
+            RTMemFree(pThis->papIoReq[pThis->iEntry]);
+            pThis->papIoReq[pThis->iEntry] = NULL;
+            pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
+        }
     }
 }
@@ -779,5 +847,7 @@
                                     "TraceRequests\0"
                                     "CheckIntervalMs\0"
-                                    "ExpireIntervalMs\0"))
+                                    "ExpireIntervalMs\0"
+                                    "CheckDoubleCompletions\0"
+                                    "HistorySize\0"))
         return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
 
@@ -789,4 +859,8 @@
     AssertRC(rc);
     rc = CFGMR3QueryU32Def(pCfg, "ExpireIntervalMs", &pThis->uExpireIntervalMs, 20000); /* 20 seconds */
+    AssertRC(rc);
+    rc = CFGMR3QueryBoolDef(pCfg, "CheckDoubleCompletions", &pThis->fCheckDoubleCompletion, false);
+    AssertRC(rc);
+    rc = CFGMR3QueryU32Def(pCfg, "HistorySize", &pThis->cEntries, 512);
     AssertRC(rc);
 
@@ -863,4 +937,11 @@
                             0, RTTHREADTYPE_INFREQUENT_POLLER, 0, "DiskIntegrity");
         AssertRC(rc);
+    }
+
+    if (pThis->fCheckDoubleCompletion)
+    {
+        pThis->iEntry = 0;
+        pThis->papIoReq = (PDRVDISKAIOREQ *)RTMemAllocZ(pThis->cEntries * sizeof(PDRVDISKAIOREQ));
+        AssertPtr(pThis->papIoReq);
     }
 
