VirtualBox

Changeset 27431 in vbox


Ignore:
Timestamp:
Mar 17, 2010 1:33:58 AM (15 years ago)
Author:
vboxsync
Message:

iprt: Implemented RTPollSet on windows (pipes only). Optimized the RTPipeWrite (nonblocking) and RTPipeSelectOne implementations on windows so that we're down to one failing unit test in tstRTPipe.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/err.h

    r26801 r27431  
    12511251/** The handle ID was not found in the set. */
    12521252#define VERR_POLL_HANDLE_ID_NOT_FOUND           (-952)
     1253/** The poll set is full. */
     1254#define VERR_POLL_SET_IS_FULL                   (-953)
    12531255/** @} */
    12541256
  • trunk/src/VBox/Runtime/r3/win/pipe-win.cpp

    r27388 r27431  
    4444#include <iprt/mem.h>
    4545#include <iprt/string.h>
     46#include <iprt/poll.h>
    4647#include <iprt/process.h>
    4748#include <iprt/thread.h>
    4849#include <iprt/time.h>
     50#include "internal/pipe.h"
    4951#include "internal/magics.h"
     52
     53
     54/*******************************************************************************
     55*   Defined Constants And Macros                                               *
     56*******************************************************************************/
     57/** The pipe buffer size we prefere. */
     58#define RTPIPE_NT_SIZE      _64K
    5059
    5160
     
    6372    /** Set if there is already pending I/O. */
    6473    bool                fIOPending;
     74    /** Set if the zero byte read that the poll code using is pending. */
     75    bool                fZeroByteRead;
     76    /** Set if the pipe is broken. */
     77    bool                fBrokenPipe;
     78    /** Set if we've promised that the handle is writable. */
     79    bool                fPromisedWritable;
    6580    /** The number of users of the current mode. */
    6681    uint32_t            cModeUsers;
     
    7388    /** Amount of allocated buffer space. */
    7489    size_t              cbBounceBufAlloc;
     90    /** The handle of the poll set currently polling on this pipe.
     91     *  We can only have one poller at the time (lazy bird). */
     92    RTPOLLSET           hPollSet;
     93    /** The number of references to the handle in hPollSet. */
     94    uint32_t            cPolls;
    7595    /** Critical section protecting the above members.
    7696     * (Taking the lazy/simple approach.) */
    7797    RTCRITSECT          CritSect;
     98    /** Buffer for the zero byte read. */
     99    uint8_t             abBuf[8];
    78100} RTPIPEINTERNAL;
     101
     102
     103/* from ntdef.h */
     104typedef LONG NTSTATUS;
     105
     106/* from ntddk.h */
     107typedef struct _IO_STATUS_BLOCK {
     108    union {
     109        NTSTATUS Status;
     110        PVOID Pointer;
     111    };
     112    ULONG_PTR Information;
     113} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
     114
     115typedef enum _FILE_INFORMATION_CLASS {
     116    FilePipeInformation = 23,
     117    FilePipeLocalInformation = 24,
     118    FilePipeRemoteInformation = 25,
     119} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
     120
     121/* from ntifs.h */
     122typedef struct _FILE_PIPE_LOCAL_INFORMATION {
     123     ULONG NamedPipeType;
     124     ULONG NamedPipeConfiguration;
     125     ULONG MaximumInstances;
     126     ULONG CurrentInstances;
     127     ULONG InboundQuota;
     128     ULONG ReadDataAvailable;
     129     ULONG OutboundQuota;
     130     ULONG WriteQuotaAvailable;
     131     ULONG NamedPipeState;
     132     ULONG NamedPipeEnd;
     133} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
     134
     135#define FILE_PIPE_DISCONNECTED_STATE    0x00000001
     136#define FILE_PIPE_LISTENING_STATE       0x00000002
     137#define FILE_PIPE_CONNECTED_STATE       0x00000003
     138#define FILE_PIPE_CLOSING_STATE         0x00000004
     139
     140
     141extern "C" NTSYSAPI NTSTATUS WINAPI NtQueryInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, LONG, FILE_INFORMATION_CLASS);
     142
     143
     144/**
     145 * Wrapper for getting FILE_PIPE_LOCAL_INFORMATION via the NT API.
     146 *
     147 * @returns Success inidicator (true/false).
     148 * @param   pThis               The pipe.
     149 * @param   pInfo               The info structure.
     150 */
     151static bool rtPipeQueryInfo(RTPIPEINTERNAL *pThis, FILE_PIPE_LOCAL_INFORMATION *pInfo)
     152{
     153    IO_STATUS_BLOCK Ios;
     154    RT_ZERO(Ios);
     155    RT_ZERO(*pInfo);
     156    NTSTATUS rcNt = NtQueryInformationFile(pThis->hPipe, &Ios, pInfo, sizeof(*pInfo), FilePipeLocalInformation);
     157    return rcNt >= 0;
     158}
    79159
    80160
     
    118198#endif
    119199
    120         hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, _64K,  _64K,
     200        hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
    121201                                  NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
    122202#ifdef PIPE_REJECT_REMOTE_CLIENTS
     
    124204        {
    125205            dwPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS;
    126             hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, _64K,  _64K,
     206            hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
    127207                                      NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
    128208        }
     
    132212        {
    133213            dwOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE;
    134             hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, _64K,  _64K,
     214            hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
    135215                                      NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
    136216        }
     
    201281                            pThisR->fRead           = true;
    202282                            pThisW->fRead           = false;
    203                             pThisR->fIOPending      = false;
    204                             pThisW->fIOPending      = false;
     283                            //pThisR->fIOPending      = false;
     284                            //pThisW->fIOPending      = false;
     285                            //pThisR->fZeroByteRead   = false;
     286                            //pThisW->fZeroByteRead   = false;
     287                            //pThisR->fBrokenPipe     = false;
     288                            //pThisW->fBrokenPipe     = false;
     289                            //pThisW->fPromisedWritable= false;
     290                            //pThisR->fPromisedWritable= false;
    205291                            //pThisR->cModeUsers      = 0;
    206292                            //pThisW->cModeUsers      = 0;
     
    211297                            //pThisR->cbBounceBufAlloc= 0;
    212298                            //pThisW->cbBounceBufAlloc= 0;
     299                            pThisR->hPollSet        = NIL_RTPOLLSET;
     300                            pThisW->hPollSet        = NIL_RTPOLLSET;
     301                            //pThisW->cPolls          = 0;
     302                            //pThisR->cPolls          = 0;
    213303
    214304                            *phPipeRead  = pThisR;
     
    278368                        else
    279369                            rc = RTErrConvertFromWin32(GetLastError());
     370                        if (rc == VERR_BROKEN_PIPE)
     371                            pThis->fBrokenPipe = true;
    280372                    }
    281373                    break;
     
    408500            else
    409501                rc = RTErrConvertFromWin32(GetLastError());
     502            if (rc == VERR_BROKEN_PIPE)
     503                pThis->fBrokenPipe = true;
    410504
    411505            pThis->cModeUsers--;
     
    474568            }
    475569
     570            if (rc == VERR_BROKEN_PIPE)
     571                pThis->fBrokenPipe = true;
     572
    476573            if (pcbRead)
    477574            {
     
    519616                Assert(!pThis->fIOPending);
    520617
     618                /* Adjust the number of bytes to write to fit into the current
     619                   buffer quota, unless we've promissed stuff in RTPipeSelectOne.
     620                   WriteQuotaAvailable better not be zero when it shouldn't!! */
     621                FILE_PIPE_LOCAL_INFORMATION Info;
     622                if (   !pThis->fPromisedWritable
     623                    && cbToWrite > 0
     624                    && rtPipeQueryInfo(pThis, &Info))
     625                {
     626                    if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
     627                        rc = VERR_BROKEN_PIPE;
     628                    else if (   cbToWrite >= Info.WriteQuotaAvailable
     629                             && Info.OutboundQuota != 0)
     630                    {
     631                        cbToWrite = Info.WriteQuotaAvailable;
     632                        if (!cbToWrite)
     633                            rc = VINF_TRY_AGAIN;
     634                    }
     635                }
     636                pThis->fPromisedWritable = false;
     637
    521638                /* Do the bounce buffering. */
    522639                if (    pThis->cbBounceBufAlloc < cbToWrite
    523                     &&  pThis->cbBounceBufAlloc < _64K)
    524                 {
    525                     if (cbToWrite > _64K)
    526                         cbToWrite = _64K;
     640                    &&  pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE)
     641                {
     642                    if (cbToWrite > RTPIPE_NT_SIZE)
     643                        cbToWrite = RTPIPE_NT_SIZE;
    527644                    void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
    528645                    if (pv)
     
    534651                        rc = VERR_NO_MEMORY;
    535652                }
    536                 else if (cbToWrite > _64K)
    537                     cbToWrite = _64K;
     653                else if (cbToWrite > RTPIPE_NT_SIZE)
     654                    cbToWrite = RTPIPE_NT_SIZE;
    538655                if (RT_SUCCESS(rc) && cbToWrite)
    539656                {
     
    567684                *pcbWritten = 0;
    568685
     686            if (rc == VERR_BROKEN_PIPE)
     687                pThis->fBrokenPipe = true;
     688
    569689            pThis->cModeUsers--;
    570690        }
     
    612732            {
    613733                Assert(!pThis->fIOPending);
     734                pThis->fPromisedWritable = false;
    614735
    615736                /*
     
    663784            }
    664785
     786            if (rc == VERR_BROKEN_PIPE)
     787                pThis->fBrokenPipe = true;
     788
    665789            pThis->cModeUsers--;
    666790        }
     
    717841
    718842    if (!FlushFileBuffers(pThis->hPipe))
    719         return RTErrConvertFromWin32(GetLastError());
     843    {
     844        int rc = RTErrConvertFromWin32(GetLastError());
     845        if (rc == VERR_BROKEN_PIPE)
     846            pThis->fBrokenPipe = true;
     847        return rc;
     848    }
    720849    return VINF_SUCCESS;
    721850}
     
    735864    for (unsigned iLoop = 0;; iLoop++)
    736865    {
    737         uint8_t abBuf[4];
    738         bool    fPendingRead  = false;
    739         HANDLE  hWait         = INVALID_HANDLE_VALUE;
     866        HANDLE  hWait = INVALID_HANDLE_VALUE;
    740867        if (pThis->fRead)
    741868        {
     
    762889                rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
    763890                DWORD cbRead = 0;
    764                 if (ReadFile(pThis->hPipe, &abBuf[0], 0, &cbRead, &pThis->Overlapped))
     891                if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
    765892                {
    766893                    rc = VINF_SUCCESS;
     
    772899                    pThis->cModeUsers++;
    773900                    pThis->fIOPending = true;
    774                     fPendingRead = true;
     901                    pThis->fZeroByteRead = true;
    775902                    hWait = pThis->Overlapped.hEvent;
    776903                }
     
    781908        else
    782909        {
     910            if (pThis->fIOPending)
     911            {
     912                rc = rtPipeWriteCheckCompletion(pThis);
     913                if (RT_FAILURE(rc))
     914                    break;
     915            }
    783916            if (pThis->fIOPending)
    784917                hWait = pThis->Overlapped.hEvent;
    785918            else
    786919            {
    787                 /* If nothing pending, the next write will succeed because
    788                    we buffer it and pretend that it does... */
    789                 rc = VINF_SUCCESS;
    790                 break;
     920                FILE_PIPE_LOCAL_INFORMATION Info;
     921                if (rtPipeQueryInfo(pThis, &Info))
     922                {
     923                    /* Check for broken pipe. */
     924                    if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
     925                    {
     926                        rc = VERR_BROKEN_PIPE;
     927                        break;
     928                    }
     929                    /* Check for available write buffer space. */
     930                    else if (Info.WriteQuotaAvailable > 0)
     931                    {
     932                        pThis->fPromisedWritable = false;
     933                        rc = VINF_SUCCESS;
     934                        break;
     935                    }
     936                    /* delayed buffer alloc or timeout: phony promise
     937                       later: See if we still can associate a semaphore with
     938                              the pipe, like on OS/2. */
     939                    else if (   Info.OutboundQuota == 0
     940                             || cMillies)
     941                    {
     942                        pThis->fPromisedWritable = true;
     943                        rc = VINF_SUCCESS;
     944                        break;
     945                    }
     946                }
     947                else
     948                {
     949                    pThis->fPromisedWritable = true;
     950                    rc = VINF_SUCCESS;
     951                    break;
     952                }
    791953            }
    792954        }
     
    833995
    834996            RTCritSectEnter(&pThis->CritSect);
    835             if (fPendingRead)
     997            if (pThis->fZeroByteRead)
    836998            {
    837999                pThis->cModeUsers--;
     
    8471009    }
    8481010
     1011    if (rc == VERR_BROKEN_PIPE)
     1012        pThis->fBrokenPipe = true;
     1013
    8491014    RTCritSectLeave(&pThis->CritSect);
    8501015    return rc;
    8511016}
    8521017
     1018
     1019/**
     1020 * Internal RTPollSetAdd helper that returns one or two handles that should be
     1021 * added to the pollset.
     1022 *
     1023 * @returns Valid handle on success, INVALID_HANDLE_VALUE on failure.
     1024 * @param   hPipe               The pipe handle.
     1025 * @param   fEvents             The events we're polling for.
     1026 * @param   ph1                 wher to put the primary handle.
     1027 * @param   ph2                 Where to optionally return a 2nd handle.
     1028 */
     1029int rtPipePollGetHandles(RTPIPE hPipe, uint32_t fEvents, PHANDLE ph1, PHANDLE ph2)
     1030{
     1031    RTPIPEINTERNAL *pThis = hPipe;
     1032    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     1033    AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
     1034
     1035    AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
     1036    AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
     1037
     1038    *ph1 = pThis->Overlapped.hEvent;
     1039    /* Later: Try register an event handle with the pipe like on OS/2, there is
     1040       a file control for doing this obviously intended for the OS/2 subsys.
     1041       The question is whether this still exists on Vista and W7. */
     1042    *ph2 = INVALID_HANDLE_VALUE;
     1043    return VINF_SUCCESS;
     1044}
     1045
     1046
     1047/**
     1048 * Checks for pending events.
     1049 *
     1050 * @returns Event mask or 0.
     1051 * @param   pThis               The pipe handle.
     1052 * @param   fEvents             The desired events.
     1053 */
     1054static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents)
     1055{
     1056    uint32_t fRetEvents = 0;
     1057    if (pThis->fBrokenPipe)
     1058        fRetEvents |= RTPOLL_EVT_ERROR;
     1059    else if (pThis->fRead)
     1060    {
     1061        if (!pThis->fIOPending)
     1062        {
     1063            DWORD cbAvailable;
     1064            if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
     1065            {
     1066                if (   (fEvents & RTPOLL_EVT_READ)
     1067                    && cbAvailable > 0)
     1068                    fRetEvents |= RTPOLL_EVT_READ;
     1069            }
     1070            else
     1071            {
     1072                if (GetLastError() == ERROR_BROKEN_PIPE)
     1073                    pThis->fBrokenPipe = true;
     1074                fRetEvents |= RTPOLL_EVT_ERROR;
     1075            }
     1076        }
     1077    }
     1078    else
     1079    {
     1080        if (pThis->fIOPending)
     1081        {
     1082            rtPipeWriteCheckCompletion(pThis);
     1083            if (pThis->fBrokenPipe)
     1084                fRetEvents |= RTPOLL_EVT_ERROR;
     1085        }
     1086        if (   !pThis->fIOPending
     1087            && !fRetEvents)
     1088        {
     1089            FILE_PIPE_LOCAL_INFORMATION Info;
     1090            if (rtPipeQueryInfo(pThis, &Info))
     1091            {
     1092                /* Check for broken pipe. */
     1093                if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
     1094                {
     1095                    fRetEvents = RTPOLL_EVT_ERROR;
     1096                    pThis->fBrokenPipe = true;
     1097                }
     1098
     1099                /* Check if there is available buffer space. */
     1100                if (   !fRetEvents
     1101                    && (   Info.WriteQuotaAvailable > 0
     1102                        || Info.OutboundQuota == 0)
     1103                    )
     1104                    fRetEvents |= RTPOLL_EVT_WRITE;
     1105            }
     1106            else if (   !fRetEvents
     1107                && (fEvents & RTPOLL_EVT_WRITE))
     1108                fRetEvents |= RTPOLL_EVT_WRITE;
     1109        }
     1110    }
     1111
     1112    return fRetEvents;
     1113}
     1114
     1115
     1116/**
     1117 * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
     1118 * clear, starts whatever actions we've got running during the poll call.
     1119 *
     1120 * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
     1121 *          Event mask (in @a fEvents) and no actions if the handle is ready
     1122 *          already.
     1123 *          UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
     1124 *          different poll set.
     1125 *
     1126 * @param   hPipe               The pipe handle.
     1127 * @param   hPollSet            The poll set handle (for access checks).
     1128 * @param   fEvents             The events we're polling for.
     1129 * @param   fNoWait             Set if it's a zero-wait poll call.  Clear if
     1130 *                              we'll wait for an event to occur.
     1131 */
     1132uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fNoWait)
     1133{
     1134    /** @todo All this polling code could be optimized to make fewer system
     1135     *        calls; like for instance the ResetEvent calls. */
     1136    RTPIPEINTERNAL *pThis = hPipe;
     1137    AssertPtrReturn(pThis, UINT32_MAX);
     1138    AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
     1139
     1140    int rc = RTCritSectEnter(&pThis->CritSect);
     1141    AssertRCReturn(rc, UINT32_MAX);
     1142
     1143    /* Check that this is the only current use of this pipe. */
     1144    uint32_t fRetEvents;
     1145    if (   (   pThis->cPolls == 0
     1146            && pThis->cModeUsers == 0)
     1147        || pThis->hPollSet == hPollSet
     1148       )
     1149    {
     1150        /* Check what the current events are. */
     1151        fRetEvents = rtPipePollCheck(pThis, fEvents);
     1152        if (   !fRetEvents
     1153            && !fNoWait)
     1154        {
     1155            /* Make sure the event semaphore has been reset. */
     1156            if (!pThis->fIOPending)
     1157            {
     1158                rc = ResetEvent(pThis->Overlapped.hEvent);
     1159                Assert(rc == TRUE);
     1160            }
     1161
     1162            /* Kick off the zero byte read thing if applicable. */
     1163            if (   !pThis->fIOPending
     1164                && pThis->fRead
     1165                && (fEvents & RTPOLL_EVT_READ)
     1166               )
     1167            {
     1168                DWORD cbRead = 0;
     1169                if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
     1170                    fRetEvents = rtPipePollCheck(pThis, fEvents);
     1171                else if (GetLastError() == ERROR_IO_PENDING)
     1172                {
     1173                    pThis->fIOPending    = true;
     1174                    pThis->fZeroByteRead = true;
     1175                }
     1176                else
     1177                    fRetEvents = RTPOLL_EVT_ERROR;
     1178            }
     1179
     1180            /* If we're still set for the waiting, record the poll set and
     1181               mark the pipe used. */
     1182            if (!fRetEvents)
     1183            {
     1184                pThis->cPolls++;
     1185                pThis->cModeUsers++;
     1186                pThis->hPollSet = hPollSet;
     1187            }
     1188        }
     1189    }
     1190    else
     1191    {
     1192        AssertFailed();
     1193        fRetEvents = UINT32_MAX;
     1194    }
     1195
     1196    RTCritSectLeave(&pThis->CritSect);
     1197    return fRetEvents;
     1198}
     1199
     1200
     1201/**
     1202 * Called after a WaitForMultipleObjects returned in order to check for pending
     1203 * events and stop whatever actions that rtPipePollStart() initiated.
     1204 *
     1205 * @returns Event mask or 0.
     1206 *
     1207 * @param   hPipe               The pipe handle.
     1208 * @param   fEvents             The events we're polling for.
     1209 */
     1210uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents)
     1211{
     1212    RTPIPEINTERNAL *pThis = hPipe;
     1213    AssertPtrReturn(pThis, 0);
     1214    AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
     1215
     1216    int rc = RTCritSectEnter(&pThis->CritSect);
     1217    AssertRCReturn(rc, 0);
     1218
     1219    Assert(pThis->cPolls > 0);
     1220    Assert(pThis->cModeUsers > 0);
     1221
     1222
     1223    /* Cancel the zero byte read. */
     1224    uint32_t fRetEvents = 0;
     1225    if (pThis->fZeroByteRead)
     1226    {
     1227        CancelIo(pThis->hPipe);
     1228        DWORD cbRead = 0;
     1229        if (   !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)
     1230            && GetLastError() != ERROR_OPERATION_ABORTED)
     1231            fRetEvents = RTPOLL_EVT_ERROR;
     1232
     1233        pThis->fIOPending    = false;
     1234        pThis->fZeroByteRead = false;
     1235    }
     1236
     1237    /* harvest events. */
     1238    fRetEvents |= rtPipePollCheck(pThis, fEvents);
     1239
     1240    /* update counters. */
     1241    pThis->cPolls--;
     1242    if (!pThis->cPolls)
     1243        pThis->hPollSet = NIL_RTPOLLSET;
     1244    pThis->cModeUsers--;
     1245
     1246    RTCritSectLeave(&pThis->CritSect);
     1247    return fRetEvents;
     1248}
     1249
  • trunk/src/VBox/Runtime/r3/win/poll-win.cpp

    r26844 r27431  
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include <Windows.h>
     36
    3537#include <iprt/poll.h>
    3638#include "internal/iprt.h"
    3739
     40#include <iprt/asm.h>
    3841#include <iprt/assert.h>
    3942#include <iprt/err.h>
     43#include <iprt/mem.h>
     44#include <iprt/pipe.h>
     45#include <iprt/string.h>
     46#include <iprt/thread.h>
     47#include <iprt/time.h>
     48#include "internal/pipe.h"
    4049#include "internal/magics.h"
    4150
    4251
     52/*******************************************************************************
     53*   Structures and Typedefs                                                    *
     54*******************************************************************************/
     55/**
     56 * Handle entry in a poll set.
     57 */
     58typedef struct RTPOLLSETHNDENT
     59{
     60    /** The handle type. */
     61    RTHANDLETYPE    enmType;
     62    /** The handle ID. */
     63    uint32_t        id;
     64    /** The events we're waiting for here. */
     65    uint32_t        fEvents;
     66    /** The handle union. */
     67    RTHANDLEUNION   u;
     68} RTPOLLSETHNDENT;
     69/** Pointer to a handle entry. */
     70typedef RTPOLLSETHNDENT *PRTPOLLSETHNDENT;
     71
     72
     73/**
     74 * Poll set data, Windows.
     75 */
     76typedef struct RTPOLLSETINTERNAL
     77{
     78    /** The magic value (RTPOLLSET_MAGIC). */
     79    uint32_t            u32Magic;
     80    /** Set when someone is polling or making changes. */
     81    bool volatile       fBusy;
     82
     83    /** The number of valid handles in the set. */
     84    uint32_t            cHandles;
     85    /** The native handles. */
     86    HANDLE              ahNative[MAXIMUM_WAIT_OBJECTS];
     87    /** Array of handles and IDs. */
     88    RTPOLLSETHNDENT     aHandles[MAXIMUM_WAIT_OBJECTS];
     89} RTPOLLSETINTERNAL;
     90
     91
     92/**
     93 * Common worker for RTPoll and RTPollNoResume
     94 */
     95static int rtPollNoResumeWorker(RTPOLLSETINTERNAL *pThis, RTMSINTERVAL cMillies, uint32_t *pfEvents, uint32_t *pid)
     96{
     97    int rc;
     98
     99    if (RT_UNLIKELY(pThis->cHandles == 0 && cMillies == RT_INDEFINITE_WAIT))
     100        return VERR_DEADLOCK;
     101
     102    /*
     103     * Check for special case, RTThreadSleep...
     104     */
     105    uint32_t const  cHandles = pThis->cHandles;
     106    if (cHandles == 0)
     107    {
     108        rc = RTThreadSleep(cMillies);
     109        if (RT_SUCCESS(rc))
     110            rc = VERR_TIMEOUT;
     111        return rc;
     112    }
     113
     114    /*
     115     * Check + prepare the handles before waiting.
     116     */
     117    uint32_t        fEvents  = 0;
     118    bool const      fNoWait  = cMillies == 0;
     119    uint32_t        i;
     120    for (i = 0; i < cHandles; i++)
     121    {
     122        switch (pThis->aHandles[i].enmType)
     123        {
     124            case RTHANDLETYPE_PIPE:
     125                fEvents = rtPipePollStart(pThis->aHandles[i].u.hPipe, pThis, pThis->aHandles[i].fEvents, fNoWait);
     126                break;
     127
     128            default:
     129                AssertFailed();
     130                fEvents = UINT32_MAX;
     131                break;
     132        }
     133        if (fEvents)
     134            break;
     135    }
     136    if (   fEvents
     137        || fNoWait)
     138    {
     139
     140        if (pid)
     141            *pid = pThis->aHandles[i].id;
     142        if (pfEvents)
     143            *pfEvents = fEvents;
     144        rc = !fEvents
     145           ? VERR_TIMEOUT
     146           : fEvents != UINT32_MAX
     147           ? VINF_SUCCESS
     148           : VERR_INTERNAL_ERROR_4;
     149
     150        /* clean up */
     151        if (!fNoWait)
     152            while (i-- > 0)
     153            {
     154                switch (pThis->aHandles[i].enmType)
     155                {
     156                    case RTHANDLETYPE_PIPE:
     157                        rtPipePollDone(pThis->aHandles[i].u.hPipe, pThis->aHandles[i].fEvents);
     158                        break;
     159                    default:
     160                        AssertFailed();
     161                        break;
     162                }
     163            }
     164
     165        return rc;
     166    }
     167
     168    /*
     169     * Wait.
     170     */
     171    DWORD dwRc = WaitForMultipleObjectsEx(cHandles, &pThis->ahNative[0],
     172                                          FALSE /*fWaitAll */,
     173                                          cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies,
     174                                          TRUE /*fAlertable*/);
     175    if (    dwRc >= WAIT_OBJECT_0
     176        &&  dwRc <  WAIT_OBJECT_0 + cHandles)
     177        rc = VINF_SUCCESS;
     178    else if (dwRc == WAIT_TIMEOUT)
     179        rc = VERR_TIMEOUT;
     180    else if (dwRc == WAIT_IO_COMPLETION)
     181        rc = VERR_INTERRUPTED;
     182    else if (dwRc == WAIT_FAILED)
     183        rc = RTErrConvertFromWin32(GetLastError());
     184    else
     185    {
     186        AssertMsgFailed(("%u (%#x)\n", dwRc, dwRc));
     187        rc = VERR_INTERNAL_ERROR_5;
     188    }
     189
     190    /*
     191     * Get event (if pending) and do wait cleanup.
     192     */
     193    i = cHandles;
     194    while (i-- > 0)
     195    {
     196        fEvents = 0;
     197        switch (pThis->aHandles[i].enmType)
     198        {
     199            case RTHANDLETYPE_PIPE:
     200                fEvents = rtPipePollDone(pThis->aHandles[i].u.hPipe, pThis->aHandles[i].fEvents);
     201                break;
     202            default:
     203                AssertFailed();
     204                break;
     205        }
     206        if (fEvents)
     207        {
     208            Assert(fEvents != UINT32_MAX);
     209            if (pfEvents)
     210                *pfEvents = fEvents;
     211            if (pid)
     212                *pid = pThis->aHandles[i].id;
     213            rc = VINF_SUCCESS;
     214        }
     215    }
     216
     217    return rc;
     218}
     219
     220
    43221RTDECL(int) RTPoll(RTPOLLSET hPollSet, RTMSINTERVAL cMillies, uint32_t *pfEvents, uint32_t *pid)
    44222{
    45     return VERR_NOT_IMPLEMENTED;
     223    RTPOLLSETINTERNAL *pThis = hPollSet;
     224    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     225    AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
     226    AssertPtrNull(pfEvents);
     227    AssertPtrNull(pid);
     228
     229    /*
     230     * Set the busy flag and do the job.
     231     */
     232    AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true,  false), VERR_WRONG_ORDER);
     233
     234    int rc;
     235    if (cMillies == RT_INDEFINITE_WAIT || cMillies == 0)
     236    {
     237        do rc = rtPollNoResumeWorker(pThis, cMillies, pfEvents, pid);
     238        while (rc == VERR_INTERRUPTED);
     239    }
     240    else
     241    {
     242        uint64_t MsStart = RTTimeMilliTS();
     243        rc = rtPollNoResumeWorker(pThis, cMillies, pfEvents, pid);
     244        while (RT_UNLIKELY(rc == VERR_INTERRUPTED))
     245        {
     246            if (RTTimeMilliTS() - MsStart >= cMillies)
     247            {
     248                rc = VERR_TIMEOUT;
     249                break;
     250            }
     251            rc = rtPollNoResumeWorker(pThis, cMillies, pfEvents, pid);
     252        }
     253    }
     254
     255    ASMAtomicWriteBool(&pThis->fBusy, false);
     256
     257    return rc;
    46258}
    47259
     
    49261RTDECL(int) RTPollNoResume(RTPOLLSET hPollSet, RTMSINTERVAL cMillies, uint32_t *pfEvents, uint32_t *pid)
    50262{
    51     return VERR_NOT_IMPLEMENTED;
    52 }
    53 
    54 
    55 RTDECL(int)  RTPollSetCreate(PRTPOLLSET hPollSet)
    56 {
    57     return VERR_NOT_IMPLEMENTED;
    58 }
    59 
    60 
    61 RTDECL(int)  RTPollSetDestroy(RTPOLLSET hPollSet)
    62 {
    63     return VERR_NOT_IMPLEMENTED;
     263    RTPOLLSETINTERNAL *pThis = hPollSet;
     264    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     265    AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
     266    AssertPtrNull(pfEvents);
     267    AssertPtrNull(pid);
     268
     269    /*
     270     * Set the busy flag and do the job.
     271     */
     272    AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true,  false), VERR_WRONG_ORDER);
     273
     274    int rc = rtPollNoResumeWorker(pThis, cMillies, pfEvents, pid);
     275
     276    ASMAtomicWriteBool(&pThis->fBusy, false);
     277
     278    return rc;
     279}
     280
     281
     282RTDECL(int) RTPollSetCreate(PRTPOLLSET phPollSet)
     283{
     284    AssertPtrReturn(phPollSet, VERR_INVALID_POINTER);
     285    RTPOLLSETINTERNAL *pThis = (RTPOLLSETINTERNAL *)RTMemAllocZ(sizeof(RTPOLLSETINTERNAL));
     286    if (!pThis)
     287        return VERR_NO_MEMORY;
     288
     289    pThis->u32Magic             = RTPOLLSET_MAGIC;
     290    pThis->fBusy                = false;
     291    pThis->cHandles             = 0;
     292    for (size_t i = 0; i < RT_ELEMENTS(pThis->ahNative); i++)
     293        pThis->ahNative[i]     = INVALID_HANDLE_VALUE;
     294
     295    *phPollSet = pThis;
     296    return VINF_SUCCESS;
     297}
     298
     299
     300RTDECL(int) RTPollSetDestroy(RTPOLLSET hPollSet)
     301{
     302    RTPOLLSETINTERNAL *pThis = hPollSet;
     303    if (pThis == NIL_RTPOLLSET)
     304        return VINF_SUCCESS;
     305    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     306    AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
     307    AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true,  false), VERR_WRONG_ORDER);
     308
     309    ASMAtomicWriteU32(&pThis->u32Magic, ~RTPOLLSET_MAGIC);
     310    RTMemFree(pThis);
     311
     312    return VINF_SUCCESS;
    64313}
    65314
     
    67316RTDECL(int) RTPollSetAdd(RTPOLLSET hPollSet, PCRTHANDLE pHandle, uint32_t fEvents, uint32_t id)
    68317{
    69     return VERR_NOT_IMPLEMENTED;
     318    /*
     319     * Validate the input (tedious).
     320     */
     321    RTPOLLSETINTERNAL *pThis = hPollSet;
     322    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     323    AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
     324    AssertReturn(!(fEvents & ~RTPOLL_EVT_VALID_MASK), VERR_INVALID_PARAMETER);
     325    AssertReturn(fEvents, VERR_INVALID_PARAMETER);
     326    AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
     327
     328    if (!pHandle)
     329        return VINF_SUCCESS;
     330    AssertPtrReturn(pHandle, VERR_INVALID_POINTER);
     331    AssertReturn(pHandle->enmType > RTHANDLETYPE_INVALID && pHandle->enmType < RTHANDLETYPE_END, VERR_INVALID_PARAMETER);
     332
     333    /*
     334     * Set the busy flag and do the job.
     335     */
     336    AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true,  false), VERR_WRONG_ORDER);
     337
     338    int     rc       = VINF_SUCCESS;
     339    HANDLE  hNative  = INVALID_HANDLE_VALUE;
     340    HANDLE  hNative2 = INVALID_HANDLE_VALUE;
     341    switch (pHandle->enmType)
     342    {
     343        case RTHANDLETYPE_PIPE:
     344            if (pHandle->u.hPipe != NIL_RTPIPE)
     345                rc = rtPipePollGetHandles(pHandle->u.hPipe, fEvents, &hNative, &hNative2);
     346            break;
     347
     348        case RTHANDLETYPE_SOCKET:
     349            if (pHandle->u.hSocket != NIL_RTSOCKET)
     350                rc = VERR_NOT_IMPLEMENTED;
     351            break;
     352
     353        case RTHANDLETYPE_FILE:
     354            AssertMsgFailed(("Files are always ready for reading/writing and thus not pollable. Use native APIs for special devices.\n"));
     355            rc = VERR_POLL_HANDLE_NOT_POLLABLE;
     356            break;
     357
     358        case RTHANDLETYPE_THREAD:
     359            AssertMsgFailed(("Thread handles are currently not pollable\n"));
     360            rc = VERR_POLL_HANDLE_NOT_POLLABLE;
     361            break;
     362
     363        default:
     364            AssertMsgFailed(("\n"));
     365            rc = VERR_POLL_HANDLE_NOT_POLLABLE;
     366            break;
     367    }
     368    if (   RT_SUCCESS(rc)
     369        && hNative != INVALID_HANDLE_VALUE)
     370    {
     371        uint32_t const i = pThis->cHandles;
     372
     373        /* Check that the handle ID doesn't exist already. */
     374        uint32_t j = i;
     375        while (j-- > 0)
     376            if (pThis->aHandles[j].id == id)
     377            {
     378                rc = VERR_POLL_HANDLE_ID_EXISTS;
     379                break;
     380            }
     381
     382        /* Check that we won't overflow the poll set now. */
     383        if (    RT_SUCCESS(rc)
     384            &&  i + 1 + (hNative2 != INVALID_HANDLE_VALUE) > RT_ELEMENTS(pThis->ahNative))
     385            rc = VERR_POLL_SET_IS_FULL;
     386        if (RT_SUCCESS(rc))
     387        {
     388            /* Add the handles to the two parallel arrays. */
     389            pThis->ahNative[i]         = hNative;
     390            pThis->aHandles[i].enmType = pHandle->enmType;
     391            pThis->aHandles[i].u       = pHandle->u;
     392            pThis->aHandles[i].id      = id;
     393            pThis->aHandles[i].fEvents = fEvents;
     394            if (hNative2 == INVALID_HANDLE_VALUE)
     395                pThis->cHandles = i + 1;
     396            else
     397            {
     398                pThis->ahNative[i + 1] = hNative2;
     399                pThis->aHandles[i + 1] = pThis->aHandles[i];
     400                pThis->cHandles = i + 2;
     401            }
     402
     403            rc = VINF_SUCCESS;
     404        }
     405    }
     406
     407    ASMAtomicWriteBool(&pThis->fBusy, false);
     408    return rc;
    70409}
    71410
     
    73412RTDECL(int) RTPollSetRemove(RTPOLLSET hPollSet, uint32_t id)
    74413{
    75     return VERR_NOT_IMPLEMENTED;
     414    /*
     415     * Validate the input.
     416     */
     417    RTPOLLSETINTERNAL *pThis = hPollSet;
     418    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     419    AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
     420    AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
     421
     422    /*
     423     * Set the busy flag and do the job.
     424     */
     425    AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true,  false), VERR_WRONG_ORDER);
     426
     427    int         rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
     428    uint32_t    i  = pThis->cHandles;
     429    while (i-- > 0)
     430        if (pThis->aHandles[i].id == id)
     431        {
     432            pThis->cHandles--;
     433            size_t const cToMove = pThis->cHandles - i;
     434            if (cToMove)
     435            {
     436                memmove(&pThis->aHandles[i], &pThis->aHandles[i + 1], cToMove * sizeof(pThis->aHandles[i]));
     437                memmove(&pThis->ahNative[i], &pThis->ahNative[i + 1], cToMove * sizeof(pThis->ahNative[i]));
     438            }
     439            rc = VINF_SUCCESS;
     440        }
     441
     442    ASMAtomicWriteBool(&pThis->fBusy, false);
     443    return rc;
    76444}
    77445
     
    79447RTDECL(int) RTPollSetQueryHandle(RTPOLLSET hPollSet, uint32_t id, PRTHANDLE pHandle)
    80448{
    81     return VERR_NOT_IMPLEMENTED;
     449    /*
     450     * Validate the input.
     451     */
     452    RTPOLLSETINTERNAL *pThis = hPollSet;
     453    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     454    AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
     455    AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
     456    AssertPtrNullReturn(pHandle, VERR_INVALID_POINTER);
     457
     458    /*
     459     * Set the busy flag and do the job.
     460     */
     461    AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true,  false), VERR_WRONG_ORDER);
     462
     463    int         rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
     464    uint32_t    i  = pThis->cHandles;
     465    while (i-- > 0)
     466        if (pThis->aHandles[i].id == id)
     467        {
     468            if (pHandle)
     469            {
     470                pHandle->enmType = pThis->aHandles[i].enmType;
     471                pHandle->u       = pThis->aHandles[i].u;
     472            }
     473            rc = VINF_SUCCESS;
     474            break;
     475        }
     476
     477    ASMAtomicWriteBool(&pThis->fBusy, false);
     478    return rc;
    82479}
    83480
     
    85482RTDECL(uint32_t) RTPollSetCount(RTPOLLSET hPollSet)
    86483{
    87     return UINT32_MAX;
    88 }
    89 
     484    /*
     485     * Validate the input.
     486     */
     487    RTPOLLSETINTERNAL *pThis = hPollSet;
     488    AssertPtrReturn(pThis, UINT32_MAX);
     489    AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, UINT32_MAX);
     490
     491    /*
     492     * Set the busy flag and do the job.
     493     */
     494    AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true,  false), UINT32_MAX);
     495    uint32_t cHandles = pThis->cHandles;
     496    ASMAtomicWriteBool(&pThis->fBusy, false);
     497
     498    return cHandles;
     499}
     500
     501
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