Index: trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.cpp
===================================================================
--- trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.cpp	(revision 92733)
+++ trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.cpp	(revision 92735)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2006-2020 Oracle Corporation
+ * Copyright (C) 2006-2021 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -160,12 +160,13 @@
         /* The IDataObject *must* be created on the same thread as our (proxy) window, so post a message to it
          * to do the stuff for us. */
-        const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pTransfer->Events);
-        if (idEvent != NIL_SHCLEVENTID)
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pTransfer->Events, &pEvent);
+        if (RT_SUCCESS(rc))
         {
             /* Don't want to rely on SendMessage (synchronous) here, so just post and wait the event getting signalled. */
-            ::PostMessage(pCtx->Win.hWnd, SHCL_WIN_WM_TRANSFER_START, (WPARAM)pTransfer, (LPARAM)idEvent);
+            ::PostMessage(pCtx->Win.hWnd, SHCL_WIN_WM_TRANSFER_START, (WPARAM)pTransfer, (LPARAM)pEvent->idEvent);
 
             PSHCLEVENTPAYLOAD pPayload;
-            rc = ShClEventWait(&pTransfer->Events, idEvent, 30 * 1000 /* Timeout in ms */, &pPayload);
+            rc = ShClEventWait(pEvent, 30 * 1000 /* Timeout in ms */, &pPayload);
             if (RT_SUCCESS(rc))
             {
@@ -176,5 +177,5 @@
             }
 
-            ShClEventUnregister(&pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
         }
         else
Index: trunk/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp
===================================================================
--- trunk/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp	(revision 92733)
+++ trunk/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp	(revision 92735)
@@ -7,5 +7,5 @@
  * Includes contributions from François Revol
  *
- * Copyright (C) 2006-2020 Oracle Corporation
+ * Copyright (C) 2006-2021 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -38,4 +38,7 @@
 *   Prototypes                                                                                                                   *
 *********************************************************************************************************************************/
+static void shClEventSourceResetInternal(PSHCLEVENTSOURCE pSource);
+
+static void shClEventDestroy(PSHCLEVENT pEvent);
 DECLINLINE(PSHCLEVENT) shclEventGet(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent);
 
@@ -103,30 +106,4 @@
 
 /**
- * Destroys an event, but doesn't free the memory.
- *
- * @param   pEvent              Event to destroy.
- */
-static void shClEventTerm(PSHCLEVENT pEvent)
-{
-    if (!pEvent)
-        return;
-
-    AssertMsgReturnVoid(pEvent->cRefs == 0, ("Event %RU32 still has %RU32 references\n",
-                                             pEvent->idEvent, pEvent->cRefs));
-
-    LogFlowFunc(("Event %RU32\n", pEvent->idEvent));
-
-    if (pEvent->hEvtMulSem != NIL_RTSEMEVENT)
-    {
-        RTSemEventMultiDestroy(pEvent->hEvtMulSem);
-        pEvent->hEvtMulSem = NIL_RTSEMEVENT;
-    }
-
-    ShClPayloadFree(pEvent->pPayload);
-
-    pEvent->idEvent = 0;
-}
-
-/**
  * Creates a new event source.
  *
@@ -140,4 +117,7 @@
     AssertPtrReturn(pSource, VERR_INVALID_POINTER);
 
+    int rc = RTCritSectInit(&pSource->CritSect);
+    AssertRCReturn(rc, rc);
+
     RTListInit(&pSource->lstEvents);
 
@@ -152,25 +132,37 @@
  * Destroys an event source.
  *
+ * @returns VBox status code.
  * @param   pSource             Event source to destroy.
  */
-void ShClEventSourceDestroy(PSHCLEVENTSOURCE pSource)
+int ShClEventSourceDestroy(PSHCLEVENTSOURCE pSource)
 {
     if (!pSource)
-        return;
+        return VINF_SUCCESS;
 
     LogFlowFunc(("ID=%RU32\n", pSource->uID));
 
-    ShClEventSourceReset(pSource);
-
-    pSource->uID          = UINT16_MAX;
-    pSource->idNextEvent  = UINT32_MAX;
-}
-
-/**
- * Resets an event source.
+    int rc = RTCritSectEnter(&pSource->CritSect);
+    if (RT_SUCCESS(rc))
+    {
+        shClEventSourceResetInternal(pSource);
+
+        rc = RTCritSectLeave(&pSource->CritSect);
+        AssertRC(rc);
+
+        RTCritSectDelete(&pSource->CritSect);
+
+        pSource->uID          = UINT16_MAX;
+        pSource->idNextEvent  = UINT32_MAX;
+    }
+
+    return rc;
+}
+
+/**
+ * Resets an event source, internal version.
  *
  * @param   pSource             Event source to reset.
  */
-void ShClEventSourceReset(PSHCLEVENTSOURCE pSource)
+static void shClEventSourceResetInternal(PSHCLEVENTSOURCE pSource)
 {
     LogFlowFunc(("ID=%RU32\n", pSource->uID));
@@ -182,5 +174,5 @@
         RTListNodeRemove(&pEvIt->Node);
 
-        shClEventTerm(pEvIt);
+        shClEventDestroy(pEvIt);
 
         RTMemFree(pEvIt);
@@ -190,42 +182,73 @@
 
 /**
+ * Resets an event source.
+ *
+ * @param   pSource             Event source to reset.
+ */
+void ShClEventSourceReset(PSHCLEVENTSOURCE pSource)
+{
+    int rc2 = RTCritSectEnter(&pSource->CritSect);
+    if (RT_SUCCESS(rc2))
+    {
+        shClEventSourceResetInternal(pSource);
+
+        rc2 = RTCritSectLeave(&pSource->CritSect);
+        AssertRC(rc2);
+    }
+}
+
+/**
  * Generates a new event ID for a specific event source and registers it.
  *
- * @returns New event ID generated, or NIL_SHCLEVENTID on error.
+ * @returns VBox status code.
  * @param   pSource             Event source to generate event for.
- */
-SHCLEVENTID ShClEventIdGenerateAndRegister(PSHCLEVENTSOURCE pSource)
-{
-    AssertPtrReturn(pSource, NIL_SHCLEVENTID);
-
-    /*
-     * Allocate an event.
-     */
+ * @param   ppEvent             Where to return the new event generated on success.
+ */
+int ShClEventSourceGenerateAndRegisterEvent(PSHCLEVENTSOURCE pSource, PSHCLEVENT *ppEvent)
+{
+    AssertPtrReturn(pSource, VERR_INVALID_POINTER);
+    AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
+
     PSHCLEVENT pEvent = (PSHCLEVENT)RTMemAllocZ(sizeof(SHCLEVENT));
-    AssertReturn(pEvent, NIL_SHCLEVENTID);
+    AssertReturn(pEvent, VERR_NO_MEMORY);
     int rc = RTSemEventMultiCreate(&pEvent->hEvtMulSem);
-    AssertRCReturnStmt(rc, RTMemFree(pEvent), NIL_SHCLEVENTID);
-
-    /*
-     * Allocate an unique event ID.
-     */
-    for (uint32_t cTries = 0;; cTries++)
-    {
-        SHCLEVENTID idEvent = ++pSource->idNextEvent;
-        if (idEvent < VBOX_SHCL_MAX_EVENTS)
-        { /* likely */ }
-        else
-            pSource->idNextEvent = idEvent = 1; /* zero == error, remember! */
-
-        if (shclEventGet(pSource, idEvent) == NULL)
-        {
-            pEvent->idEvent = idEvent;
-            RTListAppend(&pSource->lstEvents, &pEvent->Node);
-
-            LogFlowFunc(("uSource=%RU16: New event: %#x\n", pSource->uID, idEvent));
-            return idEvent;
-        }
-
-        AssertBreak(cTries < 4096);
+    if (RT_SUCCESS(rc))
+    {
+        rc = RTCritSectEnter(&pSource->CritSect);
+        if (RT_SUCCESS(rc))
+        {
+            /*
+             * Allocate an unique event ID.
+             */
+            for (uint32_t cTries = 0;; cTries++)
+            {
+                SHCLEVENTID idEvent = ++pSource->idNextEvent;
+                if (idEvent < VBOX_SHCL_MAX_EVENTS)
+                { /* likely */ }
+                else
+                    pSource->idNextEvent = idEvent = 1; /* zero == error, remember! */
+
+                if (shclEventGet(pSource, idEvent) == NULL)
+                {
+                    pEvent->pParent = pSource;
+                    pEvent->idEvent = idEvent;
+                    RTListAppend(&pSource->lstEvents, &pEvent->Node);
+
+                    rc = RTCritSectLeave(&pSource->CritSect);
+                    AssertRC(rc);
+
+                    LogFlowFunc(("uSource=%RU16: New event: %#x\n", pSource->uID, idEvent));
+
+                    *ppEvent = pEvent;
+
+                    return VINF_SUCCESS;
+                }
+
+                AssertBreak(cTries < 4096);
+            }
+
+            rc = RTCritSectLeave(&pSource->CritSect);
+            AssertRC(rc);
+        }
     }
 
@@ -235,5 +258,62 @@
     pEvent->hEvtMulSem = NIL_RTSEMEVENTMULTI;
     RTMemFree(pEvent);
-    return NIL_SHCLEVENTID;
+    return rc;
+}
+
+/**
+ * Destroys an event.
+ *
+ * @param   pEvent              Event to destroy.
+ */
+static void shClEventDestroy(PSHCLEVENT pEvent)
+{
+    if (!pEvent)
+        return;
+
+    AssertMsgReturnVoid(pEvent->cRefs == 0, ("Event %RU32 still has %RU32 references\n",
+                                             pEvent->idEvent, pEvent->cRefs));
+
+    LogFlowFunc(("Event %RU32\n", pEvent->idEvent));
+
+    if (pEvent->hEvtMulSem != NIL_RTSEMEVENT)
+    {
+        RTSemEventMultiDestroy(pEvent->hEvtMulSem);
+        pEvent->hEvtMulSem = NIL_RTSEMEVENT;
+    }
+
+    ShClPayloadFree(pEvent->pPayload);
+
+    pEvent->idEvent = NIL_SHCLEVENTID;
+}
+
+/**
+ * Unregisters an event.
+ *
+ * @returns VBox status code.
+ * @param   pSource             Event source to unregister event for.
+ * @param   pEvent              Event to unregister. On success the pointer will be invalid.
+ */
+static int shClEventSourceUnregisterEventInternal(PSHCLEVENTSOURCE pSource, PSHCLEVENT pEvent)
+{
+    LogFlowFunc(("idEvent=%RU32, cRefs=%RU32\n", pEvent->idEvent, pEvent->cRefs));
+
+    AssertReturn(pEvent->cRefs == 0, VERR_WRONG_ORDER);
+
+    int rc = RTCritSectEnter(&pSource->CritSect);
+    if (RT_SUCCESS(rc))
+    {
+        RTListNodeRemove(&pEvent->Node);
+
+        shClEventDestroy(pEvent);
+
+        rc = RTCritSectLeave(&pSource->CritSect);
+        if (RT_SUCCESS(rc))
+        {
+            RTMemFree(pEvent);
+            pEvent = NULL;
+        }
+    }
+
+    return rc;
 }
 
@@ -262,9 +342,22 @@
  * @returns Pointer to event if found, or NULL if not found.
  * @param   pSource             Event source to get event from.
- * @param   uID                 Event ID to get.
- */
-PSHCLEVENT ShClEventGet(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent)
-{
-    return shclEventGet(pSource, idEvent);
+ * @param   idEvent             ID of event to return.
+ */
+PSHCLEVENT ShClEventSourceGetFromId(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent)
+{
+    AssertPtrReturn(pSource, NULL);
+
+    int rc = RTCritSectEnter(&pSource->CritSect);
+    if (RT_SUCCESS(rc))
+    {
+         PSHCLEVENT pEvent = shclEventGet(pSource, idEvent);
+
+         rc = RTCritSectLeave(&pSource->CritSect);
+         AssertRC(rc);
+
+         return pEvent;
+    }
+
+    return NULL;
 }
 
@@ -272,15 +365,23 @@
  * Returns the last (newest) event ID which has been registered for an event source.
  *
- * @returns Last registered event ID, or 0 if not found.
+ * @returns Pointer to last registered event, or NULL if not found.
  * @param   pSource             Event source to get last registered event from.
  */
-SHCLEVENTID ShClEventGetLast(PSHCLEVENTSOURCE pSource)
-{
-    AssertPtrReturn(pSource, 0);
-    PSHCLEVENT pEvent = RTListGetLast(&pSource->lstEvents, SHCLEVENT, Node);
-    if (pEvent)
-        return pEvent->idEvent;
-
-    return 0;
+PSHCLEVENT ShClEventSourceGetLast(PSHCLEVENTSOURCE pSource)
+{
+    AssertPtrReturn(pSource, NULL);
+
+    int rc = RTCritSectEnter(&pSource->CritSect);
+    if (RT_SUCCESS(rc))
+    {
+        PSHCLEVENT pEvent = RTListGetLast(&pSource->lstEvents, SHCLEVENT, Node);
+
+        rc = RTCritSectLeave(&pSource->CritSect);
+        AssertRC(rc);
+
+        return pEvent;
+    }
+
+    return NULL;
 }
 
@@ -292,12 +393,9 @@
  * @param   idEvent             Event ID to return reference count for.
  */
-uint32_t ShClEventGetRefs(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent)
-{
-    PSHCLEVENT pEvent = shclEventGet(pSource, idEvent);
-    if (pEvent)
-        return pEvent->cRefs;
-
-    AssertMsgFailed(("No event with %RU32\n", idEvent));
-    return 0;
+uint32_t ShClEventGetRefs(PSHCLEVENT pEvent)
+{
+    AssertPtrReturn(pEvent, 0);
+
+    return ASMAtomicReadU32(&pEvent->cRefs);
 }
 
@@ -322,91 +420,30 @@
 
 /**
- * Unregisters an event.
+ * Waits for an event to get signalled.
  *
  * @returns VBox status code.
- * @param   pSource             Event source to unregister event for.
- * @param   uID                 Event ID to unregister.
- *
- * @todo    r=bird: The caller must enter crit sect protecting the event source
- *          here, must it?  See explanation in ShClEventWait.
- */
-int ShClEventUnregister(PSHCLEVENTSOURCE pSource, SHCLEVENTID uID)
-{
-    AssertPtrReturn(pSource, VERR_INVALID_POINTER);
-
-    int rc;
-
-    LogFlowFunc(("uSource=%RU16, uEvent=%RU32\n", pSource->uID, uID));
-
-    PSHCLEVENT pEvent = shclEventGet(pSource, uID);
-    if (pEvent)
-    {
-        LogFlowFunc(("Event %RU32\n", pEvent->idEvent));
-
-        RTListNodeRemove(&pEvent->Node);
-
-        shClEventTerm(pEvent);
-
-        RTMemFree(pEvent);
-        pEvent = NULL;
-
-        rc = VINF_SUCCESS;
-    }
-    else
-        rc = VERR_NOT_FOUND;
-
-    LogFlowFuncLeaveRC(rc);
-    return rc;
-}
-
-/**
- * Waits for an event to get signalled.
- *
- * @returns VBox status code.
- * @param   pSource             Event source that contains the event to wait for.
- * @param   uID                 Event ID to wait for.
+ * @param   pEvent              Event to wait for.
  * @param   uTimeoutMs          Timeout (in ms) to wait.
  * @param   ppPayload           Where to store the (allocated) event payload on success. Needs to be free'd with
  *                              SharedClipboardPayloadFree(). Optional.
- *
- * @todo    r=bird: Locking protocol is totally buggered here, or at least not
- *          explained in any way whatsoever.  We cannot really do shclEventGet
- *          and shclEventPayloadDetachInternal w/o holding the critical section
- *          protecting that list, otherwise wtf is the point of the locking.
- *
- *          More to the point, ShClEventSignal is called on the HGCM service
- *          thread and would be racing the host clipboard worker thread/whomever
- *          sent the request and is making this call closely followed by
- *          ShClEventRelease and ShClEventUnregister.  The waiting here might be
- *          safe if there can only ever be one single thread making these kind
- *          of requests, but the unregistering is _not_ safe w/o holding the
- *          lock and that isn't explained anywhere that I can see.
- */
-int ShClEventWait(PSHCLEVENTSOURCE pSource, SHCLEVENTID uID, RTMSINTERVAL uTimeoutMs, PSHCLEVENTPAYLOAD *ppPayload)
-{
-    AssertPtrReturn(pSource, VERR_INVALID_POINTER);
+ */
+int ShClEventWait(PSHCLEVENT pEvent, RTMSINTERVAL uTimeoutMs, PSHCLEVENTPAYLOAD *ppPayload)
+{
+    AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
     AssertPtrNullReturn(ppPayload, VERR_INVALID_POINTER);
     LogFlowFuncEnter();
 
-    int rc;
-
-    PSHCLEVENT pEvent = shclEventGet(pSource, uID);
-    if (pEvent)
-    {
-        rc = RTSemEventMultiWait(pEvent->hEvtMulSem, uTimeoutMs);
-        if (RT_SUCCESS(rc))
-        {
-            if (ppPayload)
-            {
-                /* Make sure to detach payload here, as the caller now owns the data. */
-                *ppPayload = shclEventPayloadDetachInternal(pEvent);
-            }
-        }
-
-        if (RT_FAILURE(rc))
-            LogRel2(("Shared Clipboard: Waiting for event %RU32 failed, rc=%Rrc\n", uID, rc));
-    }
-    else
-        rc = VERR_NOT_FOUND;
+    int rc = RTSemEventMultiWait(pEvent->hEvtMulSem, uTimeoutMs);
+    if (RT_SUCCESS(rc))
+    {
+        if (ppPayload)
+        {
+            /* Make sure to detach payload here, as the caller now owns the data. */
+            *ppPayload = shclEventPayloadDetachInternal(pEvent);
+        }
+    }
+
+    if (RT_FAILURE(rc))
+        LogRel2(("Shared Clipboard: Waiting for event %RU32 failed, rc=%Rrc\n", pEvent->idEvent, rc));
 
     LogFlowFuncLeaveRC(rc);
@@ -418,56 +455,39 @@
  *
  * @returns New reference count, or UINT32_MAX if failed.
- * @param   pSource             Event source of event to retain.
- * @param   idEvent             ID of event to retain.
- */
-uint32_t ShClEventRetain(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent)
-{
-    PSHCLEVENT pEvent = shclEventGet(pSource, idEvent);
+ * @param   pEvent              Event to retain.
+ */
+uint32_t ShClEventRetain(PSHCLEVENT pEvent)
+{
+    AssertPtrReturn(pEvent, UINT32_MAX);
+    AssertReturn(ASMAtomicReadU32(&pEvent->cRefs) < 64, UINT32_MAX);
+    return ASMAtomicIncU32(&pEvent->cRefs);
+}
+
+/**
+ * Releases an event by decreasing its reference count.
+ *
+ * @returns New reference count, or UINT32_MAX if failed.
+ * @param   pEvent              Event to release.
+ *                              If the reference count reaches 0, the event will
+ *                              be destroyed and \a pEvent will be invalid.
+ */
+uint32_t ShClEventRelease(PSHCLEVENT pEvent)
+{
     if (!pEvent)
-    {
-        AssertFailed();
-        return UINT32_MAX;
-    }
-
-    AssertReturn(pEvent->cRefs < 64, UINT32_MAX); /* Sanity. Yeah, not atomic. */
-
-    return ASMAtomicIncU32(&pEvent->cRefs);
-}
-
-/**
- * Releases an event by decreasing its reference count.
- *
- * @returns New reference count, or UINT32_MAX if failed.
- * @param   pSource             Event source of event to release.
- * @param   idEvent             ID of event to release.
- *
- * @todo    r=bird: The two places this is currently used make my head explode.
- *          First you release then you unregister it.  Unregister frees is
- *          unconditionally.
- *
- *          Any sane thinking person would assume that after we've release our
- *          reference to the @a idEvent, we cannot touch it any more because we
- *          don't have a valid reference any more.  So, when ShClEventUnregister
- *          is then called and unconditionally frees the event: Boooooom!
- *
- *          The locking interface here is generally not sane either, as the
- *          locking isn't done on the @a pSource but something the caller needs
- *          implements.  Since we don't have access to the lock we cannot verify
- *          the locking protocol nor can we implement it (like in
- *          ShClEventWait).
- *
- *          A better interface would return PSHCLEVENT instead of SHCLEVENTID,
- *          and then you could work directly on that as an object, rather via a
- *          slow object list.  It would eliminate shclEventGet and any though
- *          that someone might be updating the list while shclEventGet traverses
- *          it.
- */
-uint32_t ShClEventRelease(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent)
-{
-    PSHCLEVENT pEvent = shclEventGet(pSource, idEvent);
-    AssertReturn(pEvent, UINT32_MAX);
-    AssertReturn(pEvent->cRefs, UINT32_MAX); /* Sanity. Yeah, not atomic. */
-
-    return ASMAtomicDecU32(&pEvent->cRefs);
+        return 0;
+
+    AssertReturn(ASMAtomicReadU32(&pEvent->cRefs) > 0, UINT32_MAX);
+
+    uint32_t const cRefs = ASMAtomicDecU32(&pEvent->cRefs);
+    if (cRefs == 0)
+    {
+        AssertPtr(pEvent->pParent);
+        int rc2 = shClEventSourceUnregisterEventInternal(pEvent->pParent, pEvent);
+        AssertRC(rc2);
+
+        return RT_SUCCESS(rc2) ? 0 : UINT32_MAX;
+    }
+
+    return cRefs;
 }
 
@@ -476,32 +496,19 @@
  *
  * @returns VBox status code.
- * @param   pSource             Event source of event to signal.
- * @param   uID                 Event ID to signal.
+ * @param   pEvent              Event to signal.
  * @param   pPayload            Event payload to associate. Takes ownership on
  *                              success. Optional.
- *
- * @note    Caller must enter crit sect protecting the event source!
- */
-int ShClEventSignal(PSHCLEVENTSOURCE pSource, SHCLEVENTID uID, PSHCLEVENTPAYLOAD pPayload)
-{
-    AssertPtrReturn(pSource, VERR_INVALID_POINTER);
-
-    int rc;
-
-    LogFlowFunc(("uSource=%RU16, uEvent=%RU32\n", pSource->uID, uID));
-
-    PSHCLEVENT pEvent = shclEventGet(pSource, uID);
-    if (pEvent)
-    {
-        Assert(pEvent->pPayload == NULL);
-
-        pEvent->pPayload = pPayload;
-
-        rc = RTSemEventMultiSignal(pEvent->hEvtMulSem);
-        if (RT_FAILURE(rc))
-            pEvent->pPayload = NULL; /* (no race condition if consumer also enters the critical section) */
-    }
-    else
-        rc = VERR_NOT_FOUND;
+ */
+int ShClEventSignal(PSHCLEVENT pEvent, PSHCLEVENTPAYLOAD pPayload)
+{
+    AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
+
+    Assert(pEvent->pPayload == NULL);
+
+    pEvent->pPayload = pPayload;
+
+    int rc = RTSemEventMultiSignal(pEvent->hEvtMulSem);
+    if (RT_FAILURE(rc))
+        pEvent->pPayload = NULL; /* (no race condition if consumer also enters the critical section) */
 
     LogFlowFuncLeaveRC(rc);
Index: trunk/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp
===================================================================
--- trunk/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp	(revision 92733)
+++ trunk/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp	(revision 92735)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2019-2020 Oracle Corporation
+ * Copyright (C) 2019-2021 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -1079,5 +1079,5 @@
 
 /**
- * Duplicaates a transfer object data chunk.
+ * Duplicates a transfer object data chunk.
  *
  * @returns Duplicated object data chunk on success, or NULL on failure.
@@ -1158,9 +1158,6 @@
     LogFlowFuncEnter();
 
-    PSHCLTRANSFER pTransfer = (PSHCLTRANSFER)RTMemAlloc(sizeof(SHCLTRANSFER));
-    if (!pTransfer)
-        return VERR_NO_MEMORY;
-
-    int rc = VINF_SUCCESS;
+    PSHCLTRANSFER pTransfer = (PSHCLTRANSFER)RTMemAllocZ(sizeof(SHCLTRANSFER));
+    AssertPtrReturn(pTransfer, VERR_NO_MEMORY);
 
     pTransfer->State.uID       = 0;
@@ -1181,5 +1178,7 @@
     pTransfer->uTimeoutMs     = RT_MS_30SEC;
 #endif
-    pTransfer->cbMaxChunkSize = _64K; /** @todo Make this configurable. */
+    pTransfer->cbMaxChunkSize  = _64K; /** @todo Make this configurable. */
+    pTransfer->cMaxListHandles = _4K;  /** @todo Ditto. */
+    pTransfer->cMaxObjHandles  = _4K;  /** @todo Ditto. */
 
     pTransfer->pvUser = NULL;
@@ -1192,8 +1191,5 @@
     RTListInit(&pTransfer->lstRoots);
 
-    RTListInit(&pTransfer->Events.lstEvents);
-    pTransfer->Events.uID          = 0;
-    pTransfer->Events.idNextEvent  = 1;
-
+    int rc = ShClEventSourceCreate(&pTransfer->Events, 0 /* uID */);
     if (RT_SUCCESS(rc))
     {
@@ -1248,5 +1244,4 @@
 int ShClTransferInit(PSHCLTRANSFER pTransfer, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource)
 {
-    pTransfer->State.uID       = 0;
     pTransfer->State.enmDir    = enmDir;
     pTransfer->State.enmSource = enmSource;
@@ -1255,20 +1250,16 @@
                  pTransfer->State.uID, pTransfer->State.enmDir, pTransfer->State.enmSource));
 
-    int rc = ShClEventSourceCreate(&pTransfer->Events, pTransfer->State.uID);
-    if (RT_SUCCESS(rc))
-    {
-        pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_INITIALIZED; /* Now we're ready to run. */
-
-        pTransfer->cListHandles    = 0;
-        pTransfer->cMaxListHandles = _4K; /** @todo Make this dynamic. */
-        pTransfer->uListHandleNext = 1;
-
-        pTransfer->cObjHandles     = 0;
-        pTransfer->cMaxObjHandles  = _4K; /** @todo Ditto. */
-        pTransfer->uObjHandleNext  = 1;
-
-        if (pTransfer->Callbacks.pfnOnInitialize)
-            rc = pTransfer->Callbacks.pfnOnInitialize(&pTransfer->CallbackCtx);
-    }
+    pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_INITIALIZED; /* Now we're ready to run. */
+
+    pTransfer->cListHandles    = 0;
+    pTransfer->uListHandleNext = 1;
+
+    pTransfer->cObjHandles     = 0;
+    pTransfer->uObjHandleNext  = 1;
+
+    int rc = VINF_SUCCESS;
+
+    if (pTransfer->Callbacks.pfnOnInitialize)
+        rc = pTransfer->Callbacks.pfnOnInitialize(&pTransfer->CallbackCtx);
 
     LogFlowFuncLeaveRC(rc);
@@ -3048,8 +3039,8 @@
 #ifdef RT_OS_WINDOWS
             if ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR) != SHCL_OBJ_CF_ACCESS_ATTR_NONE)
-                fOpen |= RTFILE_O_ATTR_ONLY;
+                fOpen |= RTFILE_O_OPEN | RTFILE_O_ATTR_ONLY;
             else
 #endif
-                fOpen |= RTFILE_O_READ;
+                fOpen |= RTFILE_O_OPEN | RTFILE_O_READ;
             LogFlowFunc(("SHCL_OBJ_CF_ACCESS_NONE\n"));
             break;
@@ -3058,5 +3049,5 @@
         case SHCL_OBJ_CF_ACCESS_READ:
         {
-            fOpen |= RTFILE_O_READ;
+            fOpen |= RTFILE_O_OPEN | RTFILE_O_READ;
             LogFlowFunc(("SHCL_OBJ_CF_ACCESS_READ\n"));
             break;
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h	(revision 92733)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h	(revision 92735)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2006-2020 Oracle Corporation
+ * Copyright (C) 2006-2021 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -293,5 +293,5 @@
  * @{
  */
-int ShClSvcGuestDataRequest(PSHCLCLIENT pClient, SHCLFORMATS fFormats, PSHCLEVENTID pidEvent);
+int ShClSvcGuestDataRequest(PSHCLCLIENT pClient, SHCLFORMATS fFormats, PSHCLEVENT *ppEvent);
 int ShClSvcGuestDataSignal(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData);
 int ShClSvcHostReportFormats(PSHCLCLIENT pClient, SHCLFORMATS fFormats);
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp	(revision 92733)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp	(revision 92735)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2019-2020 Oracle Corporation
+ * Copyright (C) 2019-2021 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -100,9 +100,10 @@
     if (pMsgHdr)
     {
-        SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pCtx->pTransfer->Events);
-        if (idEvent != NIL_SHCLEVENTID)
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
         {
             HGCMSvcSetU64(&pMsgHdr->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
-                                                                        pCtx->pTransfer->State.uID, idEvent));
+                                                                        pCtx->pTransfer->State.uID, pEvent->idEvent));
             HGCMSvcSetU32(&pMsgHdr->aParms[1], 0 /* fRoots */);
 
@@ -117,6 +118,5 @@
             {
                 PSHCLEVENTPAYLOAD pPayloadHdr;
-                rc = ShClEventWait(&pCtx->pTransfer->Events, idEvent,
-                                   pCtx->pTransfer->uTimeoutMs, &pPayloadHdr);
+                rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayloadHdr);
                 if (RT_SUCCESS(rc))
                 {
@@ -141,10 +141,11 @@
                                                                                VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ_REQ);
 
-                                    idEvent = ShClEventIdGenerateAndRegister(&pCtx->pTransfer->Events);
-                                    if (idEvent != NIL_SHCLEVENTID)
+                                    PSHCLEVENT pEvRoot;
+                                    rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvRoot);
+                                    if (RT_SUCCESS(rc))
                                     {
                                         HGCMSvcSetU64(&pMsgEntry->aParms[0],
                                                       VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uClientID,
-                                                                               pCtx->pTransfer->State.uID, idEvent));
+                                                                               pCtx->pTransfer->State.uID, pEvRoot->idEvent));
                                         HGCMSvcSetU32(&pMsgEntry->aParms[1], 0 /* fRoots */);
                                         HGCMSvcSetU32(&pMsgEntry->aParms[2], i /* uIndex */);
@@ -155,6 +156,5 @@
 
                                         PSHCLEVENTPAYLOAD pPayloadEntry;
-                                        rc = ShClEventWait(&pCtx->pTransfer->Events, idEvent,
-                                                           pCtx->pTransfer->uTimeoutMs, &pPayloadEntry);
+                                        rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayloadEntry);
                                         if (RT_FAILURE(rc))
                                             break;
@@ -167,5 +167,6 @@
                                         ShClPayloadFree(pPayloadEntry);
 
-                                        ShClEventUnregister(&pCtx->pTransfer->Events, idEvent);
+                                        ShClEventRelease(pEvRoot);
+                                        pEvRoot = NULL;
                                     }
                                     else
@@ -197,5 +198,5 @@
             }
 
-            ShClEventUnregister(&pCtx->pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
         }
         else
@@ -223,9 +224,10 @@
     if (pMsg)
     {
-        const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pCtx->pTransfer->Events);
-        if (idEvent != NIL_SHCLEVENTID)
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
         {
             pMsg->idCtx = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pCtx->pTransfer->State.uID,
-                                                   idEvent);
+                                                   pEvent->idEvent);
 
             rc = shClSvcTransferSetListOpen(pMsg->cParms, pMsg->aParms, pMsg->idCtx, pOpenParms);
@@ -238,5 +240,5 @@
                 {
                     PSHCLEVENTPAYLOAD pPayload;
-                    rc = ShClEventWait(&pCtx->pTransfer->Events, idEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
+                    rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
                     if (RT_SUCCESS(rc))
                     {
@@ -257,5 +259,5 @@
             }
 
-            ShClEventUnregister(&pCtx->pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
         }
         else
@@ -282,9 +284,10 @@
     if (pMsg)
     {
-        const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pCtx->pTransfer->Events);
-        if (idEvent != NIL_SHCLEVENTID)
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
         {
             pMsg->idCtx = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pCtx->pTransfer->State.uID,
-                                                   idEvent);
+                                                   pEvent->idEvent);
 
             rc = shClSvcTransferSetListClose(pMsg->cParms, pMsg->aParms, pMsg->idCtx, hList);
@@ -297,5 +300,5 @@
                 {
                     PSHCLEVENTPAYLOAD pPayload;
-                    rc = ShClEventWait(&pCtx->pTransfer->Events, idEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
+                    rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
                     if (RT_SUCCESS(rc))
                         ShClPayloadFree(pPayload);
@@ -303,5 +306,5 @@
             }
 
-            ShClEventUnregister(&pCtx->pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
         }
         else
@@ -329,9 +332,10 @@
     if (pMsg)
     {
-        const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pCtx->pTransfer->Events);
-        if (idEvent != NIL_SHCLEVENTID)
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
         {
             HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
-                                                                     pCtx->pTransfer->State.uID, idEvent));
+                                                                     pCtx->pTransfer->State.uID, pEvent->idEvent));
             HGCMSvcSetU64(&pMsg->aParms[1], hList);
             HGCMSvcSetU32(&pMsg->aParms[2], 0 /* fFlags */);
@@ -343,6 +347,5 @@
             {
                 PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClEventWait(&pCtx->pTransfer->Events, idEvent,
-                                   pCtx->pTransfer->uTimeoutMs, &pPayload);
+                rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
                 if (RT_SUCCESS(rc))
                 {
@@ -355,5 +358,5 @@
             }
 
-            ShClEventUnregister(&pCtx->pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
         }
         else
@@ -391,9 +394,10 @@
     if (pMsg)
     {
-        const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pCtx->pTransfer->Events);
-        if (idEvent != NIL_SHCLEVENTID)
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
         {
             HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
-                                                                     pCtx->pTransfer->State.uID, idEvent));
+                                                                     pCtx->pTransfer->State.uID, pEvent->idEvent));
             HGCMSvcSetU64(&pMsg->aParms[1], hList);
             HGCMSvcSetU32(&pMsg->aParms[2], 0 /* fInfo */);
@@ -405,5 +409,5 @@
             {
                 PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClEventWait(&pCtx->pTransfer->Events, idEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
+                rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
                 if (RT_SUCCESS(rc))
                 {
@@ -416,5 +420,5 @@
             }
 
-            ShClEventUnregister(&pCtx->pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
         }
         else
@@ -452,6 +456,7 @@
     if (pMsg)
     {
-        const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pCtx->pTransfer->Events);
-        if (idEvent != NIL_SHCLEVENTID)
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
         {
             LogFlowFunc(("pszPath=%s, fCreate=0x%x\n", pCreateParms->pszPath, pCreateParms->fCreate));
@@ -460,5 +465,5 @@
 
             HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
-                                                                     pCtx->pTransfer->State.uID, idEvent));
+                                                                     pCtx->pTransfer->State.uID, pEvent->idEvent));
             HGCMSvcSetU64(&pMsg->aParms[1], 0); /* uHandle */
             HGCMSvcSetU32(&pMsg->aParms[2], cbPath);
@@ -472,5 +477,5 @@
             {
                 PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClEventWait(&pCtx->pTransfer->Events, idEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
+                rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
                 if (RT_SUCCESS(rc))
                 {
@@ -490,5 +495,5 @@
             }
 
-            ShClEventUnregister(&pCtx->pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
         }
         else
@@ -515,9 +520,10 @@
     if (pMsg)
     {
-        const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pCtx->pTransfer->Events);
-        if (idEvent != NIL_SHCLEVENTID)
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
         {
             HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
-                                                                     pCtx->pTransfer->State.uID, idEvent));
+                                                                     pCtx->pTransfer->State.uID, pEvent->idEvent));
             HGCMSvcSetU64(&pMsg->aParms[1], hObj);
 
@@ -528,5 +534,5 @@
             {
                 PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClEventWait(&pCtx->pTransfer->Events, idEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
+                rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
                 if (RT_SUCCESS(rc))
                 {
@@ -544,5 +550,5 @@
             }
 
-            ShClEventUnregister(&pCtx->pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
         }
         else
@@ -570,9 +576,10 @@
     if (pMsg)
     {
-        const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pCtx->pTransfer->Events);
-        if (idEvent != NIL_SHCLEVENTID)
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
         {
             HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
-                                                                     pCtx->pTransfer->State.uID, idEvent));
+                                                                     pCtx->pTransfer->State.uID, pEvent->idEvent));
             HGCMSvcSetU64(&pMsg->aParms[1], hObj);
             HGCMSvcSetU32(&pMsg->aParms[2], cbData);
@@ -585,5 +592,5 @@
             {
                 PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClEventWait(&pCtx->pTransfer->Events, idEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
+                rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
                 if (RT_SUCCESS(rc))
                 {
@@ -604,5 +611,5 @@
             }
 
-            ShClEventUnregister(&pCtx->pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
         }
         else
@@ -630,9 +637,10 @@
     if (pMsg)
     {
-        const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pCtx->pTransfer->Events);
-        if (idEvent != NIL_SHCLEVENTID)
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
         {
             HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
-                                                                     pCtx->pTransfer->State.uID, idEvent));
+                                                                     pCtx->pTransfer->State.uID, pEvent->idEvent));
             HGCMSvcSetU64(&pMsg->aParms[1], hObj);
             HGCMSvcSetU64(&pMsg->aParms[2], cbData);
@@ -645,5 +653,5 @@
             {
                 PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClEventWait(&pCtx->pTransfer->Events, idEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
+                rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
                 if (RT_SUCCESS(rc))
                 {
@@ -659,5 +667,5 @@
             }
 
-            ShClEventUnregister(&pCtx->pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
         }
         else
@@ -1191,5 +1199,5 @@
                                       uint32_t cParms, VBOXHGCMSVCPARM aParms[])
 {
-    RT_NOREF(pClient);
+    RT_NOREF(pClient, pTransfer);
 
     int rc;
@@ -1225,9 +1233,13 @@
                         if (RT_SUCCESS(rc))
                         {
-                            const SHCLEVENTID idEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(uCID);
-
-                            LogFlowFunc(("uCID=%RU64 -> idEvent=%RU32\n", uCID, idEvent));
-
-                            rc = ShClEventSignal(&pTransfer->Events, idEvent, pPayload);
+                            const PSHCLEVENT pEvent
+                                = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
+                            if (pEvent)
+                            {
+                                LogFlowFunc(("uCID=%RU64 -> idEvent=%RU32\n", uCID, pEvent->idEvent));
+
+                                rc = ShClEventSignal(pEvent, pPayload);
+                            }
+                            /** @todo Silently skip? */
                         }
                         break;
@@ -1382,14 +1394,18 @@
                 uint32_t cbData = sizeof(SHCLROOTLISTHDR);
 
-                const SHCLEVENTID idEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(uCID);
-
-                PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClPayloadAlloc(idEvent, pvData, cbData, &pPayload);
-                if (RT_SUCCESS(rc))
-                {
-                    rc = ShClEventSignal(&pTransfer->Events, idEvent, pPayload);
-                    if (RT_FAILURE(rc))
-                        ShClPayloadFree(pPayload);
-                }
+                const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
+                if (pEvent)
+                {
+                    PSHCLEVENTPAYLOAD pPayload;
+                    rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload);
+                    if (RT_SUCCESS(rc))
+                    {
+                        rc = ShClEventSignal(pEvent, pPayload);
+                        if (RT_FAILURE(rc))
+                            ShClPayloadFree(pPayload);
+                    }
+                }
+                else
+                    rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND;
             }
             break;
@@ -1427,14 +1443,18 @@
                 uint32_t cbData = sizeof(SHCLROOTLISTENTRY);
 
-                const SHCLEVENTID idEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(uCID);
-
-                PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClPayloadAlloc(idEvent, pvData, cbData, &pPayload);
-                if (RT_SUCCESS(rc))
-                {
-                    rc = ShClEventSignal(&pTransfer->Events, idEvent, pPayload);
-                    if (RT_FAILURE(rc))
-                        ShClPayloadFree(pPayload);
-                }
+                const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
+                if (pEvent)
+                {
+                    PSHCLEVENTPAYLOAD pPayload;
+                    rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload);
+                    if (RT_SUCCESS(rc))
+                    {
+                        rc = ShClEventSignal(pEvent, pPayload);
+                        if (RT_FAILURE(rc))
+                            ShClPayloadFree(pPayload);
+                    }
+                }
+                else
+                    rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND;
             }
             break;
@@ -1502,14 +1522,18 @@
                     uint32_t cbData = sizeof(SHCLLISTHDR);
 
-                    const SHCLEVENTID idEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(uCID);
-
-                    PSHCLEVENTPAYLOAD pPayload;
-                    rc = ShClPayloadAlloc(idEvent, pvData, cbData, &pPayload);
-                    if (RT_SUCCESS(rc))
+                    const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
+                    if (pEvent)
                     {
-                        rc = ShClEventSignal(&pTransfer->Events, idEvent, pPayload);
-                        if (RT_FAILURE(rc))
-                            ShClPayloadFree(pPayload);
+                        PSHCLEVENTPAYLOAD pPayload;
+                        rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload);
+                        if (RT_SUCCESS(rc))
+                        {
+                            rc = ShClEventSignal(pEvent, pPayload);
+                            if (RT_FAILURE(rc))
+                                ShClPayloadFree(pPayload);
+                        }
                     }
+                    else
+                        rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND;
                 }
             }
@@ -1551,14 +1575,18 @@
                     uint32_t cbData = sizeof(SHCLLISTENTRY);
 
-                    const SHCLEVENTID idEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(uCID);
-
-                    PSHCLEVENTPAYLOAD pPayload;
-                    rc = ShClPayloadAlloc(idEvent, pvData, cbData, &pPayload);
-                    if (RT_SUCCESS(rc))
+                    const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
+                    if (pEvent)
                     {
-                        rc = ShClEventSignal(&pTransfer->Events, idEvent, pPayload);
-                        if (RT_FAILURE(rc))
-                            ShClPayloadFree(pPayload);
+                        PSHCLEVENTPAYLOAD pPayload;
+                        rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload);
+                        if (RT_SUCCESS(rc))
+                        {
+                            rc = ShClEventSignal(pEvent, pPayload);
+                            if (RT_FAILURE(rc))
+                                ShClPayloadFree(pPayload);
+                        }
                     }
+                    else
+                        rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND;
                 }
             }
@@ -1656,14 +1684,18 @@
                 uint32_t cbData = sizeof(SHCLOBJDATACHUNK);
 
-                const SHCLEVENTID idEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(uCID);
-
-                PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClPayloadAlloc(idEvent, pvData, cbData, &pPayload);
-                if (RT_SUCCESS(rc))
-                {
-                    rc = ShClEventSignal(&pTransfer->Events, idEvent, pPayload);
-                    if (RT_FAILURE(rc))
-                        ShClPayloadFree(pPayload);
-                }
+                const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
+                if (pEvent)
+                {
+                    PSHCLEVENTPAYLOAD pPayload;
+                    rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload);
+                    if (RT_SUCCESS(rc))
+                    {
+                        rc = ShClEventSignal(pEvent, pPayload);
+                        if (RT_FAILURE(rc))
+                            ShClPayloadFree(pPayload);
+                    }
+                }
+                else
+                    rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND;
             }
 
@@ -1738,12 +1770,13 @@
  * @param   uStatus             Status to report.
  * @param   rcTransfer          Result code to report. Optional and depending on status.
- * @param   pidEvent            Where to store the created wait event. Optional.
+ * @param   ppEvent             Where to return the wait event on success. Optional.
+ *                              Must be released by the caller with ShClEventRelease().
  */
 int shClSvcTransferSendStatus(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS uStatus,
-                              int rcTransfer, PSHCLEVENTID pidEvent)
+                              int rcTransfer, PSHCLEVENT *ppEvent)
 {
     AssertPtrReturn(pClient,   VERR_INVALID_POINTER);
     AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
-    /* pidEvent is optional. */
+    /* ppEvent is optional. */
 
     PSHCLCLIENTMSG pMsgReadData = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_STATUS,
@@ -1752,11 +1785,10 @@
         return VERR_NO_MEMORY;
 
-    int rc;
-
-    const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pTransfer->Events);
-    if (idEvent != NIL_SHCLEVENTID)
+    PSHCLEVENT pEvent;
+    int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+    if (RT_SUCCESS(rc))
     {
         HGCMSvcSetU64(&pMsgReadData->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
-                                                                         pTransfer->State.uID, idEvent));
+                                                                         pTransfer->State.uID, pEvent->idEvent));
         HGCMSvcSetU32(&pMsgReadData->aParms[1], pTransfer->State.enmDir);
         HGCMSvcSetU32(&pMsgReadData->aParms[2], uStatus);
@@ -1772,13 +1804,13 @@
                      ShClTransferStatusToStr(uStatus), rcTransfer, pTransfer->State.uID));
 
-            if (pidEvent)
-            {
-                *pidEvent = idEvent;
-            }
-            else /* If event is not consumed by the caller, unregister event again. */
-                ShClEventUnregister(&pTransfer->Events, idEvent);
+            if (ppEvent)
+            {
+                *ppEvent = pEvent; /* Takes ownership. */
+            }
+            else /* If event is not consumed by the caller, release the event again. */
+                ShClEventRelease(pEvent);
         }
         else
-            ShClEventUnregister(&pTransfer->Events, idEvent);
+            ShClEventRelease(pEvent);
     }
     else
@@ -1869,8 +1901,7 @@
                             if (RT_SUCCESS(rc))
                             {
-                                SHCLEVENTID idEvent;
+                                PSHCLEVENT pEvent;
                                 rc = shClSvcTransferSendStatus(pClient, pTransfer,
-                                                               SHCLTRANSFERSTATUS_INITIALIZED, VINF_SUCCESS,
-                                                               &idEvent);
+                                                               SHCLTRANSFERSTATUS_INITIALIZED, VINF_SUCCESS, &pEvent);
                                 if (RT_SUCCESS(rc))
                                 {
@@ -1882,5 +1913,5 @@
 
                                     PSHCLEVENTPAYLOAD pPayload = NULL;
-                                    rc = ShClEventWait(&pTransfer->Events, idEvent, pTransfer->uTimeoutMs, &pPayload);
+                                    rc = ShClEventWait(pEvent, pTransfer->uTimeoutMs, &pPayload);
                                     if (RT_SUCCESS(rc))
                                     {
@@ -1907,4 +1938,5 @@
 
                                     ShClPayloadFree(pPayload);
+                                    ShClEventRelease(pEvent);
 
                                     /* Re-enter the client's critical section again. */
@@ -1954,15 +1986,16 @@
 int shClSvcTransferStop(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
 {
-    SHCLEVENTID idEvent;
+    PSHCLEVENT pEvent;
     int rc = shClSvcTransferSendStatus(pClient, pTransfer,
-                                       SHCLTRANSFERSTATUS_STOPPED, VINF_SUCCESS,
-                                       &idEvent);
+                                       SHCLTRANSFERSTATUS_STOPPED, VINF_SUCCESS, &pEvent);
     if (RT_SUCCESS(rc))
     {
         LogRel2(("Shared Clipboard: Waiting for stop of transfer %RU32 on guest ...\n", pTransfer->State.uID));
 
-        rc = ShClEventWait(&pTransfer->Events, idEvent, pTransfer->uTimeoutMs, NULL);
+        rc = ShClEventWait(pEvent, pTransfer->uTimeoutMs, NULL /* ppPayload */);
         if (RT_SUCCESS(rc))
             LogRel2(("Shared Clipboard: Stopped transfer %RU32 on guest\n", pTransfer->State.uID));
+
+        ShClEventRelease(pEvent);
     }
 
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp	(revision 92733)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp	(revision 92735)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2006-2020 Oracle Corporation
+ * Copyright (C) 2006-2021 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -207,5 +207,4 @@
 
         ShClEventRelease(&pCtx->pClient->EventSrc, idEvent);
-        ShClEventUnregister(&pCtx->pClient->EventSrc, idEvent);
     }
 
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp	(revision 92733)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp	(revision 92735)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2006-2020 Oracle Corporation
+ * Copyright (C) 2006-2021 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -218,16 +218,18 @@
                  pClient, uFormat, pvData, cbData, pcbActual));
 
-    int rc = VINF_SUCCESS;
+    int rc;
 
     CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
     if (pReq)
     {
-        pReq->pv        = pvData;
-        pReq->cb        = cbData;
-        pReq->pcbActual = pcbActual;
-        const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pClient->EventSrc);
-        pReq->idEvent    = idEvent;
-        if (idEvent != NIL_SHCLEVENTID)
-        {
+        PSHCLEVENT pEvent;
+        rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+        if (RT_SUCCESS(rc))
+        {
+            pReq->pv        = pvData;
+            pReq->cb        = cbData;
+            pReq->pcbActual = pcbActual;
+            pReq->idEvent   = pEvent->idEvent;
+
             /* Note: ShClX11ReadDataFromX11() will consume pReq on success. */
             rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, uFormat, pReq);
@@ -235,5 +237,5 @@
             {
                 PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClEventWait(&pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
+                rc = ShClEventWait(pEvent, 30 * 1000, &pPayload);
                 if (RT_SUCCESS(rc))
                 {
@@ -251,8 +253,6 @@
             }
 
-            ShClEventUnregister(&pClient->EventSrc, idEvent);
-        }
-        else
-            rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
+            ShClEventRelease(pEvent);
+        }
 
         if (RT_FAILURE(rc))
@@ -331,6 +331,10 @@
         if (RT_SUCCESS(rc2))
         {
-            rc2 = ShClEventSignal(&pCtx->pClient->EventSrc, pReq->idEvent, pPayload);
+            const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pCtx->pClient->EventSrc, pReq->idEvent);
+            if (pEvent)
+                rc2 = ShClEventSignal(pEvent, pPayload);
+
             RTCritSectLeave(&pCtx->pClient->CritSect);
+
             if (RT_SUCCESS(rc2))
                 pPayload = NULL;
@@ -394,6 +398,6 @@
     {
         /* Request data from the guest. */
-        SHCLEVENTID idEvent;
-        rc = ShClSvcGuestDataRequest(pCtx->pClient, uFmt, &idEvent);
+        PSHCLEVENT pEvent;
+        rc = ShClSvcGuestDataRequest(pCtx->pClient, uFmt, &pEvent);
         if (RT_SUCCESS(rc))
         {
@@ -401,5 +405,5 @@
 
             PSHCLEVENTPAYLOAD pPayload;
-            rc = ShClEventWait(&pCtx->pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
+            rc = ShClEventWait(pEvent, 30 * 1000, &pPayload);
             if (RT_SUCCESS(rc))
             {
@@ -418,6 +422,5 @@
             RTCritSectEnter(&pClient->CritSect);
 
-            ShClEventRelease(&pCtx->pClient->EventSrc, idEvent);
-            ShClEventUnregister(&pCtx->pClient->EventSrc, idEvent);
+            ShClEventRelease(pEvent);
         }
     }
@@ -459,13 +462,12 @@
     LogFlowFuncEnter();
 
-    int rc;
-
-    SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pClient->EventSrc);
-    if (idEvent != NIL_SHCLEVENTID)
+    PSHCLEVENT pEvent;
+    int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+    if (RT_SUCCESS(rc))
     {
         CLIPREADCBREQ *pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(CLIPREADCBREQ));
         if (pReq)
         {
-            pReq->idEvent = idEvent;
+            pReq->idEvent = pEvent->idEvent;
 
             rc = ShClX11ReadDataFromX11(&pClient->State.pCtx->X11, VBOX_SHCL_FMT_URI_LIST, pReq);
@@ -474,5 +476,5 @@
                 /* X supplies the data asynchronously, so we need to wait for data to arrive first. */
                 PSHCLEVENTPAYLOAD pPayload;
-                rc = ShClEventWait(&pClient->EventSrc, idEvent, 30 * 1000, &pPayload);
+                rc = ShClEventWait(pEvent, 30 * 1000, &pPayload);
                 if (RT_SUCCESS(rc))
                 {
@@ -485,5 +487,5 @@
             rc = VERR_NO_MEMORY;
 
-        ShClEventUnregister(&pClient->EventSrc, idEvent);
+        ShClEventRelease(pEvent);
     }
     else
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp	(revision 92733)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp	(revision 92735)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2006-2020 Oracle Corporation
+ * Copyright (C) 2006-2021 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -1201,19 +1201,14 @@
  * @param   pClient             Client to request to read data form.
  * @param   fFormats            The formats being requested, OR'ed together (VBOX_SHCL_FMT_XXX).
- * @param   pidEvent            Event ID for waiting for new data. Optional.
- *                              Must be released by the caller with ShClEventRelease() before unregistering then.
- */
-int ShClSvcGuestDataRequest(PSHCLCLIENT pClient, SHCLFORMATS fFormats, PSHCLEVENTID pidEvent)
-{
-    LogFlowFuncEnter();
-    if (pidEvent)
-        *pidEvent = NIL_SHCLEVENTID;
-    AssertPtrReturn(pClient,  VERR_INVALID_POINTER);
+ * @param   ppEvent             Where to return the event for waiting for new data on success. Optional.
+ *                              Must be released by the caller with ShClEventRelease().
+ */
+int ShClSvcGuestDataRequest(PSHCLCLIENT pClient, SHCLFORMATS fFormats, PSHCLEVENT *ppEvent)
+{
+    AssertPtrReturn(pClient, VERR_INVALID_POINTER);
 
     LogFlowFunc(("fFormats=%#x\n", fFormats));
 
     int rc = VERR_NOT_SUPPORTED;
-
-    SHCLEVENTID idEvent = NIL_SHCLEVENTID;
 
     /* Generate a separate message for every (valid) format we support. */
@@ -1255,10 +1250,11 @@
             RTCritSectEnter(&pClient->CritSect);
 
-            idEvent = ShClEventIdGenerateAndRegister(&pClient->EventSrc);
-            if (idEvent != NIL_SHCLEVENTID)
+            PSHCLEVENT pEvent;
+            rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
+            if (RT_SUCCESS(rc))
             {
-                LogFlowFunc(("fFormats=%#x -> fFormat=%#x, idEvent=%#x\n", fFormats, fFormat, idEvent));
-
-                const uint64_t uCID = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pClient->EventSrc.uID, idEvent);
+                LogFlowFunc(("fFormats=%#x -> fFormat=%#x, idEvent=%#x\n", fFormats, fFormat, pEvent->idEvent));
+
+                const uint64_t uCID = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pClient->EventSrc.uID, pEvent->idEvent);
 
                 rc = VINF_SUCCESS;
@@ -1296,4 +1292,14 @@
 
                     shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
+
+                    /* Retain the last event generated (in case there were multiple clipboard formats)
+                     * if we need to return the event to the caller. */
+                    if (ppEvent)
+                    {
+                        ShClEventRetain(pEvent);
+                        *ppEvent = pEvent;
+                    }
+
+                    shClSvcClientWakeup(pClient);
                 }
             }
@@ -1311,21 +1317,4 @@
         if (RT_FAILURE(rc))
             break;
-    }
-
-    if (RT_SUCCESS(rc))
-    {
-        RTCritSectEnter(&pClient->CritSect);
-
-        /* Retain the last event generated (in case there were multiple clipboard formats)
-         * if we need to return the event ID to the caller. */
-        if (pidEvent)
-        {
-            ShClEventRetain(&pClient->EventSrc, idEvent);
-            *pidEvent = idEvent;
-        }
-
-        shClSvcClientWakeup(pClient);
-
-        RTCritSectLeave(&pClient->CritSect);
     }
 
@@ -1355,5 +1344,5 @@
 
     /*
-     * Validate intput.
+     * Validate input.
      */
     AssertPtrReturn(pClient, VERR_INVALID_POINTER);
@@ -1364,5 +1353,7 @@
     const SHCLEVENTID idEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(pCmdCtx->uContextID);
     AssertMsgReturn(idEvent != NIL_SHCLEVENTID, ("NIL event in context ID %#RX64\n", pCmdCtx->uContextID), VERR_WRONG_ORDER);
-    AssertMsg(ShClEventGet(&pClient->EventSrc, idEvent) != NULL, ("Event %#x not found\n", idEvent));
+
+    PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, idEvent);
+    AssertMsg(pEvent != NULL, ("Event %#x not found\n", idEvent));
 
     /*
@@ -1380,7 +1371,5 @@
      * Signal the event.
      */
-    RTCritSectEnter(&pClient->CritSect);
-    int rc2 = ShClEventSignal(&pClient->EventSrc, idEvent, pPayload);
-    RTCritSectLeave(&pClient->CritSect);
+    int rc2 = ShClEventSignal(pEvent, pPayload);
     if (RT_FAILURE(rc2))
     {
@@ -1391,14 +1380,7 @@
 
     /*
-     * No one holding a reference to the event anymore? Unregister it.
-     */
-    /** @todo r=bird: This isn't how reference counting is supposed to be
-     *        implemented, is it now? */
-    if (ShClEventGetRefs(&pClient->EventSrc, idEvent) == 0)
-    {
-        rc2 = ShClEventUnregister(&pClient->EventSrc, idEvent);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
+     * Release reference (and free it if not used anymore).
+     */
+    ShClEventRelease(pEvent);
 
     LogFlowFuncLeaveRC(rc);
