VirtualBox

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


Ignore:
Timestamp:
Sep 24, 2007 1:57:39 PM (17 years ago)
Author:
vboxsync
Message:

serial: use PDMThreads for send/receive. Thread is already a problem when terminating on Windows.

File:
1 edited

Legend:

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

    r4993 r5004  
    4040
    4141#ifdef RT_OS_LINUX
     42# include <errno.h>
    4243# include <termios.h>
    4344# include <sys/types.h>
     
    7273    /** Our char interface. */
    7374    PDMICHAR                    IChar;
    74     /** Flag to notify the receive thread it should terminate. */
    75     volatile bool               fShutdown;
    76     /** Receive thread ID. */
    77     RTTHREAD                    ReceiveThread;
    78     /** Send thread ID. */
    79     RTTHREAD                    SendThread;
     75    /** Receive thread. */
     76    PPDMTHREAD                  pReceiveThread;
     77    /** Send thread. */
     78    PPDMTHREAD                  pSendThread;
    8079    /** Send event semephore */
    8180    RTSEMEVENT                  SendSem;
    8281
    83 #ifdef RT_OS_LINUX
    84     /** The receive thread wakeup pipe. */
    85     RTFILE                      WakeupPipeR;
    86     RTFILE                      WakeupPipeW;
    87 #endif
    88 
    8982    /** the device path */
    9083    char                        *pszDevicePath;
     84
     85#ifdef RT_OS_LINUX
    9186    /** the device handle */
    9287    RTFILE                      DeviceFile;
     88    /** The read end of the control pipe */
     89    RTFILE                      WakeupPipeR;
     90    /** The write end of the control pipe */
     91    RTFILE                      WakeupPipeW;
     92#elif RT_OS_WINDOWS
     93    /** the device handle */
     94    HANDLE                      hDeviceFile;
     95    /** The event semaphore for waking up the receive thread */
     96    HANDLE                      hHaltEventSem;
     97    /** The event semaphore for overlapped receiving */
     98    HANDLE                      hEventReceive;
     99    /** The event semaphore for overlapped sending */
     100    HANDLE                      hEventSend;
     101#endif
    93102
    94103    /** Internal send FIFO queue */
     
    106115#define PDMICHAR_2_DRVHOSTSERIAL(pInterface) ( (PDRVHOSTSERIAL)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTSERIAL, IChar)) )
    107116
    108 #ifdef RT_OS_LINUX
    109 /** String written to the wakeup pipe. */
    110 #define WAKE_UP_STRING      "WakeUp!"
    111 /** Length of the string written. */
    112 #define WAKE_UP_STRING_LEN  ( sizeof (WAKE_UP_STRING) - 1 )
    113 #endif
     117
    114118/* -=-=-=-=- IBase -=-=-=-=- */
    115119
     
    375379    comSetup->EvtChar = 0;
    376380
    377     SetCommState((HANDLE)pData->DeviceFile, comSetup);
     381    SetCommState(pData->hDeviceFile, comSetup);
    378382    RTMemFree(comSetup);
    379383#endif /* RT_OS_WINDOWS */
     
    391395 * @param   pvUser      User argument.
    392396 */
    393 static DECLCALLBACK(int) drvHostSerialSendLoop(RTTHREAD ThreadSelf, void *pvUser)
    394 {
    395     PDRVHOSTSERIAL pData = (PDRVHOSTSERIAL)pvUser;
    396 
    397     while (!pData->fShutdown)
     397static DECLCALLBACK(int) drvHostSerialSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     398{
     399    PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
     400
     401    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
     402        return VINF_SUCCESS;
     403
     404#ifdef RT_OS_WINDOWS
     405    HANDLE haWait[2];
     406    haWait[0] = pData->hEventSend;
     407    haWait[1] = pData->hHaltEventSem;
     408#endif
     409
     410    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
    398411    {
    399412        int rc = RTSemEventWait(pData->SendSem, RT_INDEFINITE_WAIT);
     
    404417         * Write the character to the host device.
    405418         */
    406         while (     !pData->fShutdown
    407                &&   pData->iSendQueueTail != pData->iSendQueueHead)
     419        while (   pThread->enmState == PDMTHREADSTATE_RUNNING
     420               && pData->iSendQueueTail != pData->iSendQueueHead)
    408421        {
    409422            unsigned cbProcessed = 1;
    410423
     424#if defined(RT_OS_LINUX)
     425
    411426            rc = RTFileWrite(pData->DeviceFile, &pData->aSendQueue[pData->iSendQueueTail], cbProcessed, NULL);
     427
     428#elif defined(RT_OS_WINDOWS)
     429
     430            DWORD cbBytesWritten;
     431            OVERLAPPED overlapped;
     432            memset(&overlapped, 0, sizeof(overlapped));
     433            overlapped.hEvent = pData->hEventSend;
     434
     435            if (!WriteFile(pData->hDeviceFile, &pData->aSendQueue[pData->iSendQueueTail], cbProcessed, &cbBytesWritten, &overlapped))
     436            {
     437                DWORD dwRet = GetLastError();
     438                if (dwRet == ERROR_IO_PENDING)
     439                {
     440                    /*
     441                     * write blocked, wait ...
     442                     */
     443                    dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
     444                    if (dwRet != WAIT_OBJECT_0)
     445                        break;
     446                }
     447                else
     448                    rc = RTErrConvertFromWin32(dwRet);
     449            }
     450
     451#endif
     452
    412453            if (VBOX_SUCCESS(rc))
    413454            {
     
    418459            else if (VBOX_FAILURE(rc))
    419460            {
    420                 LogFlow(("Write failed with %Vrc; skipping\n", rc));
    421                 break;
     461                LogRel(("HostSerial#%d: Serial Write failed with %Vrc; terminating send thread\n", pDrvIns->iInstance, rc));
     462                return VINF_SUCCESS;
    422463            }
    423464        }
     
    427468}
    428469
     470/**
     471 * Unblock the send thread so it can respond to a state change.
     472 *
     473 * @returns a VBox status code.
     474 * @param     pDrvIns     The driver instance.
     475 * @param     pThread     The send thread.
     476 */
     477static DECLCALLBACK(int) drvHostSerialWakeupSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     478{
     479    PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
     480    int rc;
     481   
     482    rc = RTSemEventSignal(pData->SendSem);
     483    if (RT_FAILURE(rc))
     484        return rc;
     485
     486#ifdef RT_OS_WINDOWS
     487    if (!SetEvent(pData->hHaltEventSem))
     488        return RTErrConvertFromWin32(GetLastError());
     489#endif
     490
     491    return VINF_SUCCESS;
     492}
    429493
    430494/* -=-=-=-=- receive thread -=-=-=-=- */
     
    440504 * @param   pvUser      User argument.
    441505 */
    442 static DECLCALLBACK(int) drvHostSerialReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
    443 {
    444     PDRVHOSTSERIAL pData = (PDRVHOSTSERIAL)pvUser;
     506static DECLCALLBACK(int) drvHostSerialReceiveThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     507{
     508    PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
    445509    uint8_t abBuffer[256];
    446510    uint8_t *pbBuffer = NULL;
     
    448512    int rc = VINF_SUCCESS;
    449513
    450     while (!pData->fShutdown)
     514    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
     515        return VINF_SUCCESS;
     516
     517#ifdef RT_OS_WINDOWS
     518    HANDLE haWait[2];
     519    haWait[0] = pData->hEventReceive;
     520    haWait[1] = pData->hHaltEventSem;
     521#endif
     522
     523    while (pThread->enmState == PDMTHREADSTATE_RUNNING)
    451524    {
    452525        if (!cbRemaining)
    453526        {
    454527            /* Get a block of data from the host serial device. */
    455             size_t        cbRead;
    456 
    457 #ifdef RT_OS_LINUX
    458             struct pollfd pfd[2];
    459 
    460             pfd[0].fd = pData->DeviceFile;
    461             pfd[0].events = POLLIN;
    462             pfd[1].fd = pData->WakeupPipeR;
    463             pfd[1].events = POLLIN | POLLERR | POLLHUP;
    464 
    465             rc = poll(&pfd[0], 2, -1);
    466 
     528
     529#if defined(RT_OS_LINUX)
     530
     531            size_t cbRead;
     532            struct pollfd aFDs[2];
     533            aFDs[0].fd      = pData->DeviceFile;
     534            aFDs[0].events = POLLIN;
     535            aFDs[0].revents = 0;
     536            aFDs[1].fd      = pData->WakeupPipeR;
     537            aFDs[1].events  = POLLIN | POLLERR | POLLHUP;
     538            aFDs[1].revents = 0;
     539            rc = poll(aFDs, ELEMENTS(aFDs), -1);
    467540            if (rc < 0)
     541            {
     542                /* poll failed for whatever reason */
    468543                break;
    469 
    470             if (rc > 0)
    471             {
    472                 if (pfd[1].revents & POLLIN)
     544            }
     545            /* this might have changed in the meantime */
     546            if (pThread->enmState != PDMTHREADSTATE_RUNNING)
     547                break;
     548            if (rc > 0 && aFDs[1].revents)
     549            {
     550                if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
     551                    break;
     552                /* notification to terminate -- drain the pipe */
     553                char ch;
     554                size_t cbRead;
     555                RTFileRead(pData->WakeupPipeR, &ch, 1, &cbRead);
     556                continue;
     557            }
     558            rc = RTFileRead(pData->DeviceFile, abBuffer, sizeof(abBuffer), &cbRead);
     559            if (VBOX_FAILURE(rc))
     560            {
     561                LogRel(("HostSerial#%d: Read failed with %Vrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
     562                break;
     563            }
     564            cbRemaining = cbRead;
     565
     566#elif defined(RT_OS_WINDOWS)
     567
     568            DWORD dwEventMask = 0;
     569            DWORD dwNumberOfBytesTransferred;
     570            OVERLAPPED overlapped;
     571
     572            memset(&overlapped, 0, sizeof(overlapped));
     573            overlapped.hEvent = pData->hEventReceive;
     574
     575            if (!WaitCommEvent(pData->hDeviceFile, &dwEventMask, &overlapped))
     576            {
     577                DWORD dwRet = GetLastError();
     578                if (dwRet == ERROR_IO_PENDING)
    473579                {
    474                     /* Empty the pipe. */
    475                     char szBuf[WAKE_UP_STRING_LEN];
    476                     rc = RTFileRead (pData->WakeupPipeR, szBuf, sizeof (szBuf), NULL);
    477                     AssertRC (rc);
     580                    dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
     581                    if (dwRet != WAIT_OBJECT_0)
     582                    {
     583                        /* notification to terminate */
     584                        break;
     585                    }
     586                }
     587                else
     588                {
     589                    LogRel(("HostSerial#%d: Wait failed with error %Vrc; terminating the worker thread.\n", pDrvIns->iInstance, RTErrConvertFromWin32(dwRet)));
    478590                    break;
    479591                }
    480592            }
    481 #elif defined(RT_OS_WINDOWS)
    482             BOOL retval;
    483             DWORD dwEventMask = EV_RXCHAR;
    484 
    485             retval = WaitCommEvent((HANDLE)pData->DeviceFile, &dwEventMask, NULL);
    486 
    487             if (!retval)
    488             {
    489                 LogRel(("Host Serial Driver: WaitCommEvent failed, terminating the worker thread.\n"));
     593            /* this might have changed in the meantime */
     594            if (pThread->enmState != PDMTHREADSTATE_RUNNING)
    490595                break;
    491             }
    492 #endif
    493 
    494             rc = RTFileRead(pData->DeviceFile, abBuffer, sizeof(abBuffer), &cbRead);
    495             if (VBOX_FAILURE(rc))
    496             {
    497                 LogRel(("Host Serial Driver: Read failed with %Vrc, terminating the worker thread.\n", rc));
     596            if (!ReadFile(pData->hDeviceFile, abBuffer, sizeof(abBuffer), &dwNumberOfBytesTransferred, &overlapped))
     597            {
     598                LogRel(("HostSerial#%d: Read failed with error %Vrc; terminating the worker thread.\n", pDrvIns->iInstance, RTErrConvertFromWin32(GetLastError())));
    498599                break;
    499600            }
    500             Log(("Host Serial Driver: Read %d bytes.\n", cbRead));
    501             cbRemaining = cbRead;
     601            cbRemaining = dwNumberOfBytesTransferred;
     602
     603#endif
     604
     605            Log(("Read %d bytes.\n", cbRemaining));
    502606            pbBuffer = abBuffer;
    503607        }
     
    522626            else
    523627            {
    524                 LogRel(("Host Serial Driver: NotifyRead failed with %Vrc, terminating the worker thread.\n", rc));
     628                LogRel(("HostSerial#%d: NotifyRead failed with %Vrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
    525629                break;
    526630            }
     
    529633
    530634    return VINF_SUCCESS;
     635}
     636
     637/**
     638 * Unblock the send thread so it can respond to a state change.
     639 *
     640 * @returns a VBox status code.
     641 * @param     pDrvIns     The driver instance.
     642 * @param     pThread     The send thread.
     643 */
     644static DECLCALLBACK(int) drvHostSerialWakeupReceiveThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
     645{
     646    PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
     647#ifdef RT_OS_LINUX
     648    return RTFileWrite(pData->WakeupPipeW, "", 1, NULL);
     649#elif RT_OS_WINDOWS
     650    if (!SetEvent(pData->hHaltEventSem))
     651        return RTErrConvertFromWin32(GetLastError());
     652    return VINF_SUCCESS;
     653#else
     654# error adapt me!
     655#endif
    531656}
    532657
     
    549674{
    550675    PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
    551 #ifdef RT_OS_LINUX
    552     int filedes[2];
    553 #endif
    554 
    555676    LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
    556677
     
    558679     * Init basic data members and interfaces.
    559680     */
    560     pData->ReceiveThread                    = NIL_RTTHREAD;
    561     pData->SendThread                       = NIL_RTTHREAD;
    562     pData->fShutdown                        = false;
    563681    /* IBase. */
    564682    pDrvIns->IBase.pfnQueryInterface        = drvHostSerialQueryInterface;
     
    581699     * Open the device
    582700     */
     701#ifdef RT_OS_WINDOWS
     702
     703    pData->hHaltEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
     704    AssertReturn(pData->hHaltEventSem != NULL, VERR_NO_MEMORY);
     705
     706    pData->hEventReceive = CreateEvent(NULL, FALSE, FALSE, NULL);
     707    AssertReturn(pData->hEventReceive != NULL, VERR_NO_MEMORY);
     708
     709    pData->hEventSend = CreateEvent(NULL, FALSE, FALSE, NULL);
     710    AssertReturn(pData->hEventSend != NULL, VERR_NO_MEMORY);
     711
     712    HANDLE hFile = CreateFile(pData->pszDevicePath,
     713                              GENERIC_READ | GENERIC_WRITE,
     714                              0, // must be opened with exclusive access
     715                              NULL, // no SECURITY_ATTRIBUTES structure
     716                              OPEN_EXISTING, // must use OPEN_EXISTING
     717                              FILE_FLAG_OVERLAPPED, // overlapped I/O
     718                              NULL); // no template file
     719    if (hFile == INVALID_HANDLE_VALUE)
     720        rc = RTErrConvertFromWin32(GetLastError());
     721    else
     722    {
     723        pData->hDeviceFile = hFile;
     724        /* for overlapped read */
     725        if (!SetCommMask(hFile, EV_RXCHAR))
     726        {
     727            LogRel(("HostSerial#%d: SetCommMask failed with error %d.\n", pDrvIns->iInstance, GetLastError()));
     728            return VERR_FILE_IO_ERROR;
     729        }
     730        rc = VINF_SUCCESS;
     731    }
     732
     733#else
     734
     735    pData->DeviceFile = NIL_RTFILE;
    583736    rc = RTFileOpen(&pData->DeviceFile, pData->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READWRITE);
    584737
    585     if (VBOX_FAILURE(rc)) {
    586         pData->DeviceFile = NIL_RTFILE;
     738#endif
     739
     740    if (VBOX_FAILURE(rc))
     741    {
    587742        AssertMsgFailed(("Could not open host device %s, rc=%Vrc\n", pData->pszDevicePath, rc));
    588         switch (rc) {
     743        switch (rc)
     744        {
    589745            case VERR_ACCESS_DENIED:
    590746                return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
     
    608764    /* Set to non blocking I/O */
    609765#ifdef RT_OS_LINUX
     766
    610767    fcntl(pData->DeviceFile, F_SETFL, O_NONBLOCK);
    611     /* Create the wakeup pipe. */
    612     if (!pipe(filedes))
    613     {
    614         pData->WakeupPipeR = filedes[0];
    615         pData->WakeupPipeW = filedes[1];
    616     }
     768    int aFDs[2];
     769    if (pipe(aFDs) != 0)
     770    {
     771        int rc = RTErrConvertFromErrno(errno);
     772        AssertRC(rc);
     773        return rc;
     774    }
     775    pData->WakeupPipeR = aFDs[0];
     776    pData->WakeupPipeW = aFDs[1];
     777
    617778#elif defined(RT_OS_WINDOWS)
     779
    618780    /* Set the COMMTIMEOUTS to get non blocking I/O */
    619781    COMMTIMEOUTS comTimeout;
     
    625787    comTimeout.WriteTotalTimeoutConstant   = 0;
    626788
    627     SetCommTimeouts((HANDLE)pData->DeviceFile, &comTimeout);
     789    SetCommTimeouts(pData->hDeviceFile, &comTimeout);
     790
    628791#endif
    629792
     
    635798        return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no char port interface above"), pDrvIns->iInstance);
    636799
    637     rc = RTThreadCreate(&pData->ReceiveThread, drvHostSerialReceiveLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "Char Receive");
     800    rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pData->pReceiveThread, pData, drvHostSerialReceiveThread, drvHostSerialWakeupReceiveThread, 0, RTTHREADTYPE_IO, "Char Receive");
    638801    if (VBOX_FAILURE(rc))
    639802        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create receive thread"), pDrvIns->iInstance);
     
    642805    AssertRC(rc);
    643806
    644     rc = RTThreadCreate(&pData->SendThread, drvHostSerialSendLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "Serial Send");
     807    rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pData->pSendThread, pData, drvHostSerialSendThread, drvHostSerialWakeupSendThread, 0, RTTHREADTYPE_IO, "Serial Send");
    645808    if (VBOX_FAILURE(rc))
    646809        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create send thread"), pDrvIns->iInstance);
     
    664827static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
    665828{
    666     PDRVHOSTSERIAL     pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
    667     int rc;
     829    PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
    668830
    669831    LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
    670 
    671     ASMAtomicXchgBool(&pData->fShutdown, true);
    672 
    673 #ifdef RT_OS_LINUX
    674     /* Notify the receive thread that we want to shutdown. */
    675     rc = RTFileWrite (pData->WakeupPipeW, WAKE_UP_STRING, WAKE_UP_STRING_LEN, NULL);
    676     if (VBOX_SUCCESS (rc))
    677         RTFileFlush (pData->WakeupPipeW);
    678 #endif
    679     if (pData->ReceiveThread != NIL_RTTHREAD)
    680     {
    681         rc = RTThreadWait(pData->ReceiveThread, 15000, NULL);
    682         if (RT_FAILURE(rc))
    683             LogRel(("HostSerial%d: receive thread did not terminate (rc=%Rrc)\n", pDrvIns->iInstance, rc));
    684         pData->ReceiveThread = NIL_RTTHREAD;
    685     }
    686832
    687833    /* Empty the send queue */
    688834    pData->iSendQueueTail = pData->iSendQueueHead = 0;
    689835
    690     RTSemEventSignal(pData->SendSem);
    691836    RTSemEventDestroy(pData->SendSem);
    692837    pData->SendSem = NIL_RTSEMEVENT;
    693838
    694     if (pData->SendThread != NIL_RTTHREAD)
    695     {
    696         int rc = RTThreadWait(pData->SendThread, 15000, NULL);
    697         if (RT_FAILURE(rc))
    698             LogRel(("HostSerial%d: send thread did not terminate (rc=%Rrc)\n", pDrvIns->iInstance, rc));
    699         pData->SendThread = NIL_RTTHREAD;
    700     }
    701 
    702 #ifdef RT_OS_LINUX
    703     RTFileClose(pData->WakeupPipeR);
    704     RTFileClose(pData->WakeupPipeW);
    705 #endif
    706 
    707     RTFileClose(pData->DeviceFile);
     839#if defined(RT_OS_LINUX)
     840
     841    if (pData->WakeupPipeW != NIL_RTFILE)
     842    {
     843        int rc = RTFileClose(pData->WakeupPipeW);
     844        AssertRC(rc);
     845        pData->WakeupPipeW = NIL_RTFILE;
     846    }
     847    if (pData->WakeupPipeR != NIL_RTFILE)
     848    {
     849        int rc = RTFileClose(pData->WakeupPipeR);
     850        AssertRC(rc);
     851        pData->WakeupPipeR = NIL_RTFILE;
     852    }
     853    if (pData->DeviceFile != NIL_RTFILE)
     854    {
     855        int rc = RTFileClose(pData->DeviceFile);
     856        AssertRC(rc);
     857        pData->DeviceFile = NIL_RTFILE;
     858    }
     859
     860#elif defined(RT_OS_WINDOWS)
     861
     862    CloseHandle(pData->hEventReceive);
     863    CloseHandle(pData->hEventSend);
     864    CancelIo(pData->hDeviceFile);
     865    CloseHandle(pData->hDeviceFile);
     866
     867#endif
    708868}
    709869
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