Changeset 15928 in vbox for trunk/src/VBox/HostServices/SharedClipboard/x11.cpp
- Timestamp:
- Jan 13, 2009 9:09:28 PM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/HostServices/SharedClipboard/x11.cpp
r15898 r15928 79 79 } VBOXCLIPBOARDFORMAT; 80 80 81 /** Does the host or the guestcurrently own the clipboard? */82 enum g_e ClipboardOwner { NONE = 0, HOST, GUEST};81 /** Does X11 or VBox currently own the clipboard? */ 82 enum g_eOwner { NONE = 0, X11, VB }; 83 83 84 84 typedef struct { … … 127 127 128 128 /** Does the host or the guest currently own the clipboard? */ 129 volatile enum g_e ClipboardOwner eOwner;129 volatile enum g_eOwner eOwner; 130 130 131 131 /** What is the best text format the host has to offer? INVALID for none. */ … … 148 148 semaphore, which is triggered when the data arrives. */ 149 149 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; 159 156 160 157 /** Format which we are reading from the host clipboard (valid during a request for the … … 182 179 183 180 /** 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 */ 184 static 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 /** 184 194 * Send a request to VBox to transfer the contents of its clipboard to X11. 185 195 * … … 191 201 static int vboxClipboardReadDataFromVBox (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format) 192 202 { 193 VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient;203 volatile VBOXCLIPBOARDCLIENTDATA *pClient = pCtx->pClient; 194 204 195 205 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 } 196 215 /* Assert that no other transfer is in process (requests are serialised) 197 216 * and that the last transfer cleaned up properly. */ 198 AssertLogRelReturn( pC tx->pClient->data.pv == NULL199 && pC tx->pClient->data.cb == 0200 && pC tx->pClient->data.u32Format == 0,217 AssertLogRelReturn( pClient->data.pv == NULL 218 && pClient->data.cb == 0 219 && pClient->data.u32Format == 0, 201 220 VERR_WRONG_ORDER 202 221 ); … … 204 223 * requests from X11 are serialised and the second because VBox previously 205 224 * 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); 220 244 AssertLogRelRCSuccess(rc); 221 245 if (RT_FAILURE(rc)) … … 223 247 /* I believe this should not happen. Wait until the assertions arrive 224 248 * to prove the contrary. */ 249 vboxClipboardEmptyGuestBuffer(); 225 250 pCtx->guestFormats = 0; 226 pCtx->waiter = 0;227 251 return rc; 228 252 } 229 pCtx->waiter = 0; 253 if (pClient->data.pv == NULL) 254 return VERR_TIMEOUT; 230 255 LogFlowFunc(("wait completed. Returning.\n")); 231 256 return VINF_SUCCESS; … … 490 515 /* We grab this mutex whenever an asynchronous clipboard operation completes and while 491 516 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); 493 518 if (reinterpret_cast<VBOXCLIPBOARDCLIENTDATA *>(pClientData) != g_ctx.pClient) 494 519 { … … 496 521 XtFree(reinterpret_cast<char *>(pValue)); 497 522 LogFlowFunc(("client is no longer connected, returning\n")); 498 RTSemMutexRelease(g_ctx. asyncMutex);523 RTSemMutexRelease(g_ctx.clipboardMutex); 499 524 return; 500 525 } … … 534 559 LogFunc (("bad target format\n")); 535 560 XtFree(reinterpret_cast<char *>(pValue)); 536 RTSemMutexRelease(g_ctx. asyncMutex);561 RTSemMutexRelease(g_ctx.clipboardMutex); 537 562 return; 538 563 } 539 564 g_ctx.notifyGuest = true; 540 RTSemMutexRelease(g_ctx. asyncMutex);565 RTSemMutexRelease(g_ctx.clipboardMutex); 541 566 } 542 567 … … 567 592 /* We grab this mutex whenever an asynchronous clipboard operation completes and while 568 593 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); 570 595 if (reinterpret_cast<VBOXCLIPBOARDCLIENTDATA *>(pClientData) != g_ctx.pClient) 571 596 { 572 597 /* If the client is no longer connected, just return. */ 573 598 LogFlowFunc(("client is no longer connected, returning\n")); 574 RTSemMutexRelease(g_ctx. asyncMutex);599 RTSemMutexRelease(g_ctx.clipboardMutex); 575 600 return; 576 601 } … … 633 658 } 634 659 XtFree(reinterpret_cast<char *>(pValue)); 635 RTSemMutexRelease(g_ctx. asyncMutex);660 RTSemMutexRelease(g_ctx.clipboardMutex); 636 661 } 637 662 … … 646 671 Log3 (("%s: called\n", __PRETTY_FUNCTION__)); 647 672 /* 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) 649 674 { 650 675 Log3 (("%s: requesting the targets that the host clipboard offers\n", … … 812 837 LogRel(("Initializing host clipboard service\n")); 813 838 RTSemEventCreate(&g_ctx.waitForData); 814 RTSemMutexCreate(&g_ctx. asyncMutex);839 RTSemMutexCreate(&g_ctx.clipboardMutex); 815 840 rc = vboxClipboardInitX11(); 816 841 if (RT_SUCCESS(rc)) … … 824 849 { 825 850 RTSemEventDestroy(g_ctx.waitForData); 826 RTSemMutexDestroy(g_ctx. asyncMutex);851 RTSemMutexDestroy(g_ctx.clipboardMutex); 827 852 } 828 853 return rc; … … 852 877 * immediately. */ 853 878 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. */ 855 890 XtAppSetExitFlag(g_ctx.appContext); 856 891 /* Wake up the event loop */ … … 860 895 XSendEvent(XtDisplay(g_ctx.widget), XtWindow(g_ctx.widget), false, 0, &ev); 861 896 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 no864 * longer going to arrive, and we must release it to allow the event865 * loop to terminate. In this case the buffer where VBox would have866 * written the clipboard data will still be empty and we will just867 * return "no data" to X11. Any subsequent attempts to get the data868 * from VBox will fail immediately as the client reference is gone. */869 RTSemEventSignal(g_ctx.waitForData);870 897 do 871 898 { … … 885 912 */ 886 913 RTSemEventDestroy(g_ctx.waitForData); 887 RTSemMutexDestroy(g_ctx. asyncMutex);914 RTSemMutexDestroy(g_ctx.clipboardMutex); 888 915 AssertRC(rcThread); 889 916 } … … 917 944 pClient->pCtx = &g_ctx; 918 945 pClient->pCtx->pClient = pClient; 919 g_ctx.eOwner = HOST;946 g_ctx.eOwner = X11; 920 947 g_ctx.notifyGuest = true; 921 948 return VINF_SUCCESS; … … 961 988 LogFlow(("vboxClipboardDisconnect\n")); 962 989 963 RTSemMutexRequest(g_ctx. asyncMutex, RT_INDEFINITE_WAIT);990 RTSemMutexRequest(g_ctx.clipboardMutex, RT_INDEFINITE_WAIT); 964 991 g_ctx.pClient = NULL; 965 992 g_ctx.eOwner = NONE; 966 993 g_ctx.hostTextFormat = INVALID; 967 994 g_ctx.hostBitmapFormat = INVALID; 968 RTSemMutexRelease(g_ctx. asyncMutex);995 RTSemMutexRelease(g_ctx.clipboardMutex); 969 996 } 970 997 … … 1025 1052 *piFormatReturn = 32; 1026 1053 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;1040 1054 } 1041 1055 … … 1340 1354 LogFlowFunc(("\n")); 1341 1355 /* Drop requests that we receive too late. */ 1342 if (g_ctx.eOwner != GUEST)1356 if (g_ctx.eOwner != VB) 1343 1357 return false; 1344 1358 if ( (*atomSelection != g_ctx.atomClipboard) … … 1406 1420 { 1407 1421 LogFlowFunc (("called, giving VBox clipboard ownership\n")); 1408 g_ctx.eOwner = HOST;1422 g_ctx.eOwner = X11; 1409 1423 g_ctx.notifyGuest = true; 1410 1424 } … … 1434 1448 return; 1435 1449 } 1436 if (g_ctx.eOwner == GUEST)1450 if (g_ctx.eOwner == VB) 1437 1451 { 1438 1452 /* We already own the clipboard, so no need to grab it, especially as that can lead … … 1443 1457 } 1444 1458 Log2 (("%s: giving the guest clipboard ownership\n", __PRETTY_FUNCTION__)); 1445 g_ctx.eOwner = GUEST;1459 g_ctx.eOwner = VB; 1446 1460 g_ctx.hostTextFormat = INVALID; 1447 1461 g_ctx.hostBitmapFormat = INVALID; … … 1453 1467 guest formats are found which we understand. */ 1454 1468 g_ctx.notifyGuest = true; 1455 g_ctx.eOwner = HOST;1469 g_ctx.eOwner = X11; 1456 1470 } 1457 1471 XtOwnSelection(g_ctx.widget, g_ctx.atomPrimary, CurrentTime, vboxClipboardConvertForX11, … … 1501 1515 * requests from VBox are serialised and the second because X11 previously 1502 1516 * 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); 1504 1518 g_ctx.requestHostFormat = g_ctx.hostTextFormat; 1505 1519 g_ctx.requestBuffer = pv; … … 1519 1533 if (RT_FAILURE(rc)) 1520 1534 { 1521 g_ctx.waiter = 0;1535 g_ctx.waiter = NONE; 1522 1536 return rc; 1523 1537 } 1524 g_ctx.waiter = 0;1538 g_ctx.waiter = NONE; 1525 1539 } 1526 1540 else … … 1554 1568 && pClient->data.u32Format == 0); 1555 1569 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) 1557 1574 { 1558 1575 pClient->data.pv = RTMemAlloc (cb); … … 1565 1582 } 1566 1583 } 1584 RTSemMutexRelease(g_ctx.clipboardMutex); 1567 1585 1568 1586 RTSemEventSignal(g_ctx.waitForData);
Note:
See TracChangeset
for help on using the changeset viewer.