VirtualBox

Changeset 23092 in vbox


Ignore:
Timestamp:
Sep 17, 2009 1:20:18 PM (15 years ago)
Author:
vboxsync
Message:

Main/glue: EventQueue bugfixes and code style adjustments and cleanups. Windows was polling instead of waiting and processEventQueue didn't return what it should on all platforms (nobody uses it currently, so that didn't break anything). Tested on 64-bit linux, 64-bit windows and 32-bit darwin.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/com/EventQueue.h

    r22911 r23092  
    3333
    3434#if !defined (VBOX_WITH_XPCOM)
    35 #include <windows.h>
     35# include <Windows.h>
    3636#else
    37 #include <nsEventQueueUtils.h>
     37# include <nsEventQueueUtils.h>
    3838#endif
    3939
     
    7777 *  Simple event queue.
    7878 *
    79  *  On Linux, if this queue is created on the main thread, it automatically
    80  *  processes XPCOM/IPC events while waiting for its own (Event) events.
     79 *  When using XPCOM, this will map onto the default XPCOM queue for the thread.
     80 *  So, if a queue is created on the main thread, it automatically processes
     81 *  XPCOM/IPC events while waiting for its own (Event) events.
     82 *
     83 *  When using Windows, Darwin and OS/2, this will map onto the native thread
     84 *  queue/runloop.  So, windows messages and want not will be processed while
     85 *  waiting for events.
    8186 */
    8287class EventQueue
     
    9095    BOOL waitForEvent (Event **event);
    9196    BOOL handleEvent (Event *event);
    92     /**
    93      * Process events pending on this event queue, and wait
    94      * up to given timeout, if nothing is available.
    95      * Must be called on same thread this event queue was created on.
    96      */
    9797    int processEventQueue(uint32_t cMsTimeout);
    98     /**
    99      * Interrupt thread waiting on event queue processing.
    100      * Can be called on any thread.
    101      */
    10298    int interruptEventQueueProcessing();
    103     /**
    104      * Get select()'able selector for this event queue, can be -1
    105      * on platforms not supporting such functionality.
    106      */
    10799    int getSelectFD();
    108     /**
    109      * Initialize/deinitialize event queues.
    110      */
    111100    static int init();
    112     static int deinit();
    113     /**
    114      * Get main event queue instance.
    115      */
    116     static EventQueue* getMainEventQueue();
     101    static int uninit();
     102    static EventQueue *getMainEventQueue();
    117103
    118104private:
    119     static EventQueue* mMainQueue;
     105    static EventQueue *mMainQueue;
    120106
    121107#if !defined (VBOX_WITH_XPCOM)
    122108
     109    /** The thread which the queue belongs to. */
    123110    DWORD mThreadId;
     111    /** Duplicated thread handle for MsgWaitForMultipleObjects. */
     112    HANDLE mhThread;
    124113
    125114#else
    126115
     116    /** Whether it was created (and thus needs destroying) or if a queue already
     117     *  associated with the thread was used. */
    127118    BOOL mEQCreated;
    128119
  • trunk/src/VBox/Main/glue/EventQueue.cpp

    r22916 r23092  
    7070#endif // !defined (RT_OS_WINDOWS)
    7171
    72 EventQueue* EventQueue::mMainQueue = NULL;
     72EventQueue *EventQueue::mMainQueue = NULL;
    7373
    7474/**
     
    8787    MSG msg;
    8888    PeekMessage (&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
     89
     90    if (!DuplicateHandle (GetCurrentProcess(),
     91                          GetCurrentThread(),
     92                          GetCurrentProcess(),
     93                          &mhThread,
     94                          0 /*dwDesiredAccess*/,
     95                          FALSE /*bInheritHandle*/,
     96                          DUPLICATE_SAME_ACCESS))
     97      mhThread = INVALID_HANDLE_VALUE;
    8998
    9099#else
     
    132141{
    133142#if defined (RT_OS_WINDOWS)
     143    if (mhThread != INVALID_HANDLE_VALUE)
     144    {
     145        CloseHandle (mhThread);
     146        mhThread = INVALID_HANDLE_VALUE;
     147    }
    134148#else
    135149    // process all pending events before destruction
     
    148162}
    149163
    150 /* static */ int EventQueue::init()
    151 {
     164/**
     165 *  Initializes the main event queue instance.
     166 *  @returns VBox status code.
     167 *
     168 *  @remarks If you're using the rest of the COM/XPCOM glue library,
     169 *           com::Initialize() will take care of initializing and uninitializing
     170 *           the EventQueue class.  If you don't call com::Initialize, you must
     171 *           make sure to call this method on the same thread that did the
     172 *           XPCOM initialization or we'll end up using the wrong main queue.
     173 */
     174/* static */ int
     175EventQueue::init()
     176{
     177    Assert(mMainQueue == NULL);
    152178    mMainQueue = new EventQueue();
     179
    153180#if defined (VBOX_WITH_XPCOM)
     181    /* Check that it actually is the main event queue, i.e. that
     182       we're called on the right thread. */
    154183    nsCOMPtr<nsIEventQueue> q;
    155184    nsresult rv = NS_GetMainEventQ(getter_AddRefs(q));
    156185    Assert(NS_SUCCEEDED(rv));
    157186    Assert(q == mMainQueue->mEventQ);
     187
     188    /* Check that it's a native queue. */
    158189    PRBool fIsNative = PR_FALSE;
    159190    rv = mMainQueue->mEventQ->IsQueueNative(&fIsNative);
    160191    Assert(NS_SUCCEEDED(rv) && fIsNative);
    161192#endif
     193
    162194    return VINF_SUCCESS;
    163195}
    164196
    165 /* static */ int EventQueue::deinit()
     197/**
     198 *  Uninitialize the global resources (i.e. the main event queue instance).
     199 *  @returns VINF_SUCCESS
     200 */
     201/* static */ int
     202EventQueue::uninit()
    166203{
    167204    delete mMainQueue;
     
    170207}
    171208
    172 /* static */ EventQueue* EventQueue::getMainEventQueue()
     209/**
     210 *  Get main event queue instance.
     211 *
     212 *  Depends on init() being called first.
     213 */
     214/* static */ EventQueue *
     215EventQueue::getMainEventQueue()
    173216{
    174217    return mMainQueue;
     
    176219
    177220#ifdef RT_OS_DARWIN
     221/**
     222 *  Wait for events and process them (Darwin).
     223 *
     224 *  @returns VINF_SUCCESS or VERR_TIMEOUT.
     225 *
     226 *  @param  cMsTimeout      How long to wait, or RT_INDEFINITE_WAIT.
     227 */
    178228static int
    179 timedWaitForEventsOnDarwin(nsIEventQueue *pQueue, PRInt32 cMsTimeout)
    180 {
    181   OSStatus       orc       = -1;
    182   CFTimeInterval rdTimeout = (double)cMsTimeout / 1000;
    183   orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/);
    184   if (orc == kCFRunLoopRunHandledSource)
    185     orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
    186   if (!orc || orc == kCFRunLoopRunHandledSource)
    187     return VINF_SUCCESS;
    188 
    189   if (orc != kCFRunLoopRunTimedOut)
    190   {
    191       NS_WARNING("Unexpected status code from CFRunLoopRunInMode");
    192   }
    193 
    194   return VERR_TIMEOUT;
    195 }
     229waitForEventsOnDarwin(unsigned cMsTimeout)
     230{
     231    /*
     232     * Wait for the requested time, if we get a hit we do a poll to process
     233     * any other pending messages.
     234     *
     235     * Note! About 1.0e10: According to the sources anything above 3.1556952e+9
     236     *       means indefinite wait and 1.0e10 is what CFRunLoopRun() uses.
     237     */
     238    CFTimeInterval rdTimeout = cMsTimeout == RT_INDEFINITE_WAIT ? 1e10 : (double)cMsTimeout / 1000;
     239    OSStatus orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/);
     240    /** @todo Not entire sure if the poll actually processes more than one message.
     241     *        Feel free to check the sources anyone.  */
     242    if (orc == kCFRunLoopRunHandledSource)
     243        orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
     244    if (    orc == 0
     245        ||  orc == kCFRunLoopRunHandledSource)
     246        return VINF_SUCCESS;
     247    if (    orc == kCFRunLoopRunStopped
     248        ||  orc == kCFRunLoopRunFinished)
     249        return VERR_INTERRUPTED;
     250    AssertMsg(orc == kCFRunLoopRunTimedOut, ("Unexpected status code from CFRunLoopRunInMode: %#x", orc));
     251    return VERR_TIMEOUT;
     252}
     253#elif !defined(RT_OS_WINDOWS)
     254
     255/**
     256 *  Wait for events (Unix).
     257 *
     258 *  @returns VINF_SUCCESS or VERR_TIMEOUT.
     259 *
     260 *  @param  pQueue          The queue to wait on.
     261 *  @param  cMsTimeout      How long to wait, or RT_INDEFINITE_WAIT.
     262 */
     263static int
     264waitForEventsOnUnix(nsIEventQueue *pQueue, unsigned cMsTimeout)
     265{
     266    int     fd = pQueue->GetEventQueueSelectFD();
     267    fd_set  fdsetR;
     268    FD_ZERO(&fdsetR);
     269    FD_SET(fd, &fdsetR);
     270
     271    fd_set  fdsetE = fdsetR;
     272
     273    struct timeval  tv = {0,0};
     274    struct timeval *ptv;
     275    if (cMsTimeout == RT_INDEFINITE_WAIT)
     276        ptv = NULL;
     277    else
     278    {
     279        tv.tv_sec  = cMsTimeout / 1000;
     280        tv.tv_usec = (cMsTimeout % 1000) * 1000;
     281        ptv = &tv;
     282    }
     283
     284    int rc = select(fd + 1, &fdsetR, NULL, &fdsetE, ptv);
     285    if (rc > 0)
     286        rc = VINF_SUCCESS;
     287    else if (rc == 0)
     288        rc = VERR_TIMEOUT;
     289    else if (errno == EINTR)
     290        rc = VERR_INTERRUPTED;
     291    else
     292    {
     293        AssertMsgFailed(("rc=%d errno=%d\n", rc, errno));
     294        rc = VERR_INTERNAL_ERROR_4;
     295    }
     296    return rc;
     297}
     298
    196299#endif
    197300
    198301#ifdef RT_OS_WINDOWS
     302/**
     303 *  Process pending events (Windows).
     304 *  @returns VINF_SUCCESS, VERR_TIMEOUT or VERR_INTERRUPTED.
     305 */
    199306static int
    200 processPendingEvents()
     307processPendingEvents(void)
    201308{
    202309    MSG Msg;
     
    213320    return rc;
    214321}
    215 /** For automatic cleanup,   */
    216 class MyThreadHandle
    217 {
    218 public:
    219   HANDLE mh;
    220 
    221   MyThreadHandle()
    222   {
    223     if (!DuplicateHandle(GetCurrentProcess(),
    224                          GetCurrentThread(),
    225                          GetCurrentProcess(),
    226                          &mh,
    227                          0 /*dwDesiredAccess*/,
    228                          FALSE /*bInheritHandle*/,
    229                          DUPLICATE_SAME_ACCESS))
    230       mh = INVALID_HANDLE_VALUE;
    231   }
    232 
    233   ~MyThreadHandle()
    234   {
    235     CloseHandle(mh);
    236     mh = INVALID_HANDLE_VALUE;
    237   }
    238 };
    239 #else
     322#else  /* !RT_OS_WINDOWS */
     323/**
     324 * Process pending XPCOM events.
     325 * @param pQueue The queue to process events on.
     326 * @returns VINF_SUCCESS or VERR_TIMEOUT.
     327 */
    240328static int
    241 processPendingEvents(nsIEventQueue* pQueue)
    242 {
    243   /** @todo: rethink interruption events, current NULL event approach is bad */
    244   pQueue->ProcessPendingEvents();
    245   return VINF_SUCCESS;
    246 }
    247 #endif
     329processPendingEvents(nsIEventQueue *pQueue)
     330{
     331    /* Check for timeout condition so the caller can be a bit more lazy. */
     332    PRBool fHasEvents = PR_FALSE;
     333    nsresult hr = pQueue->PendingEvents(&fHasEvents);
     334    if (NS_FAILED(hr))
     335        return VERR_INTERNAL_ERROR_2;
     336    if (!fHasEvents)
     337        return VERR_TIMEOUT;
     338
     339    /** @todo: rethink interruption events, current NULL event approach is bad */
     340    pQueue->ProcessPendingEvents();
     341    return VINF_SUCCESS;
     342}
     343#endif /* !RT_OS_WINDOWS */
     344
     345
     346/**
     347 *  Process events pending on this event queue, and wait up to given timeout, if
     348 *  nothing is available.
     349 *
     350 *  Must be called on same thread this event queue was created on.
     351 *
     352 *  @param cMsTimeout The timeout specified as milliseconds.  Use
     353 *                    RT_INDEFINITE_WAIT to wait till an event is posted on the
     354 *                    queue.
     355 *
     356 *  @returns VBox status code
     357 *  @retval VINF_SUCCESS
     358 *  @retval VERR_TIMEOUT
     359 *  @retval VERR_INVALID_CONTEXT
     360 */
    248361int EventQueue::processEventQueue(uint32_t cMsTimeout)
    249362{
    250     int rc = VINF_SUCCESS;
    251     /** @todo: check that current thread == one we were created on */
     363    int rc;
     364    CHECK_THREAD_RET(VERR_INVALID_CONTEXT);
     365
    252366#if defined (VBOX_WITH_XPCOM)
    253     do {
    254       PRBool fHasEvents = PR_FALSE;
    255       nsresult rc;
    256 
    257       rc = mEventQ->PendingEvents(&fHasEvents);
    258       if (NS_FAILED (rc))
    259           return VERR_INTERNAL_ERROR_3;
    260 
    261       if (fHasEvents || cMsTimeout == 0)
    262           break;
    263 
    264       /**
    265        * Unfortunately, WaitForEvent isn't interruptible with Ctrl-C,
    266        * while select() is.
    267        */
    268 
    269       if (cMsTimeout == RT_INDEFINITE_WAIT)
    270       {
    271 #if 0
    272           PLEvent *pEvent = NULL;
    273           int rc1 = mEventQ->WaitForEvent(&pEvent);
    274           if (NS_FAILED(rc1) || pEvent == NULL)
    275           {
    276                 rc = VERR_INTERRUPTED;
     367    /*
     368     * Process pending events, if none are available and we're not in a
     369     * poll call, wait for some to appear.  (We have to be a little bit
     370     * careful after waiting for the events since darwin will process
     371     * them as part of the wait, while the unix case will not.)
     372     *
     373     * Note! Unfortunately, WaitForEvent isn't interruptible with Ctrl-C,
     374     *       while select() is.  So we cannot use it for indefinite waits.
     375     */
     376    rc = processPendingEvents(mEventQ);
     377    if (    rc == VERR_TIMEOUT
     378        &&  cMsTimeout > 0)
     379    {
     380# ifdef RT_OS_DARWIN
     381        /** @todo check how Ctrl-C works on darwin. */
     382        rc = waitForEventsOnDarwin(cMsTimeout);
     383        if (rc == VERR_TIMEOUT)
     384            rc = processPendingEvents(mEventQ);
     385# else
     386        rc = waitForEventsOnUnix(mEventQ, cMsTimeout);
     387        if (    RT_SUCCESS(rc)
     388            ||  rc == VERR_TIMEOUT)
     389            rc = processPendingEvents(mEventQ);
     390# endif
     391    }
     392
     393#else  /* !VBOX_WITH_XPCOM */
     394    if (cMsTimeout == RT_INDEFINITE_WAIT)
     395    {
     396        Event *aEvent = NULL;
     397
     398        BOOL fHasEvent = waitForEvent(&aEvent);
     399        if (fHasEvent)
     400        {
     401            handleEvent(aEvent);
     402            rc = processPendingEvents();
     403            if (rc == VERR_TIMEOUT)
     404                rc = VINF_SUCCESS;
     405        }
     406        else
     407            rc = VERR_INTERRUPTED;
     408    }
     409    else
     410    {
     411        uint64_t const StartTS = RTTimeMilliTS();
     412        for (;;)
     413        {
     414            rc = processPendingEvents();
     415            if (    rc != VERR_TIMEOUT
     416                ||  cMsTimeout == 0)
    277417                break;
    278           }
    279           mEventQ->HandleEvent(pEvent);
    280           break;
    281 #else
    282           /* Pretty close to forever */
    283           cMsTimeout = 0xffff0000;
    284 #endif
    285       }
    286 
    287       /* Bit tricky part - perform timed wait */
    288 #  ifdef RT_OS_DARWIN
    289       rc = timedWaitForEventsOnDarwin(mEventQ, cMsTimeout);
    290 #  else
    291       int fd = mEventQ->GetEventQueueSelectFD();
    292       fd_set fdsetR, fdsetE;
    293       struct timeval tv;
    294 
    295       FD_ZERO(&fdsetR);
    296       FD_SET(fd, &fdsetR);
    297 
    298       fdsetE = fdsetR;
    299       tv.tv_sec = (PRInt64)cMsTimeout / 1000;
    300       tv.tv_usec = ((PRInt64)cMsTimeout % 1000) * 1000;
    301 
    302       int aCode = select(fd + 1, &fdsetR, NULL, &fdsetE, &tv);
    303       if (aCode == 0)
    304         rc = VERR_TIMEOUT;
    305       else if (aCode == EINTR)
    306         rc = VERR_INTERRUPTED;
    307       else if (aCode < 0)
    308         rc = VERR_INTERNAL_ERROR_4;
    309 
    310 #  endif
    311     } while (0);
    312 
    313     rc = processPendingEvents(mEventQ);
    314 #else /* Windows */
    315     do {
    316         int aCode = processPendingEvents();
    317         if (aCode != VERR_TIMEOUT || cMsTimeout == 0)
    318         {
    319             rc = aCode;
    320             break;
     418
     419            uint64_t cMsElapsed = RTTimeMilliTS() - StartTS;
     420            if (cMsElapsed >= cMsTimeout)
     421            {
     422                rc = VERR_TIMEOUT;
     423                break;
     424            }
     425            uint32_t cMsLeft = cMsTimeout - (unsigned)cMsElapsed;
     426            DWORD rcW = MsgWaitForMultipleObjects(1,
     427                                                  &mhThread,
     428                                                  TRUE /*fWaitAll*/,
     429                                                  cMsLeft,
     430                                                  QS_ALLINPUT);
     431            AssertMsgBreakStmt(rcW == WAIT_TIMEOUT || rcW == WAIT_OBJECT_0,
     432                               ("%d\n", rcW),
     433                               rc = VERR_INTERNAL_ERROR_4);
    321434        }
    322 
    323         if (cMsTimeout == RT_INDEFINITE_WAIT)
    324         {
    325             Event* aEvent = NULL;
    326 
    327             BOOL fHasEvent = waitForEvent(&aEvent);
    328             if (fHasEvent)
    329               handleEvent(aEvent);
    330             else
    331               rc = VERR_INTERRUPTED;
    332             break;
    333         }
    334 
    335         /* Perform timed wait */
    336         MyThreadHandle aHandle;
    337 
    338         DWORD aCode2 = MsgWaitForMultipleObjects(1, &aHandle.mh,
    339                                                  TRUE /*fWaitAll*/,
    340                                                  0 /*ms*/,
    341                                                  QS_ALLINPUT);
    342         if (aCode2 == WAIT_TIMEOUT)
    343             rc = VERR_TIMEOUT;
    344         else if (aCode2 == WAIT_OBJECT_0)
    345             rc = VINF_SUCCESS;
    346         else
    347             rc = VERR_INTERNAL_ERROR_4;
    348     } while (0);
    349 
    350     rc = processPendingEvents();
    351 #endif
     435    }
     436#endif /* !VBOX_WITH_XPCOM */
    352437    return rc;
    353438}
    354439
     440/**
     441 *  Interrupt thread waiting on event queue processing.
     442 *
     443 *  Can be called on any thread.
     444 */
    355445int EventQueue::interruptEventQueueProcessing()
    356446{
     
    475565}
    476566
     567/**
     568 *  Get select()'able selector for this event queue.
     569 *  This will return -1 on platforms and queue variants not supporting such
     570 *  functionality.
     571 */
    477572int  EventQueue::getSelectFD()
    478573{
  • trunk/src/VBox/Main/glue/initterm.cpp

    r22847 r23092  
    498498    AssertComRC (rc);
    499499
    500     EventQueue::init();
     500    /*
     501     * Init the main event queue (ASSUMES it cannot fail).
     502     */
     503    if (SUCCEEDED(rc))
     504        EventQueue::init();
    501505
    502506    return rc;
     
    507511    HRESULT rc = S_OK;
    508512
    509     EventQueue::deinit();
     513    EventQueue::uninit();
    510514
    511515#if !defined (VBOX_WITH_XPCOM)
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