VirtualBox

Ignore:
Timestamp:
Dec 12, 2022 6:36:50 PM (2 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
154859
Message:

DnD: Simplified / untangled reset handling:

  • Don't implicitly reset the DnD state with the GuestDnDSendCtx / GuestDnDRecvCtx classes; that can cause side effects.
  • Don't reset GuestDnDTargets state before sending data to the guest via GuestDnDTarget::sendData().
  • Don't reset the DnD state within GuestDnDState::setProgress() anymore. This needs to be done explicitly by the caller waiting for the progress.
  • Make sure to also clear the DnD state's callback map within GuestDnDState::reset().
  • Implemented a default guest callback, which gets invoked when no other (registered) guest callbacks are in place (anymore).
  • Return VERR_CANCELLED to VBoxTray / VBoxClient clients when there was a reset or the current operation was aborted.
  • Release (2) log what has been reset.
  • Implement reference counting for internal DnD messages within the host service; this should help wrt races between peeking for new client messages and actual retrieving those messages.
Location:
trunk/src/VBox/HostServices/DragAndDrop
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp

    r97763 r97788  
    285285
    286286    /*
    287      * Reset the message queue as soon as a new clients connect
     287     * Reset the message queue as soon as a new client connects
    288288     * to ensure that every client has the same state.
    289289     */
    290290    if (m_pManager)
    291         m_pManager->Reset();
     291        m_pManager->Reset(true /* fForce */);
    292292
    293293    LogFlowFunc(("Client %RU32 connected (VINF_SUCCESS)\n", idClient));
     
    574574                if (cParms == 3)
    575575                {
    576                     rc = m_pManager->GetNextMsgInfo(&paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */);
     576                    /* Make sure to increase the reference count so that the next message doesn't get removed between
     577                     * the guest's GUEST_DND_FN_GET_NEXT_HOST_MSG call and the actual message retrieval call. */
     578                    rc = m_pManager->GetNextMsgInfo(true /* fAddRef */,
     579                                                    &paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */);
    577580                    if (RT_FAILURE(rc)) /* No queued messages available? */
    578581                    {
     
    589592                                /* Note: paParms[2] was set by the guest as blocking flag. */
    590593                            }
     594
     595                            LogFlowFunc(("Host callback returned %Rrc\n", rc));
    591596                        }
    592597                        else /* No host callback in place, so drag and drop is not supported by the host. */
     
    10011006                        data.paParms = paParms;
    10021007
    1003                         rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function,
    1004                                                       &data, sizeof(data));
     1008                        rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
    10051009                        if (RT_SUCCESS(rc))
    10061010                        {
     
    10101014                        else
    10111015                        {
    1012                             /*
    1013                              * In case the guest is too fast asking for the next message
    1014                              * and the host did not supply it yet, just defer the client's
    1015                              * return until a response from the host available.
    1016                              */
    1017                             LogFlowFunc(("No new messages from the host (yet), deferring request: %Rrc\n", rc));
    1018                             rc = VINF_HGCM_ASYNC_EXECUTE;
     1016                            if (rc == VERR_CANCELLED)
     1017                            {
     1018                                /* Host indicated that the current operation was cancelled. Tell the guest. */
     1019                                LogFunc(("Host indicated that operation was cancelled\n", rc));
     1020                            }
     1021                            else
     1022                            {
     1023                                /*
     1024                                 * In case the guest is too fast asking for the next message
     1025                                 * and the host did not supply it yet, just defer the client's
     1026                                 * return until a response from the host available.
     1027                                 */
     1028                                LogFunc(("No new messages from the host (%Rrc), deferring request\n", rc));
     1029                                rc = VINF_HGCM_ASYNC_EXECUTE;
     1030                            }
    10191031                        }
    10201032                    }
     
    10361048    if (rc == VINF_HGCM_ASYNC_EXECUTE)
    10371049    {
    1038         LogFlowFunc(("Deferring client %RU32\n", idClient));
     1050        LogFunc(("Deferring client %RU32\n", idClient));
    10391051
    10401052        try
     
    10611073    }
    10621074
    1063     LogFlowFunc(("Returning rc=%Rrc\n", rc));
     1075    LogFunc(("Returning %Rrc to guest\n", rc));
    10641076}
    10651077
     
    11701182            LogFlowFunc(("Cancelling all waiting clients ...\n"));
    11711183
    1172             /* Reset the message queue as the host cancelled the whole operation. */
    1173             m_pManager->Reset();
    1174 
    1175             rc = m_pManager->AddMsg(u32Function, cParms, paParms, true /* fAppend */);
    1176             if (RT_FAILURE(rc))
    1177             {
    1178                 AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n", u32Function, rc));
    1179                 break;
    1180             }
     1184            /* Forcefully reset the message queue, as the host has cancelled the current operation. */
     1185            m_pManager->Reset(true /* fForce */);
    11811186
    11821187            /*
     
    11961201                                                      /* Protocol v3+ also contains the context ID. */
    11971202                                                      pClient->uProtocolVerDeprecated >= 3 ? 1 : 0);
    1198                 pClient->CompleteDeferred(rc2);
     1203                AssertRC(rc2);
     1204
     1205                /* Return VERR_CANCELLED when waking up the guest side. */
     1206                pClient->CompleteDeferred(VERR_CANCELLED);
    11991207
    12001208                m_clientQueue.erase(itQueue);
     
    12121220        {
    12131221            /* Reset the message queue as a new DnD operation just began. */
    1214             m_pManager->Reset();
     1222            m_pManager->Reset(false /* fForce */);
    12151223
    12161224            fSendToGuest = true;
     
    12721280            uint32_t uMsgNext   = 0;
    12731281            uint32_t cParmsNext = 0;
    1274             int rcNext = m_pManager->GetNextMsgInfo(&uMsgNext, &cParmsNext);
     1282            /* Note: We only want to peek for the next message, hence fAddRef is false. */
     1283            int rcNext = m_pManager->GetNextMsgInfo(false /* fAddRef */, &uMsgNext, &cParmsNext);
    12751284
    12761285            LogFlowFunc(("uMsgClient=%s (%#x), uMsgNext=%s (%#x), cParmsNext=%RU32, rcNext=%Rrc\n",
  • trunk/src/VBox/HostServices/DragAndDrop/dndmanager.cpp

    r97747 r97788  
    112112        if (i > 0)
    113113            Log((" - "));
    114         uint32_t const uType = m_queueMsg[i]->GetType();
    115         Log(("%s (%d / %#x)", DnDHostMsgToStr(uType), uType, uType));
     114        DnDMessage const *pMsg = m_queueMsg[i];
     115        uint32_t const uType = pMsg->GetType();
     116        Log(("%s (%d / %#x) cRefS=%RU32", DnDHostMsgToStr(uType), uType, uType, pMsg->RefCount()));
    116117    }
    117118    Log(("\n"));
     
    123124 *
    124125 * @returns IPRT status code. VERR_NO_DATA if no next message is available.
     126 * @param   fAddRef             Set to \c true to increase the message's reference count, or \c false if not.
    125127 * @param   puType              Where to store the message type.
    126128 * @param   pcParms             Where to store the message parameter count.
    127129 */
    128 int DnDManager::GetNextMsgInfo(uint32_t *puType, uint32_t *pcParms)
     130int DnDManager::GetNextMsgInfo(bool fAddRef, uint32_t *puType, uint32_t *pcParms)
    129131{
    130132    AssertPtrReturn(puType, VERR_INVALID_POINTER);
     
    133135    int rc;
    134136
    135 #ifdef DEBUG
    136     DumpQueue();
    137 #endif
    138 
    139137    if (m_queueMsg.isEmpty())
    140138    {
     
    149147        *pcParms = pMsg->GetParamCount();
    150148
     149        if (fAddRef)
     150            pMsg->AddRef();
     151
    151152        rc = VINF_SUCCESS;
    152153    }
    153154
    154     LogFlowFunc(("Returning uMsg=%s (%#x), cParms=%RU32, rc=%Rrc\n", DnDHostMsgToStr(*puType), *puType, *pcParms, rc));
     155#ifdef DEBUG
     156    DumpQueue();
     157#endif
     158
     159    LogFlowFunc(("Returning uMsg=%s (%#x), cParms=%RU32, fAddRef=%RTbool, rc=%Rrc\n",
     160                 DnDHostMsgToStr(*puType), *puType, *pcParms, fAddRef, rc));
    155161    return rc;
    156162}
     
    158164/**
    159165 * Retrieves the next queued up message and removes it from the queue on success.
    160  * Will return VERR_NO_DATA if no next message is available.
    161  *
    162  * @returns IPRT status code.
     166 *
     167 * @returns VBox status code.
     168 * @retval  VERR_NO_DATA if no next message is available.
    163169 * @param   uMsg                Message type to retrieve.
    164170 * @param   cParms              Number of parameters the \@a paParms array can store.
     
    173179        return VERR_NO_DATA;
    174180
     181#ifdef DEBUG
     182    DumpQueue();
     183#endif
     184
    175185    /* Get the current message. */
    176186    DnDMessage *pMsg = m_queueMsg.first();
    177187    AssertPtr(pMsg);
    178188
    179     m_queueMsg.removeFirst(); /* Remove the current message from the queue. */
     189    if (pMsg->Release() == 0)     /* Not referenced by any client anymore? */
     190        m_queueMsg.removeFirst(); /* Remove the current message from the queue. */
    180191
    181192    /* Fetch the current message info. */
     
    184195    /*
    185196     * If there was an error handling the current message or the user has canceled
    186      * the operation, we need to cleanup all pending events and inform the progress
    187      * callback about our exit.
     197     * the operation, we need to cleanup all pending events.
    188198     */
    189199    if (RT_FAILURE(rc))
    190200    {
    191201        /* Clear any pending messages. */
    192         Reset();
    193 
    194         /* Create a new cancel message to inform the guest + call
    195          * the host whether the current transfer was canceled or aborted
    196          * due to an error. */
    197         try
    198         {
    199             if (rc == VERR_CANCELLED)
    200                 LogFlowFunc(("Operation was cancelled\n"));
    201 
    202             DnDHGCancelMessage *pMsgCancel = new DnDHGCancelMessage();
    203 
    204             int rc2 = AddMsg(pMsgCancel, false /* Prepend */);
    205             AssertRC(rc2);
    206 
    207             if (m_pfnProgressCallback)
    208             {
    209                 LogFlowFunc(("Notifying host about aborting operation (%Rrc) ...\n", rc));
    210                 m_pfnProgressCallback(  rc == VERR_CANCELLED
    211                                       ? DragAndDropSvc::DND_PROGRESS_CANCELLED
    212                                       : DragAndDropSvc::DND_PROGRESS_ERROR,
    213                                       100 /* Percent */, rc,
    214                                       m_pvProgressUser);
    215             }
    216         }
    217         catch(std::bad_alloc &)
    218         {
    219             rc = VERR_NO_MEMORY;
    220         }
     202        Reset(true /* fForce */);
    221203    }
    222204
     
    227209/**
    228210 * Resets the manager by clearing the message queue and internal state.
    229  */
    230 void DnDManager::Reset(void)
     211 *
     212 * @param   fForce              Set to \c true to forcefully also remove still referenced messages, or \c false to only
     213 *                              remove non-referenced messages.
     214 */
     215void DnDManager::Reset(bool fForce)
    231216{
    232217    LogFlowFuncEnter();
    233218
    234     while (!m_queueMsg.isEmpty())
    235     {
    236         delete m_queueMsg.last();
    237         m_queueMsg.removeLast();
    238     }
    239 }
    240 
     219#ifdef DEBUG
     220    DumpQueue();
     221#endif
     222
     223    for (size_t i = 0; i < m_queueMsg.size(); i++)
     224    {
     225        if (   fForce
     226            || m_queueMsg[i]->RefCount() == 0)
     227        {
     228            m_queueMsg.removeAt(i);
     229            i = i > 0 ? i - 1 : 0;
     230        }
     231    }
     232}
     233
  • trunk/src/VBox/HostServices/DragAndDrop/dndmanager.h

    r97731 r97788  
    5050
    5151    DnDMessage(void)
    52     {
    53     }
     52        : m_cRefs(0) { }
    5453
    5554    DnDMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[])
    56         : Message(uMsg, cParms, aParms) { }
     55        : Message(uMsg, cParms, aParms)
     56        , m_cRefs(0) { }
    5757
    5858    virtual ~DnDMessage(void) { }
     59
     60    uint32_t AddRef(void) { Assert(m_cRefs < 32); return ++m_cRefs; }
     61    uint32_t Release(void) { if (m_cRefs) return --m_cRefs; return m_cRefs; }
     62    uint32_t RefCount(void) const { return m_cRefs; }
     63
     64protected:
     65
     66    /** The message's current reference count. */
     67    uint32_t m_cRefs;
    5968};
    6069
     
    100109    virtual ~DnDManager(void)
    101110    {
    102         Reset();
     111        Reset(true /* fForce */);
    103112    }
    104113
     
    110119#endif
    111120
    112     int GetNextMsgInfo(uint32_t *puType, uint32_t *pcParms);
     121    int GetNextMsgInfo(bool fAddRef, uint32_t *puType, uint32_t *pcParms);
    113122    int GetNextMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
    114123
    115     void Reset(void);
     124    void Reset(bool fForce);
    116125
    117126protected:
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette