VirtualBox

Changeset 68699 in vbox for trunk/src/VBox/Devices


Ignore:
Timestamp:
Sep 7, 2017 3:12:54 PM (7 years ago)
Author:
vboxsync
Message:

pdmifs.h,Serial: Reworked stream interface. The old design with the two read/write threads had a race where the read thread could access already destroyed VMM structures during destruction if data was read. This was solved by adding a poll callback which waits for data to arrive and which can be interrupt to make the thread respond to VM state changes and suspend before destruction starts. This required reworking all the drivers using it. DrvTCP was reworked to make use of the RTTcp*, RTSocket* and RTPoll* API in that process to get rid of platform dependent code there (which wasn't all available when the driver was createt).

Location:
trunk/src/VBox/Devices/Serial
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Serial/DrvChar.cpp

    r68637 r68699  
    3030#include <iprt/asm.h>
    3131#include <iprt/assert.h>
     32#include <iprt/poll.h>
    3233#include <iprt/stream.h>
    3334#include <iprt/semaphore.h>
     
    4041*   Defined Constants And Macros                                                                                                 *
    4142*********************************************************************************************************************************/
    42 /** Converts a pointer to DRVCHAR::ICharConnector to a PDRVCHAR. */
    43 #define PDMICHAR_2_DRVCHAR(pInterface)  RT_FROM_MEMBER(pInterface, DRVCHAR, ICharConnector)
    4443
    4544
     
    6463    /** Flag to notify the receive thread it should terminate. */
    6564    volatile bool               fShutdown;
    66     /** Receive thread ID. */
    67     RTTHREAD                    ReceiveThread;
    68     /** Send thread ID. */
    69     RTTHREAD                    SendThread;
    70     /** Send event semaphore */
    71     RTSEMEVENT                  SendSem;
     65    /** I/O thread. */
     66    PPDMTHREAD                  pThrdIo;
     67    /** Thread to relay read data to the device above without
     68     * blocking send operations.
     69     * @todo: This has to go but needs changes in the interface
     70     *        between device and driver.
     71     */
     72    PPDMTHREAD                  pThrdRead;
     73    /** Event semaphore for the read relay thread. */
     74    RTSEMEVENT                  hEvtSemRead;
    7275
    7376    /** Internal send FIFO queue */
     
    7578    bool volatile               fSending;
    7679    uint8_t                     Alignment[2];
     80
     81    /** Receive buffer. */
     82    uint8_t                     abBuffer[256];
     83    /** Number of bytes remaining in the receive buffer. */
     84    volatile size_t             cbRemaining;
     85    /** Current position into the read buffer. */
     86    uint8_t                     *pbBuf;
    7787
    7888    /** Read/write statistics */
     
    108118static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
    109119{
    110     PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface);
     120    PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ICharConnector);
    111121    const char *pbBuffer = (const char *)pvBuf;
    112122
     
    119129
    120130        pThis->u8SendByte = pbBuffer[i];
    121         RTSemEventSignal(pThis->SendSem);
     131        pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
    122132        STAM_COUNTER_INC(&pThis->StatBytesWritten);
    123133    }
     
    133143{
    134144    RT_NOREF(pInterface, Bps, chParity, cDataBits, cStopBits);
    135     /*PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
    136145
    137146    LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
     
    140149
    141150
    142 /* -=-=-=-=- receive thread -=-=-=-=- */
     151/* -=-=-=-=- I/O thread -=-=-=-=- */
    143152
    144153/**
     
    149158 * @param   pvUser      User argument.
    150159 */
    151 static DECLCALLBACK(int) drvCharSendLoop(RTTHREAD hThreadSelf, void *pvUser)
    152 {
    153     RT_NOREF(hThreadSelf);
    154     PDRVCHAR pThis = (PDRVCHAR)pvUser;
    155 
    156     int rc = VINF_SUCCESS;
    157     while (!pThis->fShutdown)
     160static DECLCALLBACK(int) drvCharIoLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     161{
     162    RT_NOREF(pDrvIns);
     163    PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
     164
     165    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
     166        return VINF_SUCCESS;
     167
     168    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
    158169    {
    159         RTMSINTERVAL cMillies = (rc == VERR_TIMEOUT) ? 50 : RT_INDEFINITE_WAIT;
    160         rc = RTSemEventWait(pThis->SendSem, cMillies);
    161         if (    RT_FAILURE(rc)
    162              && rc != VERR_TIMEOUT)
    163             break;
    164 
    165         /*
    166          * Write the character to the attached stream (if present).
    167          */
    168         if (    pThis->fShutdown
    169             ||  !pThis->pDrvStream)
    170             break;
    171 
    172         size_t cbProcessed = 1;
    173         uint8_t ch = pThis->u8SendByte;
    174         rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &ch, &cbProcessed);
     170        uint32_t fEvts = 0;
     171
     172        if (   !pThis->cbRemaining
     173            && pThis->pDrvStream->pfnRead)
     174            fEvts |= RTPOLL_EVT_READ;
     175        if (pThis->fSending)
     176            fEvts |= RTPOLL_EVT_WRITE;
     177
     178        uint32_t fEvtsRecv = 0;
     179        int rc = pThis->pDrvStream->pfnPoll(pThis->pDrvStream, fEvts, &fEvtsRecv, RT_INDEFINITE_WAIT);
    175180        if (RT_SUCCESS(rc))
    176181        {
    177             ASMAtomicXchgBool(&pThis->fSending, false);
    178             Assert(cbProcessed == 1);
    179         }
    180         else if (rc == VERR_TIMEOUT)
    181         {
    182             /* Normal case, just means that the stream didn't accept a new
    183              * character before the timeout elapsed. Just retry. */
    184 
    185             /* do not change the rc status here, otherwise the (rc == VERR_TIMEOUT) branch
    186              * in the wait above will never get executed */
    187             /* rc = VINF_SUCCESS; */
    188         }
    189         else
    190         {
    191             LogRel(("Write failed with %Rrc; skipping\n", rc));
    192             break;
    193         }
    194     }
    195 
    196     return VINF_SUCCESS;
    197 }
    198 
    199 
    200 /* -=-=-=-=- receive thread -=-=-=-=- */
    201 
    202 /**
    203  * Receive thread loop.
    204  *
    205  * @returns 0 on success.
    206  * @param   hThreadSelf Thread handle to this thread.
    207  * @param   pvUser      User argument.
    208  *
    209  * @todo    This thread isn't managed correctly wrt to the VM state.
    210  * @todo    This thread isn't managed correctly wrt to the VM state.
    211  * @todo    This thread isn't managed correctly wrt to the VM state.
    212  * @todo    This thread isn't managed correctly wrt to the VM state.
    213  * @todo    This thread isn't managed correctly wrt to the VM state.
    214  * @todo    This thread isn't managed correctly wrt to the VM state.
    215  * @todo    This thread isn't managed correctly wrt to the VM state.
    216  * @todo    This thread isn't managed correctly wrt to the VM state.
    217  * @todo    This thread isn't managed correctly wrt to the VM state.
    218  * @todo    This thread isn't managed correctly wrt to the VM state.
    219  * @todo    This thread isn't managed correctly wrt to the VM state.
    220  * @todo    This thread isn't managed correctly wrt to the VM state.
    221  * @todo    This thread isn't managed correctly wrt to the VM state.
    222  * @todo    This thread isn't managed correctly wrt to the VM state.
    223  * @todo    This thread isn't managed correctly wrt to the VM state.
    224  * @todo    This thread isn't managed correctly wrt to the VM state.
    225  * @todo    This thread isn't managed correctly wrt to the VM state.
    226  * @todo    This thread isn't managed correctly wrt to the VM state.
    227  * @todo    This thread isn't managed correctly wrt to the VM state.
    228  * @todo    This thread isn't managed correctly wrt to the VM state.
    229  * @todo    This thread isn't managed correctly wrt to the VM state.
    230  * @todo    This thread isn't managed correctly wrt to the VM state.
    231  * @todo    This thread isn't managed correctly wrt to the VM state.
    232  * @todo    This thread isn't managed correctly wrt to the VM state.
    233  * @todo    This thread isn't managed correctly wrt to the VM state.
    234  * @todo    This thread isn't managed correctly wrt to the VM state.
    235  * @todo    This thread isn't managed correctly wrt to the VM state.
    236  * @todo    This thread isn't managed correctly wrt to the VM state.
    237  * @todo    This thread isn't managed correctly wrt to the VM state.
    238  * @todo    This thread isn't managed correctly wrt to the VM state.
    239  * @todo    This thread isn't managed correctly wrt to the VM state.
    240  * @todo    This thread isn't managed correctly wrt to the VM state.
    241  *
    242  *          It's possible to end up in the APIC code while the VM is being destroyed!
    243  * @code
    244 0:018> k
    245  # Child-SP          RetAddr           Call Site
    246 00 00000000`2061f1b0 00007ffe`42a889d9 VBoxVMM!apicReadRaw32+0x78 [e:\vbox\svn\trunk\src\vbox\vmm\vmmall\apicall.cpp @ 462]
    247 01 00000000`2061f1f0 00007ffe`42916fc2 VBoxVMM!APICLocalInterrupt+0x189 [e:\vbox\svn\trunk\src\vbox\vmm\vmmall\apicall.cpp @ 2524]
    248 02 00000000`2061f320 00007ffe`41b96937 VBoxVMM!pdmR3PicHlp_ClearInterruptFF+0x172 [e:\vbox\svn\trunk\src\vbox\vmm\vmmr3\pdmdevmischlp.cpp @ 67]
    249 03 00000000`2061f360 00007ffe`41b961a8 VBoxDD!pic_update_irq+0x327 [e:\vbox\svn\trunk\src\vbox\devices\pc\devpic.cpp @ 318]
    250 04 00000000`2061f3f0 00007ffe`42bcbaa3 VBoxDD!picSetIrq+0x2b8 [e:\vbox\svn\trunk\src\vbox\devices\pc\devpic.cpp @ 352]
    251 05 00000000`2061f450 00007ffe`42905dff VBoxVMM!PDMIsaSetIrq+0xe3 [e:\vbox\svn\trunk\src\vbox\vmm\vmmall\pdmall.cpp @ 138]
    252 06 00000000`2061f490 00007ffe`41c06db4 VBoxVMM!pdmR3DevHlp_ISASetIrq+0x2df [e:\vbox\svn\trunk\src\vbox\vmm\vmmr3\pdmdevhlp.cpp @ 1804]
    253 07 00000000`2061f500 00007ffe`41c08d39 VBoxDD!PDMDevHlpISASetIrqNoWait+0x44 [e:\vbox\svn\trunk\include\vbox\vmm\pdmdev.h @ 4972]
    254 08 00000000`2061f530 00007ffe`41c08a44 VBoxDD!serial_update_irq+0x1c9 [e:\vbox\svn\trunk\src\vbox\devices\serial\devserial.cpp @ 326]
    255 09 00000000`2061f590 00007ffe`41c08814 VBoxDD!serial_receive+0x134 [e:\vbox\svn\trunk\src\vbox\devices\serial\devserial.cpp @ 718]
    256 0a 00000000`2061f5d0 00007ffe`41c1d894 VBoxDD!serialNotifyRead+0x124 [e:\vbox\svn\trunk\src\vbox\devices\serial\devserial.cpp @ 744]
    257 0b 00000000`2061f630 00007ffe`43a6250f VBoxDD!drvCharReceiveLoop+0x194 [e:\vbox\svn\trunk\src\vbox\devices\serial\drvchar.cpp @ 241]
    258 0c 00000000`2061f800 00007ffe`43b8ddbf VBoxRT!rtThreadMain+0x1bf [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\thread.cpp @ 717]
    259 0d 00000000`2061f880 00000000`52971d9f VBoxRT!rtThreadNativeMain+0xcf [e:\vbox\svn\trunk\src\vbox\runtime\r3\win\thread-win.cpp @ 252]
    260 0e 00000000`2061f8d0 00000000`52971e3b MSVCR100!endthreadex+0x43
    261 @endcode
    262  *
    263  */
    264 static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD hThreadSelf, void *pvUser)
    265 {
    266     RT_NOREF(hThreadSelf);
    267     PDRVCHAR    pThis = (PDRVCHAR)pvUser;
    268     char        abBuffer[256];
    269     char       *pbRemaining = abBuffer;
    270     size_t      cbRemaining = 0;
    271     int         rc;
    272 
    273     while (!pThis->fShutdown)
    274     {
    275         if (!cbRemaining)
    276         {
    277             /* Get block of data from stream driver. */
    278             if (pThis->pDrvStream)
     182            if (fEvtsRecv & RTPOLL_EVT_WRITE)
    279183            {
    280                 pbRemaining = abBuffer;
    281                 cbRemaining = sizeof(abBuffer);
    282                 rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, abBuffer, &cbRemaining);
     184                Assert(pThis->fSending);
     185
     186                size_t cbProcessed = 1;
     187                uint8_t ch = pThis->u8SendByte;
     188                rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &ch, &cbProcessed);
     189                if (RT_SUCCESS(rc))
     190                {
     191                    ASMAtomicXchgBool(&pThis->fSending, false);
     192                    Assert(cbProcessed == 1);
     193                }
     194                else if (rc == VERR_TIMEOUT)
     195                {
     196                    /* Normal case, just means that the stream didn't accept a new
     197                     * character before the timeout elapsed. Just retry. */
     198
     199                    /* do not change the rc status here, otherwise the (rc == VERR_TIMEOUT) branch
     200                     * in the wait above will never get executed */
     201                    /* rc = VINF_SUCCESS; */
     202                }
     203                else
     204                {
     205                    LogRel(("Write failed with %Rrc; skipping\n", rc));
     206                    break;
     207                }
     208            }
     209
     210            if (fEvtsRecv & RTPOLL_EVT_READ)
     211            {
     212                AssertPtr(pThis->pDrvStream->pfnRead);
     213                Assert(!pThis->cbRemaining);
     214
     215                size_t cbRead = sizeof(pThis->abBuffer);
     216                rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, &pThis->abBuffer[0], &cbRead);
    283217                if (RT_FAILURE(rc))
    284218                {
     
    286220                    break;
    287221                }
     222                pThis->pbBuf = &pThis->abBuffer[0];
     223                ASMAtomicWriteZ(&pThis->cbRemaining, cbRead);
     224                RTSemEventSignal(pThis->hEvtSemRead); /* Wakeup relay thread to continue. */
    288225            }
    289             else
    290                 RTThreadSleep(100);
    291226        }
    292         else
     227        else if (rc != VERR_INTERRUPTED)
     228            LogRelMax(10, ("Char#%d: Polling failed with %Rrc\n", pDrvIns->iInstance, rc));
     229    }
     230
     231    return VINF_SUCCESS;
     232}
     233
     234
     235/**
     236 * Unblock the send worker thread so it can respond to a state change.
     237 *
     238 * @returns VBox status code.
     239 * @param   pDrvIns     The char driver instance.
     240 * @param   pThread     The worker thread.
     241 */
     242static DECLCALLBACK(int) drvCharIoLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     243{
     244    PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
     245
     246    RT_NOREF(pDrvIns);
     247    return pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
     248}
     249
     250
     251static DECLCALLBACK(int) drvCharReadRelayLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     252{
     253    RT_NOREF(pDrvIns);
     254    PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
     255
     256    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
     257        return VINF_SUCCESS;
     258
     259    int rc = VINF_SUCCESS;
     260    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
     261    {
     262        size_t cbRem = ASMAtomicReadZ(&pThis->cbRemaining);
     263
     264        /* Block as long as there is nothing to relay. */
     265        if (!pThis->cbRemaining)
     266            rc = RTSemEventWait(pThis->hEvtSemRead, RT_INDEFINITE_WAIT);
     267
     268        if (pThread->enmState != PDMTHREADSTATE_RUNNING)
     269            break;
     270
     271        cbRem = ASMAtomicReadZ(&pThis->cbRemaining);
     272        if (cbRem)
    293273        {
    294274            /* Send data to guest. */
    295             size_t cbProcessed = cbRemaining;
    296             rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbRemaining, &cbProcessed);
     275            size_t cbProcessed = cbRem;
     276            rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pThis->pbBuf, &cbProcessed);
    297277            if (RT_SUCCESS(rc))
    298278            {
    299279                Assert(cbProcessed);
    300                 pbRemaining += cbProcessed;
    301                 cbRemaining -= cbProcessed;
     280                pThis->pbBuf += cbProcessed;
     281
     282                /* Wake up the I/o thread so it can read new data to process. */
     283                cbRem = ASMAtomicSubZ(&pThis->cbRemaining, cbProcessed);
     284                if (cbRem == cbProcessed)
     285                    pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
    302286                STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
    303287            }
     
    321305
    322306/**
     307 * Unblock the read relay worker thread so it can respond to a state change.
     308 *
     309 * @returns VBox status code.
     310 * @param   pDrvIns     The char driver instance.
     311 * @param   pThread     The worker thread.
     312 */
     313static DECLCALLBACK(int) drvCharReadRelayLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     314{
     315    PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
     316
     317    RT_NOREF(pDrvIns);
     318    return RTSemEventSignal(pThis->hEvtSemRead);
     319}
     320
     321
     322/**
    323323 * @callback_method_impl{PDMICHARCONNECTOR,pfnSetModemLines}
    324324 */
     
    358358    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
    359359
    360     /*
    361      * Tell the threads to shut down.
    362      */
    363     pThis->fShutdown = true;
    364     if (pThis->SendSem != NIL_RTSEMEVENT)
     360    if (pThis->hEvtSemRead != NIL_RTSEMEVENT)
    365361    {
    366         RTSemEventSignal(pThis->SendSem);
    367         pThis->SendSem = NIL_RTSEMEVENT;
    368     }
    369 
    370     /*
    371      * Wait for the threads.
    372      * ASSUMES that PDM destroys the driver chain from the bottom and up.
    373      */
    374     if (pThis->ReceiveThread != NIL_RTTHREAD)
    375     {
    376         int rc = RTThreadWait(pThis->ReceiveThread, 30000, NULL);
    377         if (RT_SUCCESS(rc))
    378             pThis->ReceiveThread = NIL_RTTHREAD;
    379         else
    380             LogRel(("Char%d: receive thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
    381     }
    382 
    383     if (pThis->SendThread != NIL_RTTHREAD)
    384     {
    385         int rc = RTThreadWait(pThis->SendThread, 30000, NULL);
    386         if (RT_SUCCESS(rc))
    387             pThis->SendThread = NIL_RTTHREAD;
    388         else
    389             LogRel(("Char%d: send thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
    390     }
    391 
    392     if (pThis->SendSem != NIL_RTSEMEVENT)
    393     {
    394         RTSemEventDestroy(pThis->SendSem);
    395         pThis->SendSem = NIL_RTSEMEVENT;
     362        RTSemEventDestroy(pThis->hEvtSemRead);
     363        pThis->hEvtSemRead = NIL_RTSEMEVENT;
    396364    }
    397365}
     
    413381     * Init basic data members and interfaces.
    414382     */
    415     pThis->fShutdown                        = false;
    416     pThis->ReceiveThread                    = NIL_RTTHREAD;
    417     pThis->SendThread                       = NIL_RTTHREAD;
    418     pThis->SendSem                          = NIL_RTSEMEVENT;
     383    pThis->pDrvIns                          = pDrvIns;
     384    pThis->pThrdIo                          = NIL_RTTHREAD;
     385    pThis->pThrdRead                        = NIL_RTTHREAD;
     386    pThis->hEvtSemRead                      = NIL_RTSEMEVENT;
    419387    /* IBase. */
    420388    pDrvIns->IBase.pfnQueryInterface        = drvCharQueryInterface;
     
    443411        return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
    444412
    445     /*
    446      * Don't start the receive thread if the driver doesn't support reading
    447      */
     413    /* Don't start the receive relay thread if reading is not supported. */
    448414    if (pThis->pDrvStream->pfnRead)
    449415    {
    450         rc = RTThreadCreate(&pThis->ReceiveThread, drvCharReceiveLoop, (void *)pThis, 0,
    451                             RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharRecv");
     416        rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdRead, pThis, drvCharReadRelayLoop,
     417                                   drvCharReadRelayLoopWakeup, 0, RTTHREADTYPE_IO, "CharReadRel");
    452418        if (RT_FAILURE(rc))
    453             return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
     419            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create read relay thread"), pDrvIns->iInstance);
     420
     421         rc = RTSemEventCreate(&pThis->hEvtSemRead);
     422         AssertRCReturn(rc, rc);
    454423    }
    455424
    456     rc = RTSemEventCreate(&pThis->SendSem);
    457     AssertRCReturn(rc, rc);
    458 
    459     rc = RTThreadCreate(&pThis->SendThread, drvCharSendLoop, (void *)pThis, 0,
    460                         RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharSend");
     425    rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdIo, pThis, drvCharIoLoop,
     426                               drvCharIoLoopWakeup, 0, RTTHREADTYPE_IO, "CharIo");
    461427    if (RT_FAILURE(rc))
    462428        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
  • trunk/src/VBox/Devices/Serial/DrvNamedPipe.cpp

    r62956 r68699  
    1 /* $Id$ */
     1  /* $Id$ */
    22/** @file
    33 * Named pipe / local socket stream driver.
     
    2626#include <iprt/stream.h>
    2727#include <iprt/alloc.h>
     28#include <iprt/pipe.h>
     29#include <iprt/poll.h>
    2830#include <iprt/string.h>
    2931#include <iprt/semaphore.h>
     32#include <iprt/socket.h>
    3033#include <iprt/uuid.h>
    3134
     
    4952*   Defined Constants And Macros                                                                                                 *
    5053*********************************************************************************************************************************/
    51 /** Converts a pointer to DRVNAMEDPIPE::IMedia to a PDRVNAMEDPIPE. */
    52 #define PDMISTREAM_2_DRVNAMEDPIPE(pInterface) ( (PDRVNAMEDPIPE)((uintptr_t)pInterface - RT_OFFSETOF(DRVNAMEDPIPE, IStream)) )
    53 
     54
     55#ifndef RT_OS_WINDOWS
     56# define DRVNAMEDPIPE_POLLSET_ID_SOCKET 0
     57# define DRVNAMEDPIPE_POLLSET_ID_WAKEUP 1
     58#endif
     59
     60# define DRVNAMEDPIPE_WAKEUP_REASON_EXTERNAL       0
     61# define DRVNAMEDPIPE_WAKEUP_REASON_NEW_CONNECTION 1
    5462
    5563/*********************************************************************************************************************************
     
    7482    /** File handle of the named pipe. */
    7583    HANDLE              NamedPipe;
     84    /** The wake event handle. */
     85    HANDLE              hEvtWake;
    7686    /** Overlapped structure for writes. */
    7787    OVERLAPPED          OverlappedWrite;
     
    8090    /** Listen thread wakeup semaphore */
    8191    RTSEMEVENTMULTI     ListenSem;
     92    /** Read buffer. */
     93    uint8_t             abBufRead[32];
     94    /** Write buffer. */
     95    uint8_t             abBufWrite[32];
     96    /** Read buffer currently used. */
     97    size_t              cbReadBufUsed;
     98    /** Size of the write buffer used. */
     99    size_t              cbWriteBufUsed;
     100    /** Flag whether a wake operation was caused by an external trigger. */
     101    volatile bool       fWakeExternal;
     102    /** Flag whether a read was started. */
     103    bool                fReadPending;
    82104#else /* !RT_OS_WINDOWS */
     105    /** Poll set used to wait for I/O events. */
     106    RTPOLLSET           hPollSet;
     107    /** Reading end of the wakeup pipe. */
     108    RTPIPE              hPipeWakeR;
     109    /** Writing end of the wakeup pipe. */
     110    RTPIPE              hPipeWakeW;
     111    /** Socket handle. */
     112    RTSOCKET            hSock;
     113    /** Flag whether the socket is in the pollset. */
     114    bool                fSockInPollSet;
    83115    /** Socket handle of the local socket for server. */
    84116    int                 LocalSocketServer;
    85     /** Socket handle of the local socket. */
    86     int                 LocalSocket;
    87117#endif /* !RT_OS_WINDOWS */
    88118    /** Thread for listening for new connections. */
     
    98128
    99129
     130/**
     131 * Kicks any possibly polling thread to get informed about changes.
     132 *
     133 * @returns VBOx status code.
     134 * @param   pThis                  The named pipe driver instance.
     135 * @param   bReason                The reason code to handle.
     136 */
     137static int drvNamedPipePollerKick(PDRVNAMEDPIPE pThis, uint8_t bReason)
     138{
     139#ifdef RT_OS_WINDOWS
     140    if (bReason == DRVNAMEDPIPE_WAKEUP_REASON_EXTERNAL)
     141        ASMAtomicXchgBool(&pThis->fWakeExternal, true);
     142    if (!SetEvent(pThis->hEvtWake))
     143        return RTErrConvertFromWin32(GetLastError());
     144
     145    return VINF_SUCCESS;
     146#else
     147    size_t cbWritten = 0;
     148    return RTPipeWrite(pThis->hPipeWakeW, &bReason, 1, &cbWritten);
     149#endif
     150}
     151
     152
     153/** @interface_method_impl{PDMISTREAM,pfnPoll} */
     154static DECLCALLBACK(int) drvNamedPipePoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies)
     155{
     156    int rc = VINF_SUCCESS;
     157    PDRVNAMEDPIPE pThis = RT_FROM_MEMBER(pInterface, DRVNAMEDPIPE, IStream);
     158
     159    LogFlowFunc(("pInterface=%#p fEvts=%#x pfEvts=%#p cMillies=%u\n", pInterface, fEvts, pfEvts, cMillies));
     160
     161#ifdef RT_OS_WINDOWS
     162    /* Immediately return if there is something to read or no write pending and the respective events are set. */
     163    *pfEvts = 0;
     164    if (   (fEvts & RTPOLL_EVT_READ)
     165        && pThis->cbReadBufUsed > 0)
     166        *pfEvts |= RTPOLL_EVT_READ;
     167    if (   (fEvts & RTPOLL_EVT_WRITE)
     168        && !pThis->cbWriteBufUsed)
     169        *pfEvts |= RTPOLL_EVT_WRITE;
     170
     171    if (*pfEvts)
     172        return VINF_SUCCESS;
     173
     174    while (RT_SUCCESS(rc))
     175    {
     176        /* Set up the waiting handles. */
     177        HANDLE ahEvts[3];
     178        unsigned cEvts = 0;
     179
     180        ahEvts[cEvts++] = pThis->hEvtWake;
     181        if (fEvts & RTPOLL_EVT_WRITE)
     182        {
     183            Assert(pThis->cbWriteBufUsed);
     184            ahEvts[cEvts++] = pThis->OverlappedWrite.hEvent;
     185        }
     186        if (   (fEvts & RTPOLL_EVT_READ)
     187            && pThis->NamedPipe != INVALID_HANDLE_VALUE
     188            && !pThis->fReadPending)
     189        {
     190            Assert(!pThis->cbReadBufUsed);
     191
     192            DWORD cbReallyRead;
     193            pThis->OverlappedRead.Offset     = 0;
     194            pThis->OverlappedRead.OffsetHigh = 0;
     195            if (!ReadFile(pThis->NamedPipe, &pThis->abBufRead[0], sizeof(pThis->abBufRead), &cbReallyRead, &pThis->OverlappedRead))
     196            {
     197                DWORD uError = GetLastError();
     198
     199                if (uError == ERROR_IO_PENDING)
     200                {
     201                    uError = 0;
     202                    pThis->fReadPending = true;
     203                }
     204
     205                if (   uError == ERROR_PIPE_LISTENING
     206                    || uError == ERROR_PIPE_NOT_CONNECTED)
     207                {
     208                    /* No connection yet/anymore */
     209                    cbReallyRead = 0;
     210                }
     211                else
     212                {
     213                    rc = RTErrConvertFromWin32(uError);
     214                    Log(("drvNamedPipePoll: ReadFile returned %d (%Rrc)\n", uError, rc));
     215                }
     216            }
     217            else
     218            {
     219                LogFlowFunc(("Read completed: cbReallyRead=%u\n", cbReallyRead));
     220                pThis->fReadPending = false;
     221                *pfEvts |= RTPOLL_EVT_READ;
     222                return VINF_SUCCESS;
     223            }
     224
     225            if (RT_FAILURE(rc))
     226            {
     227                Log(("drvNamedPipePoll: FileRead returned %Rrc fShutdown=%d\n", rc, pThis->fShutdown));
     228                if (    !pThis->fShutdown
     229                    &&  (   rc == VERR_EOF
     230                         || rc == VERR_BROKEN_PIPE
     231                        )
     232                   )
     233                {
     234                    FlushFileBuffers(pThis->NamedPipe);
     235                    DisconnectNamedPipe(pThis->NamedPipe);
     236                    if (!pThis->fIsServer)
     237                    {
     238                        CloseHandle(pThis->NamedPipe);
     239                        pThis->NamedPipe = INVALID_HANDLE_VALUE;
     240                    }
     241                    /* pretend success */
     242                    rc = VINF_SUCCESS;
     243                }
     244                cbReallyRead = 0;
     245            }
     246        }
     247
     248        if (pThis->fReadPending)
     249            ahEvts[cEvts++] = pThis->OverlappedRead.hEvent;
     250
     251        DWORD dwMillies = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies;
     252        DWORD uErr = WaitForMultipleObjects(cEvts, &ahEvts[0], FALSE /* bWaitAll */, dwMillies);
     253        if (uErr == WAIT_TIMEOUT)
     254            rc = VERR_TIMEOUT;
     255        else if (uErr == WAIT_FAILED)
     256            rc = RTErrConvertFromWin32(GetLastError());
     257        else
     258        {
     259            /* Something triggered. */
     260            unsigned idxEvt = uErr - WAIT_OBJECT_0;
     261            Assert(idxEvt < cEvts);
     262
     263            LogFlowFunc(("Interrupted by pipe activity: idxEvt=%u\n", idxEvt));
     264
     265            if (idxEvt == 0)
     266            {
     267                /* The wakeup triggered. */
     268                if (ASMAtomicXchgBool(&pThis->fWakeExternal, false))
     269                    rc = VERR_INTERRUPTED;
     270                else
     271                {
     272                    /*
     273                     * Internal event because there was a new connection from the listener thread,
     274                     * restart everything.
     275                     */
     276                    rc = VINF_SUCCESS;
     277                }
     278            }
     279            else if (ahEvts[idxEvt] == pThis->OverlappedWrite.hEvent)
     280            {
     281                LogFlowFunc(("Write completed\n"));
     282                /* Fetch the result of the write. */
     283                DWORD cbWritten = 0;
     284                if (GetOverlappedResult(pThis->NamedPipe, &pThis->OverlappedWrite, &cbWritten, TRUE) == FALSE)
     285                {
     286                    uErr = GetLastError();
     287                    rc = RTErrConvertFromWin32(uErr);
     288                    Log(("drvNamedPipePoll: Write completed with %d (%Rrc)\n", uErr, rc));
     289
     290                    if (RT_FAILURE(rc))
     291                    {
     292                        /** @todo WriteFile(pipe) has been observed to return  ERROR_NO_DATA
     293                         *        (VERR_NO_DATA) instead of ERROR_BROKEN_PIPE, when the pipe is
     294                         *        disconnected. */
     295                        if (    rc == VERR_EOF
     296                            ||  rc == VERR_BROKEN_PIPE)
     297                        {
     298                            FlushFileBuffers(pThis->NamedPipe);
     299                            DisconnectNamedPipe(pThis->NamedPipe);
     300                            if (!pThis->fIsServer)
     301                            {
     302                                CloseHandle(pThis->NamedPipe);
     303                                pThis->NamedPipe = INVALID_HANDLE_VALUE;
     304                            }
     305                            /* pretend success */
     306                            rc = VINF_SUCCESS;
     307                        }
     308                        cbWritten = (DWORD)pThis->cbWriteBufUsed;
     309                    }
     310                }
     311
     312                pThis->cbWriteBufUsed -= cbWritten;
     313                if (!pThis->cbWriteBufUsed && (fEvts & RTPOLL_EVT_WRITE))
     314                {
     315                    *pfEvts |= RTPOLL_EVT_WRITE;
     316                    break;
     317                }
     318            }
     319            else
     320            {
     321                Assert(ahEvts[idxEvt] == pThis->OverlappedRead.hEvent);
     322
     323                DWORD cbRead = 0;
     324                if (GetOverlappedResult(pThis->NamedPipe, &pThis->OverlappedRead, &cbRead, TRUE) == FALSE)
     325                {
     326                    uErr = GetLastError();
     327                    rc = RTErrConvertFromWin32(uErr);
     328                    Log(("drvNamedPipePoll: Read completed with %d (%Rrc)\n", uErr, rc));
     329
     330                    if (RT_FAILURE(rc))
     331                    {
     332                        /** @todo WriteFile(pipe) has been observed to return  ERROR_NO_DATA
     333                         *        (VERR_NO_DATA) instead of ERROR_BROKEN_PIPE, when the pipe is
     334                         *        disconnected. */
     335                        if (    rc == VERR_EOF
     336                            ||  rc == VERR_BROKEN_PIPE)
     337                        {
     338                            FlushFileBuffers(pThis->NamedPipe);
     339                            DisconnectNamedPipe(pThis->NamedPipe);
     340                            if (!pThis->fIsServer)
     341                            {
     342                                CloseHandle(pThis->NamedPipe);
     343                                pThis->NamedPipe = INVALID_HANDLE_VALUE;
     344                            }
     345                            /* pretend success */
     346                            rc = VINF_SUCCESS;
     347                        }
     348                        cbRead = 0;
     349                    }
     350                }
     351
     352                LogFlowFunc(("Read completed with cbRead=%u\n", cbRead));
     353                pThis->fReadPending = false;
     354                pThis->cbReadBufUsed = cbRead;
     355                if (pThis->cbReadBufUsed && (fEvts & RTPOLL_EVT_READ))
     356                {
     357                    *pfEvts |= RTPOLL_EVT_READ;
     358                    break;
     359                }
     360            }
     361        }
     362    }
     363#else
     364    if (pThis->hSock != NIL_RTSOCKET)
     365    {
     366        if (!pThis->fSockInPollSet)
     367        {
     368            rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hSock,
     369                                    fEvts, DRVNAMEDPIPE_POLLSET_ID_SOCKET);
     370            if (RT_SUCCESS(rc))
     371                pThis->fSockInPollSet = true;
     372        }
     373        else
     374        {
     375            /* Always include error event. */
     376            fEvts |= RTPOLL_EVT_ERROR;
     377            rc = RTPollSetEventsChange(pThis->hPollSet, DRVNAMEDPIPE_POLLSET_ID_SOCKET, fEvts);
     378            AssertRC(rc);
     379        }
     380    }
     381
     382    while (RT_SUCCESS(rc))
     383    {
     384        uint32_t fEvtsRecv = 0;
     385        uint32_t idHnd = 0;
     386
     387        rc = RTPoll(pThis->hPollSet, cMillies, &fEvtsRecv, &idHnd);
     388        if (RT_SUCCESS(rc))
     389        {
     390            if (idHnd == DRVNAMEDPIPE_POLLSET_ID_WAKEUP)
     391            {
     392                /* We got woken up, drain the pipe and return. */
     393                uint8_t bReason;
     394                size_t cbRead = 0;
     395                rc = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead);
     396                AssertRC(rc);
     397
     398                if (bReason == DRVNAMEDPIPE_WAKEUP_REASON_EXTERNAL)
     399                    rc = VERR_INTERRUPTED;
     400                else if (bReason == DRVNAMEDPIPE_WAKEUP_REASON_NEW_CONNECTION)
     401                {
     402                    Assert(!pThis->fSockInPollSet);
     403                    rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hSock,
     404                                            fEvts, DRVNAMEDPIPE_POLLSET_ID_SOCKET);
     405                    if (RT_SUCCESS(rc))
     406                        pThis->fSockInPollSet = true;
     407                }
     408                else
     409                    AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason));
     410            }
     411            else
     412            {
     413                Assert(idHnd == DRVNAMEDPIPE_POLLSET_ID_SOCKET);
     414
     415                /* On error we close the socket here. */
     416                if (fEvtsRecv & RTPOLL_EVT_ERROR)
     417                {
     418                    rc = RTPollSetRemove(pThis->hPollSet, DRVNAMEDPIPE_POLLSET_ID_SOCKET);
     419                    AssertRC(rc);
     420
     421                    RTSocketClose(pThis->hSock);
     422                    pThis->hSock = NIL_RTSOCKET;
     423                    pThis->fSockInPollSet = false;
     424                    /* Continue with polling. */
     425                }
     426                else
     427                {
     428                    *pfEvts = fEvtsRecv;
     429                    break;
     430                }
     431            }
     432        }
     433    }
     434#endif
     435
     436    LogFlowFunc(("returns %Rrc\n", rc));
     437    return rc;
     438}
     439
     440
     441/** @interface_method_impl{PDMISTREAM,pfnPollInterrupt} */
     442static DECLCALLBACK(int) drvNamedPipePollInterrupt(PPDMISTREAM pInterface)
     443{
     444    PDRVNAMEDPIPE pThis = RT_FROM_MEMBER(pInterface, DRVNAMEDPIPE, IStream);
     445    return drvNamedPipePollerKick(pThis, DRVNAMEDPIPE_WAKEUP_REASON_EXTERNAL);
     446}
     447
     448
    100449/** @interface_method_impl{PDMISTREAM,pfnRead} */
    101450static DECLCALLBACK(int) drvNamedPipeRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
    102451{
    103452    int rc = VINF_SUCCESS;
    104     PDRVNAMEDPIPE pThis = PDMISTREAM_2_DRVNAMEDPIPE(pInterface);
     453    PDRVNAMEDPIPE pThis = RT_FROM_MEMBER(pInterface, DRVNAMEDPIPE, IStream);
    105454    LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation));
    106455
     
    109458    if (pThis->NamedPipe != INVALID_HANDLE_VALUE)
    110459    {
    111         DWORD cbReallyRead;
    112         pThis->OverlappedRead.Offset     = 0;
    113         pThis->OverlappedRead.OffsetHigh = 0;
    114         if (!ReadFile(pThis->NamedPipe, pvBuf, (DWORD)*pcbRead, &cbReallyRead, &pThis->OverlappedRead))
    115         {
    116             DWORD uError = GetLastError();
    117 
    118             if (uError == ERROR_IO_PENDING)
    119             {
    120                 uError = 0;
    121 
    122                 /* Wait for incoming bytes. */
    123                 if (GetOverlappedResult(pThis->NamedPipe, &pThis->OverlappedRead, &cbReallyRead, TRUE) == FALSE)
    124                     uError = GetLastError();
    125             }
    126 
    127             if (   uError == ERROR_PIPE_LISTENING
    128                 || uError == ERROR_PIPE_NOT_CONNECTED)
    129             {
    130                 /* No connection yet/anymore */
    131                 cbReallyRead = 0;
    132 
    133                 /* wait a bit or else we'll be called right back. */
    134                 RTThreadSleep(100);
    135             }
    136             else
    137             {
    138                 rc = RTErrConvertFromWin32(uError);
    139                 Log(("drvNamedPipeRead: ReadFile returned %d (%Rrc)\n", uError, rc));
    140             }
    141         }
    142 
    143         if (RT_FAILURE(rc))
    144         {
    145             Log(("drvNamedPipeRead: FileRead returned %Rrc fShutdown=%d\n", rc, pThis->fShutdown));
    146             if (    !pThis->fShutdown
    147                 &&  (   rc == VERR_EOF
    148                      || rc == VERR_BROKEN_PIPE
    149                     )
    150                )
    151             {
    152                 FlushFileBuffers(pThis->NamedPipe);
    153                 DisconnectNamedPipe(pThis->NamedPipe);
    154                 if (!pThis->fIsServer)
    155                 {
    156                     CloseHandle(pThis->NamedPipe);
    157                     pThis->NamedPipe = INVALID_HANDLE_VALUE;
    158                 }
    159                 /* pretend success */
     460        /* Check if there is something in the read buffer and return as much as we can. */
     461        if (pThis->cbReadBufUsed)
     462        {
     463            size_t cbRead = RT_MIN(*pcbRead, pThis->cbReadBufUsed);
     464
     465            memcpy(pvBuf, &pThis->abBufRead[0], cbRead);
     466            if (cbRead < pThis->cbReadBufUsed)
     467                memmove(&pThis->abBufRead[0], &pThis->abBufRead[cbRead], pThis->cbReadBufUsed - cbRead);
     468            pThis->cbReadBufUsed -= cbRead;
     469            *pcbRead = cbRead;
     470        }
     471        else
     472            *pcbRead = 0;
     473    }
     474#else /* !RT_OS_WINDOWS */
     475    if (pThis->hSock != NIL_RTSOCKET)
     476    {
     477        size_t cbRead;
     478        size_t cbBuf = *pcbRead;
     479        rc = RTSocketReadNB(pThis->hSock, pvBuf, cbBuf, &cbRead);
     480        if (RT_SUCCESS(rc))
     481        {
     482            if (!cbRead && rc != VINF_TRY_AGAIN)
     483            {
     484                rc = RTPollSetRemove(pThis->hPollSet, DRVNAMEDPIPE_POLLSET_ID_SOCKET);
     485                AssertRC(rc);
     486
     487                RTSocketClose(pThis->hSock);
     488                pThis->hSock = NIL_RTSOCKET;
     489                pThis->fSockInPollSet = false;
    160490                rc = VINF_SUCCESS;
    161491            }
    162             cbReallyRead = 0;
    163         }
    164         *pcbRead = (size_t)cbReallyRead;
    165     }
    166 #else /* !RT_OS_WINDOWS */
    167     if (pThis->LocalSocket != -1)
    168     {
    169         ssize_t cbReallyRead;
    170         cbReallyRead = recv(pThis->LocalSocket, pvBuf, *pcbRead, 0);
    171         if (cbReallyRead == 0)
    172         {
    173             int tmp = pThis->LocalSocket;
    174             pThis->LocalSocket = -1;
    175             close(tmp);
    176         }
    177         else if (cbReallyRead == -1)
    178         {
    179             cbReallyRead = 0;
    180             rc = RTErrConvertFromErrno(errno);
    181         }
    182         *pcbRead = cbReallyRead;
     492            *pcbRead = cbRead;
     493        }
    183494    }
    184495#endif /* !RT_OS_WINDOWS */
     
    198509{
    199510    int rc = VINF_SUCCESS;
    200     PDRVNAMEDPIPE pThis = PDMISTREAM_2_DRVNAMEDPIPE(pInterface);
     511    PDRVNAMEDPIPE pThis = RT_FROM_MEMBER(pInterface, DRVNAMEDPIPE, IStream);
    201512    LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation));
    202513
     
    205516    if (pThis->NamedPipe != INVALID_HANDLE_VALUE)
    206517    {
    207         DWORD cbWritten = (DWORD)*pcbWrite;
    208         pThis->OverlappedWrite.Offset     = 0;
    209         pThis->OverlappedWrite.OffsetHigh = 0;
    210         if (!WriteFile(pThis->NamedPipe, pvBuf, cbWritten, NULL, &pThis->OverlappedWrite))
    211         {
    212             DWORD uError = GetLastError();
    213 
    214             if (   uError == ERROR_PIPE_LISTENING
    215                 || uError == ERROR_PIPE_NOT_CONNECTED)
    216             {
    217                 /* No connection yet/anymore; just discard the write (pretending everything was written). */;
    218             }
    219             else if (uError != ERROR_IO_PENDING)
    220             {
    221                 rc = RTErrConvertFromWin32(uError);
    222                 Log(("drvNamedPipeWrite: WriteFile returned %d (%Rrc)\n", uError, rc));
    223                 cbWritten = 0;
    224             }
    225             else
    226             {
    227                 /* Wait for the write to complete. */
    228                 if (GetOverlappedResult(pThis->NamedPipe, &pThis->OverlappedWrite, &cbWritten, TRUE /*bWait*/) == FALSE)
    229                     rc = RTErrConvertFromWin32(uError = GetLastError());
    230             }
    231         }
    232 
    233         if (RT_FAILURE(rc))
    234         {
    235             /** @todo WriteFile(pipe) has been observed to return  ERROR_NO_DATA
    236              *        (VERR_NO_DATA) instead of ERROR_BROKEN_PIPE, when the pipe is
    237              *        disconnected. */
    238             if (    rc == VERR_EOF
    239                 ||  rc == VERR_BROKEN_PIPE)
    240             {
    241                 FlushFileBuffers(pThis->NamedPipe);
    242                 DisconnectNamedPipe(pThis->NamedPipe);
    243                 if (!pThis->fIsServer)
    244                 {
    245                     CloseHandle(pThis->NamedPipe);
    246                     pThis->NamedPipe = INVALID_HANDLE_VALUE;
    247                 }
    248                 /* pretend success */
    249                 rc = VINF_SUCCESS;
    250             }
    251             cbWritten = 0;
    252         }
    253         *pcbWrite = cbWritten;
     518        /* Accept the data in case the write buffer is empty. */
     519        if (!pThis->cbWriteBufUsed)
     520        {
     521            size_t cbWrite = RT_MIN(*pcbWrite, sizeof(pThis->cbWriteBufUsed));
     522
     523            memcpy(&pThis->abBufWrite[0], pvBuf, cbWrite);
     524            pThis->cbWriteBufUsed += cbWrite;
     525
     526            /* Initiate the write. */
     527            pThis->OverlappedWrite.Offset     = 0;
     528            pThis->OverlappedWrite.OffsetHigh = 0;
     529            if (!WriteFile(pThis->NamedPipe, pvBuf, (DWORD)cbWrite, NULL, &pThis->OverlappedWrite))
     530            {
     531                DWORD uError = GetLastError();
     532
     533                if (   uError == ERROR_PIPE_LISTENING
     534                    || uError == ERROR_PIPE_NOT_CONNECTED)
     535                {
     536                    /* No connection yet/anymore; just discard the write (pretending everything was written). */
     537                     pThis->cbWriteBufUsed = 0;
     538                    cbWrite = *pcbWrite;
     539                }
     540                else if (uError != ERROR_IO_PENDING) /* We wait for the write to complete in the poll callback. */
     541                {
     542                    rc = RTErrConvertFromWin32(uError);
     543                    Log(("drvNamedPipeWrite: WriteFile returned %d (%Rrc)\n", uError, rc));
     544                    cbWrite = 0;
     545                }
     546            }
     547
     548            if (RT_FAILURE(rc))
     549            {
     550                /** @todo WriteFile(pipe) has been observed to return  ERROR_NO_DATA
     551                 *        (VERR_NO_DATA) instead of ERROR_BROKEN_PIPE, when the pipe is
     552                 *        disconnected. */
     553                if (    rc == VERR_EOF
     554                    ||  rc == VERR_BROKEN_PIPE)
     555                {
     556                    FlushFileBuffers(pThis->NamedPipe);
     557                    DisconnectNamedPipe(pThis->NamedPipe);
     558                    if (!pThis->fIsServer)
     559                    {
     560                        CloseHandle(pThis->NamedPipe);
     561                        pThis->NamedPipe = INVALID_HANDLE_VALUE;
     562                    }
     563                    /* pretend success */
     564                    rc = VINF_SUCCESS;
     565                }
     566                cbWrite = 0;
     567            }
     568
     569            *pcbWrite = cbWrite;
     570        }
     571        else
     572            *pcbWrite = 0;
    254573    }
    255574#else /* !RT_OS_WINDOWS */
    256     if (pThis->LocalSocket != -1)
    257     {
    258         ssize_t cbWritten;
    259         cbWritten = send(pThis->LocalSocket, pvBuf, *pcbWrite, 0);
    260         if (cbWritten == 0)
    261         {
    262             int tmp = pThis->LocalSocket;
    263             pThis->LocalSocket = -1;
    264             close(tmp);
    265         }
    266         else if (cbWritten == -1)
    267         {
    268             cbWritten = 0;
    269             rc = RTErrConvertFromErrno(errno);
    270         }
    271         *pcbWrite = cbWritten;
    272     }
     575    if (pThis->hSock != NIL_RTSOCKET)
     576    {
     577        size_t cbBuf = *pcbWrite;
     578        rc = RTSocketWriteNB(pThis->hSock, pvBuf, cbBuf, pcbWrite);
     579    }
     580    else
     581        *pcbWrite = 0;
    273582#endif /* !RT_OS_WINDOWS */
    274583
     
    331640                if (GetOverlappedResult(pThis->NamedPipe, &overlapped, &dummy, TRUE) == FALSE)
    332641                    hrc = GetLastError();
    333 
     642                else
     643                    drvNamedPipePollerKick(pThis, DRVNAMEDPIPE_WAKEUP_REASON_NEW_CONNECTION);
    334644            }
    335645
     
    362672            break;
    363673        }
    364         if (pThis->LocalSocket != -1)
     674        if (pThis->hSock != NIL_RTSOCKET)
    365675        {
    366676            LogRel(("NamedPipe%d: only single connection supported\n", pThis->pDrvIns->iInstance));
     
    368678        }
    369679        else
    370             pThis->LocalSocket = s;
    371 
     680        {
     681            RTSOCKET hSockNew = NIL_RTSOCKET;
     682            rc = RTSocketFromNative(&hSockNew, s);
     683            if (RT_SUCCESS(rc))
     684            {
     685                pThis->hSock = hSockNew;
     686                /* Inform the poller about the new socket. */
     687                drvNamedPipePollerKick(pThis, DRVNAMEDPIPE_WAKEUP_REASON_NEW_CONNECTION);
     688            }
     689            else
     690            {
     691                LogRel(("NamedPipe%d: Failed to wrap socket with %Rrc\n", pThis->pDrvIns->iInstance));
     692                close(s);
     693            }
     694        }
    372695#endif /* !RT_OS_WINDOWS */
    373696    }
     
    473796        pThis->OverlappedWrite.hEvent = NULL;
    474797    }
     798    if (pThis->hEvtWake != NULL)
     799    {
     800        CloseHandle(pThis->hEvtWake);
     801        pThis->hEvtWake = NULL;
     802    }
    475803#else /* !RT_OS_WINDOWS */
    476804    Assert(pThis->LocalSocketServer == -1);
    477     if (pThis->LocalSocket != -1)
    478     {
    479         int rc = shutdown(pThis->LocalSocket, SHUT_RDWR);
    480         AssertRC(rc == 0); NOREF(rc);
    481 
    482         rc = close(pThis->LocalSocket);
    483         Assert(rc == 0);
    484         pThis->LocalSocket = -1;
    485     }
     805
     806    if (pThis->hSock != NIL_RTSOCKET)
     807    {
     808        int rc = RTPollSetRemove(pThis->hPollSet, DRVNAMEDPIPE_POLLSET_ID_SOCKET);
     809        AssertRC(rc);
     810
     811        rc = RTSocketShutdown(pThis->hSock, true /* fRead */, true /* fWrite */);
     812        AssertRC(rc);
     813
     814        rc = RTSocketClose(pThis->hSock);
     815        AssertRC(rc); RT_NOREF(rc);
     816
     817        pThis->hSock = NIL_RTSOCKET;
     818    }
     819
     820    if (pThis->hPipeWakeR != NIL_RTPIPE)
     821    {
     822        int rc = RTPipeClose(pThis->hPipeWakeR);
     823        AssertRC(rc);
     824
     825        pThis->hPipeWakeR = NIL_RTPIPE;
     826    }
     827
     828    if (pThis->hPipeWakeW != NIL_RTPIPE)
     829    {
     830        int rc = RTPipeClose(pThis->hPipeWakeW);
     831        AssertRC(rc);
     832
     833        pThis->hPipeWakeW = NIL_RTPIPE;
     834    }
     835
     836    if (pThis->hPollSet != NIL_RTPOLLSET)
     837    {
     838        int rc = RTPollSetDestroy(pThis->hPollSet);
     839        AssertRC(rc);
     840
     841        pThis->hPollSet = NIL_RTPOLLSET;
     842    }
     843
    486844    if (   pThis->fIsServer
    487845        && pThis->pszLocation)
     
    539897    pThis->OverlappedWrite.hEvent       = NULL;
    540898    pThis->OverlappedRead.hEvent        = NULL;
     899    pThis->hEvtWake                     = NULL;
    541900#else /* !RT_OS_WINDOWS */
    542901    pThis->LocalSocketServer            = -1;
    543     pThis->LocalSocket                  = -1;
     902    pThis->hSock                        = NIL_RTSOCKET;
     903
     904    pThis->hPollSet                     = NIL_RTPOLLSET;
     905    pThis->hPipeWakeR                   = NIL_RTPIPE;
     906    pThis->hPipeWakeW                   = NIL_RTPIPE;
     907    pThis->fSockInPollSet               = false;
    544908#endif /* !RT_OS_WINDOWS */
    545909    pThis->ListenThread                 = NIL_RTTHREAD;
     
    548912    pDrvIns->IBase.pfnQueryInterface    = drvNamedPipeQueryInterface;
    549913    /* IStream */
     914    pThis->IStream.pfnPoll              = drvNamedPipePoll;
     915    pThis->IStream.pfnPollInterrupt     = drvNamedPipePollInterrupt;
    550916    pThis->IStream.pfnRead              = drvNamedPipeRead;
    551917    pThis->IStream.pfnWrite             = drvNamedPipeWrite;
     
    619985    AssertReturn(pThis->OverlappedRead.hEvent != NULL, VERR_OUT_OF_RESOURCES);
    620986
     987    pThis->hEvtWake = CreateEvent(NULL, FALSE, FALSE, NULL);
     988    AssertReturn(pThis->hEvtWake != NULL, VERR_OUT_OF_RESOURCES);
     989
    621990#else /* !RT_OS_WINDOWS */
     991    rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */);
     992    if (RT_FAILURE(rc))
     993        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     994                                   N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance);
     995
     996    rc = RTPollSetCreate(&pThis->hPollSet);
     997    if (RT_FAILURE(rc))
     998        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     999                                   N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance);
     1000
     1001    rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR,
     1002                            RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
     1003                            DRVNAMEDPIPE_POLLSET_ID_WAKEUP);
     1004    if (RT_FAILURE(rc))
     1005        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     1006                                   N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"),
     1007                                   pDrvIns->iInstance, pThis->pszLocation);
     1008
    6221009    int s = socket(PF_UNIX, SOCK_STREAM, 0);
    6231010    if (s == -1)
     
    6481035    {
    6491036        /* Connect to the local socket. */
    650         pThis->LocalSocket = s;
    6511037        if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    6521038            return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
    6531039                                       N_("NamedPipe#%d failed to connect to local socket %s"),
    6541040                                       pDrvIns->iInstance, pThis->pszLocation);
     1041
     1042        rc = RTSocketFromNative(&pThis->hSock, s);
     1043        if (RT_FAILURE(rc))
     1044        {
     1045            close(s);
     1046            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     1047                                       N_("NamedPipe#%d failed to wrap socket %Rrc"),
     1048                                       pDrvIns->iInstance, pThis->pszLocation);
     1049        }
    6551050    }
    6561051#endif /* !RT_OS_WINDOWS */
  • trunk/src/VBox/Devices/Serial/DrvRawFile.cpp

    r62956 r68699  
    2525#include <iprt/file.h>
    2626#include <iprt/mem.h>
     27#include <iprt/poll.h>
    2728#include <iprt/semaphore.h>
    2829#include <iprt/stream.h>
     
    3637*   Defined Constants And Macros                                                                                                 *
    3738*********************************************************************************************************************************/
    38 /** Converts a pointer to DRVRAWFILE::IMedia to a PDRVRAWFILE. */
    39 #define PDMISTREAM_2_DRVRAWFILE(pInterface) ( (PDRVRAWFILE)((uintptr_t)pInterface - RT_OFFSETOF(DRVRAWFILE, IStream)) )
    4039
    4140
     
    5655    /** Pointer to the file name. (Freed by MM) */
    5756    char               *pszLocation;
    58     /** Flag whether VirtualBox represents the server or client side. */
     57    /** File handle to write the data to. */
    5958    RTFILE              hOutputFile;
     59    /** Event semaphore for the poll interface. */
     60    RTSEMEVENT          hSemEvtPoll;
    6061} DRVRAWFILE, *PDRVRAWFILE;
    6162
     
    6364
    6465/* -=-=-=-=- PDMISTREAM -=-=-=-=- */
     66
     67/** @interface_method_impl{PDMISTREAM,pfnPoll} */
     68static DECLCALLBACK(int) drvRawFilePoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies)
     69{
     70    PDRVRAWFILE pThis = RT_FROM_MEMBER(pInterface, DRVRAWFILE, IStream);
     71
     72    Assert(!(fEvts & RTPOLL_EVT_READ)); /* Reading is not supported here. */
     73
     74    /* Writing is always possible. */
     75    if (fEvts & RTPOLL_EVT_WRITE)
     76    {
     77        *pfEvts = RTPOLL_EVT_WRITE;
     78        return VINF_SUCCESS;
     79    }
     80
     81    return RTSemEventWait(pThis->hSemEvtPoll, cMillies);
     82}
     83
     84
     85/** @interface_method_impl{PDMISTREAM,pfnPollInterrupt} */
     86static DECLCALLBACK(int) drvRawFilePollInterrupt(PPDMISTREAM pInterface)
     87{
     88    PDRVRAWFILE pThis = RT_FROM_MEMBER(pInterface, DRVRAWFILE, IStream);
     89    return RTSemEventSignal(pThis->hSemEvtPoll);
     90}
     91
    6592
    6693/** @interface_method_impl{PDMISTREAM,pfnWrite} */
     
    6895{
    6996    int rc = VINF_SUCCESS;
    70     PDRVRAWFILE pThis = PDMISTREAM_2_DRVRAWFILE(pInterface);
     97    PDRVRAWFILE pThis = RT_FROM_MEMBER(pInterface, DRVRAWFILE, IStream);
    7198    LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation));
    7299
     
    144171        RTFileClose(pThis->hOutputFile);
    145172        pThis->hOutputFile = NIL_RTFILE;
     173    }
     174
     175    if (pThis->hSemEvtPoll != NIL_RTSEMEVENT)
     176    {
     177        RTSemEventDestroy(pThis->hSemEvtPoll);
     178        pThis->hSemEvtPoll = NIL_RTSEMEVENT;
    146179    }
    147180}
     
    168201    pDrvIns->IBase.pfnQueryInterface    = drvRawFileQueryInterface;
    169202    /* IStream */
     203    pThis->IStream.pfnPoll              = drvRawFilePoll;
     204    pThis->IStream.pfnPollInterrupt     = drvRawFilePollInterrupt;
     205    pThis->IStream.pfnRead              = NULL;
    170206    pThis->IStream.pfnWrite             = drvRawFileWrite;
    171207
     
    179215    if (RT_FAILURE(rc))
    180216        AssertMsgFailedReturn(("Configuration error: query \"Location\" resulted in %Rrc.\n", rc), rc);
     217
     218     rc = RTSemEventCreate(&pThis->hSemEvtPoll);
     219     AssertRCReturn(rc, rc);
    181220
    182221    /*
  • trunk/src/VBox/Devices/Serial/DrvTCP.cpp

    r62956 r68699  
    55
    66/*
    7  * Copyright (C) 2006-2016 Oracle Corporation.
     7 * Copyright (C) 2006-2017 Oracle Corporation.
    88 *
    99 * This file was contributed by Alexey Eromenko (derived from DrvNamedPipe)
     
    2828#include <iprt/stream.h>
    2929#include <iprt/alloc.h>
     30#include <iprt/pipe.h>
     31#include <iprt/poll.h>
    3032#include <iprt/string.h>
    3133#include <iprt/semaphore.h>
     34#include <iprt/socket.h>
     35#include <iprt/tcp.h>
    3236#include <iprt/uuid.h>
    3337#include <stdlib.h>
    3438
    3539#include "VBoxDD.h"
    36 
    37 #ifdef RT_OS_WINDOWS
    38 # include <iprt/win/ws2tcpip.h>
    39 #else /* !RT_OS_WINDOWS */
    40 # include <errno.h>
    41 # include <unistd.h>
    42 # include <sys/types.h>
    43 # include <sys/socket.h>
    44 # include <netinet/in.h>
    45 # include <netdb.h>
    46 # ifndef SHUT_RDWR /* OS/2 */
    47 #  define SHUT_RDWR 3
    48 # endif
    49 #endif /* !RT_OS_WINDOWS */
    50 
    51 #ifndef SHUT_RDWR
    52 # ifdef SD_BOTH
    53 #  define SHUT_RDWR SD_BOTH
    54 # else
    55 #  define SHUT_RDWR 2
    56 # endif
    57 #endif
    58 
    5940
    6041/*********************************************************************************************************************************
    6142*   Defined Constants And Macros                                                                                                 *
    6243*********************************************************************************************************************************/
    63 /** Converts a pointer to DRVTCP::IMedia to a PDRVTCP. */
    64 #define PDMISTREAM_2_DRVTCP(pInterface) ( (PDRVTCP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTCP, IStream)) )
    65 
     44
     45#define DRVTCP_POLLSET_ID_SOCKET 0
     46#define DRVTCP_POLLSET_ID_WAKEUP 1
     47
     48#define DRVTCP_WAKEUP_REASON_EXTERNAL       0
     49#define DRVTCP_WAKEUP_REASON_NEW_CONNECTION 1
    6650
    6751/*********************************************************************************************************************************
     
    8468    bool                fIsServer;
    8569
    86     /** Socket handle of the TCP socket for server. */
    87     int                 TCPServer;
     70    /** Handle of the TCP server for incoming connections. */
     71    PRTTCPSERVER        hTcpServ;
    8872    /** Socket handle of the TCP socket connection. */
    89     int                 TCPConnection;
     73    RTSOCKET            hTcpSock;
     74
     75    /** Poll set used to wait for I/O events. */
     76    RTPOLLSET           hPollSet;
     77    /** Reading end of the wakeup pipe. */
     78    RTPIPE              hPipeWakeR;
     79    /** Writing end of the wakeup pipe. */
     80    RTPIPE              hPipeWakeW;
     81    /** Flag whether the socket is in the pollset. */
     82    bool                fTcpSockInPollSet;
    9083
    9184    /** Thread for listening for new connections. */
     
    10194
    10295
     96/**
     97 * Kicks any possibly polling thread to get informed about changes.
     98 *
     99 * @returns VBOx status code.
     100 * @param   pThis                  The TCP driver instance.
     101 * @param   bReason                The reason code to handle.
     102 */
     103static int drvTcpPollerKick(PDRVTCP pThis, uint8_t bReason)
     104{
     105    size_t cbWritten = 0;
     106    return RTPipeWrite(pThis->hPipeWakeW, &bReason, 1, &cbWritten);
     107}
     108
     109
     110/** @interface_method_impl{PDMISTREAM,pfnPoll} */
     111static DECLCALLBACK(int) drvTcpPoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies)
     112{
     113    int rc = VINF_SUCCESS;
     114    PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
     115
     116    if (pThis->hTcpSock != NIL_RTSOCKET)
     117    {
     118        if (!pThis->fTcpSockInPollSet)
     119        {
     120            rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
     121                                    fEvts, DRVTCP_POLLSET_ID_SOCKET);
     122            if (RT_SUCCESS(rc))
     123                pThis->fTcpSockInPollSet = true;
     124        }
     125        else
     126        {
     127            /* Always include error event. */
     128            fEvts |= RTPOLL_EVT_ERROR;
     129            rc = RTPollSetEventsChange(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET, fEvts);
     130            AssertRC(rc);
     131        }
     132    }
     133
     134    if (RT_SUCCESS(rc))
     135    {
     136        while (RT_SUCCESS(rc))
     137        {
     138            uint32_t fEvtsRecv = 0;
     139            uint32_t idHnd = 0;
     140
     141            rc = RTPoll(pThis->hPollSet, cMillies, &fEvtsRecv, &idHnd);
     142            if (RT_SUCCESS(rc))
     143            {
     144                if (idHnd == DRVTCP_POLLSET_ID_WAKEUP)
     145                {
     146                    /* We got woken up, drain the pipe and return. */
     147                    uint8_t bReason;
     148                    size_t cbRead = 0;
     149                    rc = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead);
     150                    AssertRC(rc);
     151
     152                    if (bReason == DRVTCP_WAKEUP_REASON_EXTERNAL)
     153                        rc = VERR_INTERRUPTED;
     154                    else if (bReason == DRVTCP_WAKEUP_REASON_NEW_CONNECTION)
     155                    {
     156                        Assert(!pThis->fTcpSockInPollSet);
     157                        rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
     158                                                fEvts, DRVTCP_POLLSET_ID_SOCKET);
     159                        if (RT_SUCCESS(rc))
     160                            pThis->fTcpSockInPollSet = true;
     161                    }
     162                    else
     163                        AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason));
     164                }
     165                else
     166                {
     167                    Assert(idHnd == DRVTCP_POLLSET_ID_SOCKET);
     168
     169                    /* On error we close the socket here. */
     170                    if (fEvtsRecv & RTPOLL_EVT_ERROR)
     171                    {
     172                        rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
     173                        AssertRC(rc);
     174
     175                        if (pThis->fIsServer)
     176                            RTTcpServerDisconnectClient2(pThis->hTcpSock);
     177                        else
     178                            RTSocketClose(pThis->hTcpSock);
     179                        pThis->hTcpSock = NIL_RTSOCKET;
     180                        pThis->fTcpSockInPollSet = false;
     181                        /* Continue with polling. */
     182                    }
     183                    else
     184                    {
     185                        *pfEvts = fEvtsRecv;
     186                        break;
     187                    }
     188                }
     189            }
     190        }
     191    }
     192
     193    return rc;
     194}
     195
     196
     197/** @interface_method_impl{PDMISTREAM,pfnPollInterrupt} */
     198static DECLCALLBACK(int) drvTcpPollInterrupt(PPDMISTREAM pInterface)
     199{
     200    PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
     201    return drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_EXTERNAL);
     202}
     203
     204
    103205/** @interface_method_impl{PDMISTREAM,pfnRead} */
    104 static DECLCALLBACK(int) drvTCPRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
     206static DECLCALLBACK(int) drvTcpRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
    105207{
    106208    int rc = VINF_SUCCESS;
    107     PDRVTCP pThis = PDMISTREAM_2_DRVTCP(pInterface);
     209    PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
    108210    LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation));
    109211
    110212    Assert(pvBuf);
    111213
    112     if (pThis->TCPConnection != -1)
    113     {
    114         ssize_t cbReallyRead;
    115         unsigned cbBuf = (unsigned)*pcbRead;
    116         cbReallyRead = recv(pThis->TCPConnection, (char *)pvBuf, cbBuf, 0);
    117         if (cbReallyRead == 0)
     214    if (pThis->hTcpSock != NIL_RTSOCKET)
     215    {
     216        size_t cbRead;
     217        size_t cbBuf = *pcbRead;
     218        rc = RTSocketReadNB(pThis->hTcpSock, pvBuf, cbBuf, &cbRead);
     219        if (RT_SUCCESS(rc))
    118220        {
    119             int tmp = pThis->TCPConnection;
    120             pThis->TCPConnection = -1;
    121 #ifdef RT_OS_WINDOWS
    122             closesocket(tmp);
    123 #else
    124             close(tmp);
    125 #endif
     221            if (!cbRead && rc != VINF_TRY_AGAIN)
     222            {
     223                rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
     224                AssertRC(rc);
     225
     226                if (pThis->fIsServer)
     227                    RTTcpServerDisconnectClient2(pThis->hTcpSock);
     228                else
     229                    RTSocketClose(pThis->hTcpSock);
     230                pThis->hTcpSock = NIL_RTSOCKET;
     231                pThis->fTcpSockInPollSet = false;
     232                rc = VINF_SUCCESS;
     233            }
     234            *pcbRead = cbRead;
    126235        }
    127         else if (cbReallyRead == -1)
    128         {
    129             cbReallyRead = 0;
    130             rc = RTErrConvertFromErrno(errno);
    131         }
    132         *pcbRead = cbReallyRead;
    133236    }
    134237    else
     
    144247
    145248/** @interface_method_impl{PDMISTREAM,pfnWrite} */
    146 static DECLCALLBACK(int) drvTCPWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)
     249static DECLCALLBACK(int) drvTcpWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)
    147250{
    148251    int rc = VINF_SUCCESS;
    149     PDRVTCP pThis = PDMISTREAM_2_DRVTCP(pInterface);
     252    PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
    150253    LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation));
    151254
    152255    Assert(pvBuf);
    153     if (pThis->TCPConnection != -1)
    154     {
    155         ssize_t cbWritten;
    156         unsigned cbBuf = (unsigned)*pcbWrite;
    157         cbWritten = send(pThis->TCPConnection, (const char *)pvBuf, cbBuf, 0);
    158         if (cbWritten == 0)
    159         {
    160             int tmp = pThis->TCPConnection;
    161             pThis->TCPConnection = -1;
    162 #ifdef RT_OS_WINDOWS
    163             closesocket(tmp);
    164 #else
    165             close(tmp);
    166 #endif
    167         }
    168         else if (cbWritten == -1)
    169         {
    170             cbWritten = 0;
    171             rc = RTErrConvertFromErrno(errno);
    172         }
    173         *pcbWrite = cbWritten;
    174     }
     256    if (pThis->hTcpSock != NIL_RTSOCKET)
     257    {
     258        size_t cbBuf = *pcbWrite;
     259        rc = RTSocketWriteNB(pThis->hTcpSock, pvBuf, cbBuf, pcbWrite);
     260    }
     261    else
     262        *pcbWrite = 0;
    175263
    176264    LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
     
    205293    RT_NOREF(hThreadSelf);
    206294    PDRVTCP pThis = (PDRVTCP)pvUser;
    207     int     rc;
    208295
    209296    while (RT_LIKELY(!pThis->fShutdown))
    210297    {
    211         if (listen(pThis->TCPServer, 0) == -1)
     298        RTSOCKET hTcpSockNew = NIL_RTSOCKET;
     299        int rc = RTTcpServerListen2(pThis->hTcpServ, &hTcpSockNew);
     300        if (RT_SUCCESS(rc))
    212301        {
    213             rc = RTErrConvertFromErrno(errno);
    214             LogRel(("DrvTCP%d: listen failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc));
    215             break;
     302            if (pThis->hTcpSock != NIL_RTSOCKET)
     303            {
     304                LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance));
     305                RTTcpServerDisconnectClient2(hTcpSockNew);
     306            }
     307            else
     308            {
     309                pThis->hTcpSock = hTcpSockNew;
     310                /* Inform the poller about the new socket. */
     311                drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_NEW_CONNECTION);
     312            }
    216313        }
    217         int s = accept(pThis->TCPServer, NULL, NULL);
    218         if (s == -1)
    219         {
    220             rc = RTErrConvertFromErrno(errno);
    221             LogRel(("DrvTCP%d: accept failed, rc=%Rrc\n", pThis->pDrvIns->iInstance, rc));
    222             break;
    223         }
    224         if (pThis->TCPConnection != -1)
    225         {
    226             LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance));
    227 #ifdef RT_OS_WINDOWS
    228             closesocket(s);
    229 #else
    230             close(s);
    231 #endif
    232         }
    233         else
    234             pThis->TCPConnection = s;
    235314    }
    236315
     
    252331    pThis->fShutdown = true;
    253332    if (    pThis->fIsServer
    254         &&  pThis->TCPServer != -1)
    255     {
    256         int rc = shutdown(pThis->TCPServer, SHUT_RDWR);
    257         AssertRC(rc == 0); NOREF(rc);
    258 
    259 #ifdef RT_OS_WINDOWS
    260         rc = closesocket(pThis->TCPServer);
    261 #else
    262         rc = close(pThis->TCPServer);
    263 #endif
    264         AssertRC(rc == 0);
    265         pThis->TCPServer = -1;
     333        &&  pThis->hTcpServ != NULL)
     334    {
     335        int rc = RTTcpServerShutdown(pThis->hTcpServ);
     336        AssertRC(rc);
     337        pThis->hTcpServ = NULL;
    266338    }
    267339}
     
    303375     * While the thread exits, clean up as much as we can.
    304376     */
    305 
    306     Assert(pThis->TCPServer == -1);
    307     if (pThis->TCPConnection != -1)
    308     {
    309         int rc = shutdown(pThis->TCPConnection, SHUT_RDWR);
    310         AssertRC(rc == 0); NOREF(rc);
    311 
    312 #ifdef RT_OS_WINDOWS
    313         rc = closesocket(pThis->TCPConnection);
    314 #else
    315         rc = close(pThis->TCPConnection);
    316 #endif
    317         Assert(rc == 0);
    318         pThis->TCPConnection = -1;
    319     }
    320     if (   pThis->fIsServer
    321         && pThis->pszLocation)
    322         RTFileDelete(pThis->pszLocation);
    323 
     377    if (pThis->hTcpSock != NIL_RTSOCKET)
     378    {
     379        int rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
     380        AssertRC(rc);
     381
     382        rc = RTSocketShutdown(pThis->hTcpSock, true /* fRead */, true /* fWrite */);
     383        AssertRC(rc);
     384
     385        rc = RTSocketClose(pThis->hTcpSock);
     386        AssertRC(rc); RT_NOREF(rc);
     387
     388        pThis->hTcpSock = NIL_RTSOCKET;
     389    }
     390
     391    if (pThis->hPipeWakeR != NIL_RTPIPE)
     392    {
     393        int rc = RTPipeClose(pThis->hPipeWakeR);
     394        AssertRC(rc);
     395
     396        pThis->hPipeWakeR = NIL_RTPIPE;
     397    }
     398
     399    if (pThis->hPipeWakeW != NIL_RTPIPE)
     400    {
     401        int rc = RTPipeClose(pThis->hPipeWakeW);
     402        AssertRC(rc);
     403
     404        pThis->hPipeWakeW = NIL_RTPIPE;
     405    }
     406
     407    if (pThis->hPollSet != NIL_RTPOLLSET)
     408    {
     409        int rc = RTPollSetDestroy(pThis->hPollSet);
     410        AssertRC(rc);
     411
     412        pThis->hPollSet = NIL_RTPOLLSET;
     413    }
    324414
    325415    MMR3HeapFree(pThis->pszLocation);
     
    337427            LogRel(("DrvTCP%d: listen thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
    338428    }
    339 
    340429}
    341430
     
    351440    PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
    352441    PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
    353 
    354 #ifdef RT_OS_WINDOWS
    355     {
    356         WSADATA wsaData;
    357         int err;
    358 
    359         err = WSAStartup(MAKEWORD(2,2), &wsaData);
    360         if (err != 0)
    361         {
    362             LogRel(("DrvTCP: Failed to initialize Winsock, error %d\n", err));
    363             /* XXX: let socket creation fail below */
    364         }
    365     }
    366 #endif
    367442
    368443    /*
     
    373448    pThis->fIsServer                    = false;
    374449
    375     pThis->TCPServer                    = -1;
    376     pThis->TCPConnection                = -1;
     450    pThis->hTcpServ                     = NULL;
     451    pThis->hTcpSock                     = NIL_RTSOCKET;
     452
     453    pThis->hPollSet                     = NIL_RTPOLLSET;
     454    pThis->hPipeWakeR                   = NIL_RTPIPE;
     455    pThis->hPipeWakeW                   = NIL_RTPIPE;
     456    pThis->fTcpSockInPollSet            = false;
    377457
    378458    pThis->ListenThread                 = NIL_RTTHREAD;
     
    381461    pDrvIns->IBase.pfnQueryInterface    = drvTCPQueryInterface;
    382462    /* IStream */
    383     pThis->IStream.pfnRead              = drvTCPRead;
    384     pThis->IStream.pfnWrite             = drvTCPWrite;
     463    pThis->IStream.pfnPoll              = drvTcpPoll;
     464    pThis->IStream.pfnPollInterrupt     = drvTcpPollInterrupt;
     465    pThis->IStream.pfnRead              = drvTcpRead;
     466    pThis->IStream.pfnWrite             = drvTcpWrite;
    385467
    386468    /*
     
    398480                                   N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc);
    399481
     482    rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */);
     483    if (RT_FAILURE(rc))
     484        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     485                                   N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance);
     486
     487    rc = RTPollSetCreate(&pThis->hPollSet);
     488    if (RT_FAILURE(rc))
     489        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     490                                   N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance);
     491
     492    rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR,
     493                            RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
     494                            DRVTCP_POLLSET_ID_WAKEUP);
     495    if (RT_FAILURE(rc))
     496        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     497                                   N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"),
     498                                   pDrvIns->iInstance, pThis->pszLocation);
     499
    400500    /*
    401501     * Create/Open the socket.
    402502     */
    403     int s = socket(PF_INET, SOCK_STREAM, 0);
    404     if (s == -1)
    405         return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
    406                                    N_("DrvTCP#%d failed to create socket"), pDrvIns->iInstance);
    407 
    408     struct sockaddr_in addr;
    409     memset(&addr, 0, sizeof(addr));
    410     addr.sin_family = AF_INET;
    411 
    412503    if (pThis->fIsServer)
    413504    {
    414         addr.sin_addr.s_addr = INADDR_ANY;
    415         addr.sin_port = htons(/*PORT*/ atoi(pThis->pszLocation));
    416 
    417         /* Bind address to the telnet socket. */
    418         pThis->TCPServer = s;
    419         RTFileDelete(pThis->pszLocation);
    420         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    421             return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
    422                                        N_("DrvTCP#%d failed to bind to socket %s"),
    423                                        pDrvIns->iInstance, pThis->pszLocation);
     505        uint32_t uPort = 0;
     506        rc = RTStrToUInt32Ex(pThis->pszLocation, NULL, 10, &uPort);
     507        if (RT_FAILURE(rc))
     508            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     509                                       N_("DrvTCP#%d: The port part of the location is not a numerical value"),
     510                                       pDrvIns->iInstance);
     511
     512        /** @todo: Allow binding to distinct interfaces. */
     513        rc = RTTcpServerCreateEx(NULL, uPort, &pThis->hTcpServ);
     514        if (RT_FAILURE(rc))
     515            return PDMDrvHlpVMSetError(pDrvIns, rc,  RT_SRC_POS,
     516                                       N_("DrvTCP#%d failed to create server socket"), pDrvIns->iInstance);
     517
    424518        rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0,
    425519                            RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream");
     
    430524    else
    431525    {
    432         char *token;
    433         const char *delim = ":";
    434         struct hostent *server;
    435         token = strtok(pThis->pszLocation, delim);
    436         if(token) {
    437             server = gethostbyname(token);
    438             memmove((char *)&addr.sin_addr.s_addr,
    439                     (char *)server->h_addr,
    440                      server->h_length);
    441         }
    442         token = strtok(NULL, delim);
    443         if(token) {
    444             addr.sin_port = htons(/*PORT*/ atoi(token));
    445         }
    446 
    447         /* Connect to the telnet socket. */
    448         pThis->TCPConnection = s;
    449         if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    450             return PDMDrvHlpVMSetError(pDrvIns, RTErrConvertFromErrno(errno), RT_SRC_POS,
     526        char *pszPort = strchr(pThis->pszLocation, ':');
     527        if (!pszPort)
     528            return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS,
     529                                       N_("DrvTCP#%d: The location misses the port to connect to"),
     530                                       pDrvIns->iInstance);
     531
     532        *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
     533        uint32_t uPort = 0;
     534        rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort);
     535        if (RT_FAILURE(rc))
     536            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     537                                       N_("DrvTCP#%d: The port part of the location is not a numerical value"),
     538                                       pDrvIns->iInstance);
     539
     540        rc = RTTcpClientConnect(pThis->pszLocation, uPort, &pThis->hTcpSock);
     541        *pszPort = ':'; /* Restore delimiter before checking the status. */
     542        if (RT_FAILURE(rc))
     543            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
    451544                                       N_("DrvTCP#%d failed to connect to socket %s"),
    452545                                       pDrvIns->iInstance, pThis->pszLocation);
     546
     547        rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
     548                                RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR,
     549                                DRVTCP_POLLSET_ID_SOCKET);
     550        if (RT_FAILURE(rc))
     551            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     552                                       N_("DrvTCP#%d failed to add socket for %s to poll set"),
     553                                       pDrvIns->iInstance, pThis->pszLocation);
     554
     555        pThis->fTcpSockInPollSet = true;
    453556    }
    454557
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