VirtualBox

Ignore:
Timestamp:
Jan 13, 2009 9:09:28 PM (16 years ago)
Author:
vboxsync
Message:

HostServices/SharedClipboard: fix a deadlock and reintroduce timeouts for data coming from VBox

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/HostServices/SharedClipboard/x11.cpp

    r15898 r15928  
    7979} VBOXCLIPBOARDFORMAT;
    8080
    81 /** Does the host or the guest currently own the clipboard? */
    82 enum g_eClipboardOwner { NONE = 0, HOST, GUEST };
     81/** Does X11 or VBox currently own the clipboard? */
     82enum g_eOwner { NONE = 0, X11, VB };
    8383
    8484typedef struct {
     
    127127
    128128    /** Does the host or the guest currently own the clipboard? */
    129     volatile enum g_eClipboardOwner eOwner;
     129    volatile enum g_eOwner eOwner;
    130130
    131131    /** What is the best text format the host has to offer?  INVALID for none. */
     
    148148        semaphore, which is triggered when the data arrives. */
    149149    RTSEMEVENT waitForData;
    150     /** And because it would not do for the guest to be waiting for the host while the host
    151         is waiting for the guest, we set a flag and assert horribly if we spot a deadlock. */
    152     uint32_t waiter;
    153     /** This mutex is held while an asynchronous operation completes (i.e. the host clipboard is
    154         being queried) to make sure that the clipboard is not disconnected during that time.  It
    155         is also grabbed when the clipboard client disconnects.  When an asynchronous operation
    156         starts completing, it checks that the same client is still connected immediately after
    157         grabbing the mutex. */
    158     RTSEMMUTEX asyncMutex;
     150    /** Who (if anyone) is currently waiting for data?  Used for sanity checks
     151     *  when data arrives. */
     152    volatile uint32_t waiter;
     153    /** This mutex is grabbed during any critical operations on the clipboard
     154     * which might clash with others. */
     155    RTSEMMUTEX clipboardMutex;
    159156
    160157    /** Format which we are reading from the host clipboard (valid during a request for the
     
    182179
    183180/**
     181 * Reset the contents of the buffer used to pass clipboard data from VBox to X11.
     182 * This must be done after every clipboard transfer.
     183 */
     184static void vboxClipboardEmptyGuestBuffer(void)
     185{
     186    if (g_ctx.pClient->data.pv != 0)
     187        RTMemFree(g_ctx.pClient->data.pv);
     188    g_ctx.pClient->data.pv = 0;
     189    g_ctx.pClient->data.cb = 0;
     190    g_ctx.pClient->data.u32Format = 0;
     191}
     192
     193/**
    184194 * Send a request to VBox to transfer the contents of its clipboard to X11.
    185195 *
     
    191201static int vboxClipboardReadDataFromVBox (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format)
    192202{
    193     VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient;
     203    volatile VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient;
    194204
    195205    LogFlowFunc(("u32Format=%02X\n", u32Format));
     206    if (pClient == NULL)
     207    {
     208        /* This can legitimately happen if we disconnect during a request for
     209         * data from X11. */
     210        LogFunc(("host requested guest clipboard data after guest had disconnected.\n"));
     211        pCtx->guestFormats = 0;
     212        pCtx->waiter = NONE;
     213        return VERR_TIMEOUT;
     214    }
    196215    /* Assert that no other transfer is in process (requests are serialised)
    197216     * and that the last transfer cleaned up properly. */
    198     AssertLogRelReturn(   pCtx->pClient->data.pv == NULL
    199                        && pCtx->pClient->data.cb == 0
    200                        && pCtx->pClient->data.u32Format == 0,
     217    AssertLogRelReturn(   pClient->data.pv == NULL
     218                       && pClient->data.cb == 0
     219                       && pClient->data.u32Format == 0,
    201220                       VERR_WRONG_ORDER
    202221                      );
     
    204223     * requests from X11 are serialised and the second because VBox previously
    205224     * grabbed the clipboard, so it should not be waiting for data from us. */
    206     AssertLogRelReturn (ASMAtomicCmpXchgU32(&pCtx->waiter, 1, 0), VERR_DEADLOCK);
    207     if (pClient == 0)
    208     {
    209         /* This can legitimately happen if we disconnect during a request for
    210          * data from X11. */
    211         LogFunc(("host requested guest clipboard data after guest had disconnected.\n"));
    212         pCtx->guestFormats = 0;
    213         pCtx->waiter = 0;
    214         return VERR_TIMEOUT;
    215     }
    216     /* Request data from the guest */
    217     vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
    218     /* Which will signal us when it is ready. */
    219     int rc = RTSemEventWait(pCtx->waitForData, RT_INDEFINITE_WAIT);
     225    AssertLogRelReturn (ASMAtomicCmpXchgU32(&pCtx->waiter, X11, NONE), VERR_DEADLOCK);
     226    /* Request data from VBox */
     227    vboxSvcClipboardReportMsg(pCtx->pClient,
     228                              VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA,
     229                              u32Format);
     230    /* Which will signal us when it is ready.  We use a timeout here because
     231     * we can't be sure that the guest will behave correctly. */
     232    int rc = RTSemEventWait(pCtx->waitForData, CLIPBOARDTIMEOUT);
     233    if (rc == VERR_TIMEOUT)
     234        rc = VINF_SUCCESS;  /* Timeout handling follows. */
     235    /* Now we have a potential race between the HGCM thread delivering the data
     236     * and our setting waiter to NONE to say that we are no longer waiting for
     237     * it.  We solve this as follows: both of these operations are done under
     238     * the clipboard mutex.  The HGCM thread will only deliver the data if we
     239     * are still waiting after it acquires the mutex.  After we release the
     240     * mutex, we finally do our check to see whether the data was delivered. */
     241    RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT);
     242    pCtx->waiter = NONE;
     243    RTSemMutexRelease(g_ctx.clipboardMutex);
    220244    AssertLogRelRCSuccess(rc);
    221245    if (RT_FAILURE(rc))
     
    223247        /* I believe this should not happen.  Wait until the assertions arrive
    224248         * to prove the contrary. */
     249        vboxClipboardEmptyGuestBuffer();
    225250        pCtx->guestFormats = 0;
    226         pCtx->waiter = 0;
    227251        return rc;
    228252    }
    229     pCtx->waiter = 0;
     253    if (pClient->data.pv == NULL)
     254        return VERR_TIMEOUT;
    230255    LogFlowFunc(("wait completed.  Returning.\n"));
    231256    return VINF_SUCCESS;
     
    490515    /* We grab this mutex whenever an asynchronous clipboard operation completes and while
    491516       disconnecting a client from the clipboard to stop these operations colliding. */
    492     RTSemMutexRequest(g_ctx.asyncMutex, RT_INDEFINITE_WAIT);
     517    RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT);
    493518    if (reinterpret_cast<VBOXCLIPBOARDCLIENTDATA *>(pClientData) != g_ctx.pClient)
    494519    {
     
    496521        XtFree(reinterpret_cast<char *>(pValue));
    497522        LogFlowFunc(("client is no longer connected, returning\n"));
    498         RTSemMutexRelease(g_ctx.asyncMutex);
     523        RTSemMutexRelease(g_ctx.clipboardMutex);
    499524        return;
    500525    }
     
    534559        LogFunc (("bad target format\n"));
    535560        XtFree(reinterpret_cast<char *>(pValue));
    536         RTSemMutexRelease(g_ctx.asyncMutex);
     561        RTSemMutexRelease(g_ctx.clipboardMutex);
    537562        return;
    538563    }
    539564    g_ctx.notifyGuest = true;
    540     RTSemMutexRelease(g_ctx.asyncMutex);
     565    RTSemMutexRelease(g_ctx.clipboardMutex);
    541566}
    542567
     
    567592    /* We grab this mutex whenever an asynchronous clipboard operation completes and while
    568593       disconnecting a client from the clipboard to stop these operations colliding. */
    569     RTSemMutexRequest(g_ctx.asyncMutex, RT_INDEFINITE_WAIT);
     594    RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT);
    570595    if (reinterpret_cast<VBOXCLIPBOARDCLIENTDATA *>(pClientData) != g_ctx.pClient)
    571596    {
    572597        /* If the client is no longer connected, just return. */
    573598        LogFlowFunc(("client is no longer connected, returning\n"));
    574         RTSemMutexRelease(g_ctx.asyncMutex);
     599        RTSemMutexRelease(g_ctx.clipboardMutex);
    575600        return;
    576601    }
     
    633658    }
    634659    XtFree(reinterpret_cast<char *>(pValue));
    635     RTSemMutexRelease(g_ctx.asyncMutex);
     660    RTSemMutexRelease(g_ctx.clipboardMutex);
    636661}
    637662
     
    646671    Log3 (("%s: called\n", __PRETTY_FUNCTION__));
    647672    /* Get the current clipboard contents */
    648     if (g_ctx.eOwner == HOST && g_ctx.pClient != 0)
     673    if (g_ctx.eOwner == X11 && g_ctx.pClient != 0)
    649674    {
    650675        Log3 (("%s: requesting the targets that the host clipboard offers\n",
     
    812837    LogRel(("Initializing host clipboard service\n"));
    813838    RTSemEventCreate(&g_ctx.waitForData);
    814     RTSemMutexCreate(&g_ctx.asyncMutex);
     839    RTSemMutexCreate(&g_ctx.clipboardMutex);
    815840    rc = vboxClipboardInitX11();
    816841    if (RT_SUCCESS(rc))
     
    824849    {
    825850        RTSemEventDestroy(g_ctx.waitForData);
    826         RTSemMutexDestroy(g_ctx.asyncMutex);
     851        RTSemMutexDestroy(g_ctx.clipboardMutex);
    827852    }
    828853    return rc;
     
    852877     * immediately. */
    853878    g_ctx.pClient = NULL;
    854     /* Set the termination flag. */
     879    if (g_ctx.eOwner == VB)
     880        /* X11 may be waiting for data from VBox.  At this point it is no
     881         * longer going to arrive, and we must release it to allow the event
     882         * loop to terminate.  In this case the buffer where VBox would have
     883         * written the clipboard data will still be empty and we will just
     884         * return "no data" to X11.  Any subsequent attempts to get the data
     885         * from VBox will fail immediately as the client reference is gone. */
     886        RTSemEventSignal(g_ctx.waitForData);
     887    /* Set the termination flag.  This has been observed to block if it was set
     888     * during a request for clipboard data coming from X11, so only we do it
     889     * after releasing any such requests. */
    855890    XtAppSetExitFlag(g_ctx.appContext);
    856891    /* Wake up the event loop */
     
    860895    XSendEvent(XtDisplay(g_ctx.widget), XtWindow(g_ctx.widget), false, 0, &ev);
    861896    XFlush(XtDisplay(g_ctx.widget));
    862     if (g_ctx.eOwner == GUEST)
    863         /* X11 may be waiting for data from VBox.  At this point it is no
    864          * longer going to arrive, and we must release it to allow the event
    865          * loop to terminate.  In this case the buffer where VBox would have
    866          * written the clipboard data will still be empty and we will just
    867          * return "no data" to X11.  Any subsequent attempts to get the data
    868          * from VBox will fail immediately as the client reference is gone. */
    869         RTSemEventSignal(g_ctx.waitForData);
    870897    do
    871898    {
     
    885912         */
    886913        RTSemEventDestroy(g_ctx.waitForData);
    887         RTSemMutexDestroy(g_ctx.asyncMutex);
     914        RTSemMutexDestroy(g_ctx.clipboardMutex);
    888915        AssertRC(rcThread);
    889916    }
     
    917944    pClient->pCtx = &g_ctx;
    918945    pClient->pCtx->pClient = pClient;
    919     g_ctx.eOwner = HOST;
     946    g_ctx.eOwner = X11;
    920947    g_ctx.notifyGuest = true;
    921948    return VINF_SUCCESS;
     
    961988    LogFlow(("vboxClipboardDisconnect\n"));
    962989
    963     RTSemMutexRequest(g_ctx.asyncMutex, RT_INDEFINITE_WAIT);
     990    RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT);
    964991    g_ctx.pClient = NULL;
    965992    g_ctx.eOwner = NONE;
    966993    g_ctx.hostTextFormat = INVALID;
    967994    g_ctx.hostBitmapFormat = INVALID;
    968     RTSemMutexRelease(g_ctx.asyncMutex);
     995    RTSemMutexRelease(g_ctx.clipboardMutex);
    969996}
    970997
     
    10251052    *piFormatReturn = 32;
    10261053    return true;
    1027 }
    1028 
    1029 /**
    1030  * Reset the contents of the buffer used to pass clipboard data from VBox to X11.
    1031  * This must be done after every clipboard transfer.
    1032  */
    1033 static void vboxClipboardEmptyGuestBuffer(void)
    1034 {
    1035     if (g_ctx.pClient->data.pv != 0)
    1036         RTMemFree(g_ctx.pClient->data.pv);
    1037     g_ctx.pClient->data.pv = 0;
    1038     g_ctx.pClient->data.cb = 0;
    1039     g_ctx.pClient->data.u32Format = 0;
    10401054}
    10411055
     
    13401354    LogFlowFunc(("\n"));
    13411355    /* Drop requests that we receive too late. */
    1342     if (g_ctx.eOwner != GUEST)
     1356    if (g_ctx.eOwner != VB)
    13431357        return false;
    13441358    if (   (*atomSelection != g_ctx.atomClipboard)
     
    14061420{
    14071421    LogFlowFunc (("called, giving VBox clipboard ownership\n"));
    1408     g_ctx.eOwner = HOST;
     1422    g_ctx.eOwner = X11;
    14091423    g_ctx.notifyGuest = true;
    14101424}
     
    14341448        return;
    14351449    }
    1436     if (g_ctx.eOwner == GUEST)
     1450    if (g_ctx.eOwner == VB)
    14371451    {
    14381452        /* We already own the clipboard, so no need to grab it, especially as that can lead
     
    14431457    }
    14441458    Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__));
    1445     g_ctx.eOwner = GUEST;
     1459    g_ctx.eOwner = VB;
    14461460    g_ctx.hostTextFormat = INVALID;
    14471461    g_ctx.hostBitmapFormat = INVALID;
     
    14531467          guest formats are found which we understand. */
    14541468        g_ctx.notifyGuest = true;
    1455         g_ctx.eOwner = HOST;
     1469        g_ctx.eOwner = X11;
    14561470    }
    14571471    XtOwnSelection(g_ctx.widget, g_ctx.atomPrimary, CurrentTime, vboxClipboardConvertForX11,
     
    15011515         * requests from VBox are serialised and the second because X11 previously
    15021516         * grabbed the clipboard, so it should not be waiting for data from us. */
    1503         AssertLogRelReturn (ASMAtomicCmpXchgU32(&g_ctx.waiter, 1, 0), VERR_DEADLOCK);
     1517        AssertLogRelReturn (ASMAtomicCmpXchgU32(&g_ctx.waiter, VB, NONE), VERR_DEADLOCK);
    15041518        g_ctx.requestHostFormat = g_ctx.hostTextFormat;
    15051519        g_ctx.requestBuffer = pv;
     
    15191533        if (RT_FAILURE(rc))
    15201534        {
    1521             g_ctx.waiter = 0;
     1535            g_ctx.waiter = NONE;
    15221536            return rc;
    15231537        }
    1524         g_ctx.waiter = 0;
     1538        g_ctx.waiter = NONE;
    15251539    }
    15261540    else
     
    15541568                            && pClient->data.u32Format == 0);
    15551569
    1556     if (cb > 0)
     1570    /* Grab the mutex and check that X11 is still waiting for the data before
     1571     * delivering it.  See the explanation in vboxClipboardReadDataFromVBox. */
     1572    RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT);
     1573    if (g_ctx.waiter == X11 && cb > 0)
    15571574    {
    15581575        pClient->data.pv = RTMemAlloc (cb);
     
    15651582        }
    15661583    }
     1584    RTSemMutexRelease(g_ctx.clipboardMutex);
    15671585
    15681586    RTSemEventSignal(g_ctx.waitForData);
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