VirtualBox

Changeset 31579 in vbox for trunk/src/VBox/Main/glue


Ignore:
Timestamp:
Aug 11, 2010 5:21:27 PM (15 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
64686
Message:

EventQueue: Fix losing messages, use the right queue type on XPCOM (the fact that event handling in VBoxSVC worked was mainly luck), big code cleanup. VBoxHeadless and VirtualBoxImpl now use the only remaining event processing style. Eliminated redundant custom StateChange event in VBoxHeadless.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/glue/EventQueue.cpp

    r31371 r31579  
    88
    99/*
    10  * Copyright (C) 2006-2007 Oracle Corporation
     10 * Copyright (C) 2006-2010 Oracle Corporation
    1111 *
    1212 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    4242////////////////////////////////////////////////////////////////////////////////
    4343
    44 #if defined (RT_OS_WINDOWS)
     44#ifndef VBOX_WITH_XPCOM
    4545
    4646#define CHECK_THREAD_RET(ret) \
    4747    do { \
    48         AssertMsg (GetCurrentThreadId() == mThreadId, ("Must be on event queue thread!")); \
     48        AssertMsg(GetCurrentThreadId() == mThreadId, ("Must be on event queue thread!")); \
    4949        if (GetCurrentThreadId() != mThreadId) \
    5050            return ret; \
    5151    } while (0)
    5252
    53 #else // !defined (RT_OS_WINDOWS)
     53#else // VBOX_WITH_XPCOM
    5454
    5555#define CHECK_THREAD_RET(ret) \
     
    5858            return ret; \
    5959        BOOL isOnCurrentThread = FALSE; \
    60         mEventQ->IsOnCurrentThread (&isOnCurrentThread); \
    61         AssertMsg (isOnCurrentThread, ("Must be on event queue thread!")); \
     60        mEventQ->IsOnCurrentThread(&isOnCurrentThread); \
     61        AssertMsg(isOnCurrentThread, ("Must be on event queue thread!")); \
    6262        if (!isOnCurrentThread) \
    6363            return ret; \
    6464    } while (0)
    6565
    66 #endif // !defined (RT_OS_WINDOWS)
     66#endif // VBOX_WITH_XPCOM
    6767
    6868EventQueue *EventQueue::mMainQueue = NULL;
     69
     70#ifdef VBOX_WITH_XPCOM
     71struct MyPLEvent : public PLEvent
     72{
     73    MyPLEvent(Event *e) : event(e) {}
     74    Event *event;
     75};
     76
     77/* static */
     78void *PR_CALLBACK com::EventQueue::plEventHandler(PLEvent *self)
     79{
     80    Event *ev = ((MyPLEvent *)self)->event;
     81    if (ev)
     82        ev->handler();
     83    else
     84    {
     85        EventQueue *eq = (EventQueue *)self->owner;
     86        Assert(eq);
     87        eq->mInterrupted = true;
     88    }
     89    return NULL;
     90}
     91
     92/* static */
     93void PR_CALLBACK com::EventQueue::plEventDestructor(PLEvent *self)
     94{
     95    Event *ev = ((MyPLEvent *)self)->event;
     96    if (ev)
     97        delete ev;
     98    delete self;
     99}
     100
     101#endif // VBOX_WITH_XPCOM
    69102
    70103/**
     
    77110EventQueue::EventQueue()
    78111{
    79 #if defined (RT_OS_WINDOWS)
     112#ifndef VBOX_WITH_XPCOM
    80113
    81114    mThreadId = GetCurrentThreadId();
    82115    // force the system to create the message queue for the current thread
    83116    MSG msg;
    84     PeekMessage (&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
    85 
    86     if (!DuplicateHandle (GetCurrentProcess(),
    87                           GetCurrentThread(),
    88                           GetCurrentProcess(),
    89                           &mhThread,
    90                           0 /*dwDesiredAccess*/,
    91                           FALSE /*bInheritHandle*/,
    92                           DUPLICATE_SAME_ACCESS))
     117    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
     118
     119    if (!DuplicateHandle(GetCurrentProcess(),
     120                         GetCurrentThread(),
     121                         GetCurrentProcess(),
     122                         &mhThread,
     123                         0 /*dwDesiredAccess*/,
     124                         FALSE /*bInheritHandle*/,
     125                         DUPLICATE_SAME_ACCESS))
    93126      mhThread = INVALID_HANDLE_VALUE;
    94127
    95 #else
     128#else // VBOX_WITH_XPCOM
    96129
    97130    mEQCreated = FALSE;
    98 
    99     mLastEvent = NULL;
    100     mGotEvent = FALSE;
     131    mInterrupted = FALSE;
    101132
    102133    // Here we reference the global nsIEventQueueService instance and hold it
     
    112143    // NULL to the event thread (because it stopped accepting events).
    113144
    114     nsresult rc = NS_GetEventQueueService (getter_AddRefs (mEventQService));
     145    nsresult rc = NS_GetEventQueueService(getter_AddRefs(mEventQService));
    115146
    116147    if (NS_SUCCEEDED(rc))
    117148    {
    118         rc = mEventQService->GetThreadEventQueue (NS_CURRENT_THREAD,
    119                                                   getter_AddRefs (mEventQ));
     149        rc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD,
     150                                                 getter_AddRefs(mEventQ));
    120151        if (rc == NS_ERROR_NOT_AVAILABLE)
    121152        {
    122             rc = mEventQService->CreateMonitoredThreadEventQueue();
     153            rc = mEventQService->CreateThreadEventQueue();
    123154            if (NS_SUCCEEDED(rc))
    124155            {
    125156                mEQCreated = TRUE;
    126                 rc = mEventQService->GetThreadEventQueue (NS_CURRENT_THREAD,
    127                                                           getter_AddRefs (mEventQ));
     157                rc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD,
     158                                                         getter_AddRefs(mEventQ));
    128159            }
    129160        }
    130161    }
    131     AssertComRC (rc);
    132 
    133 #endif
     162    AssertComRC(rc);
     163
     164#endif // VBOX_WITH_XPCOM
    134165}
    135166
    136167EventQueue::~EventQueue()
    137168{
    138 #if defined (RT_OS_WINDOWS)
     169#ifndef VBOX_WITH_XPCOM
    139170    if (mhThread != INVALID_HANDLE_VALUE)
    140171    {
    141         CloseHandle (mhThread);
     172        CloseHandle(mhThread);
    142173        mhThread = INVALID_HANDLE_VALUE;
    143174    }
    144 #else
     175#else // VBOX_WITH_XPCOM
    145176    // process all pending events before destruction
    146177    if (mEventQ)
     
    155186        mEventQService = nsnull;
    156187    }
    157 #endif
     188#endif // VBOX_WITH_XPCOM
    158189}
    159190
     
    175206    mMainQueue = new EventQueue();
    176207
    177 #if defined (VBOX_WITH_XPCOM)
     208#ifdef VBOX_WITH_XPCOM
    178209    /* Check that it actually is the main event queue, i.e. that
    179210       we're called on the right thread. */
     
    187218    rv = mMainQueue->mEventQ->IsQueueNative(&fIsNative);
    188219    Assert(NS_SUCCEEDED(rv) && fIsNative);
    189 #endif
     220#endif // VBOX_WITH_XPCOM
    190221
    191222    return VINF_SUCCESS;
     
    200231{
    201232    Assert(mMainQueue);
     233    /* Must process all events to make sure that no NULL event is left
     234     * after this point. It would need to modify the state of mMainQueue. */
     235    mMainQueue->processEventQueue(0);
    202236    delete mMainQueue;
    203237    mMainQueue = NULL;
     
    216250}
    217251
    218 #ifdef RT_OS_DARWIN
     252#ifdef VBOX_WITH_XPCOM
     253# ifdef RT_OS_DARWIN
    219254/**
    220255 *  Wait for events and process them (Darwin).
     
    249284    return VERR_TIMEOUT;
    250285}
    251 #elif !defined(RT_OS_WINDOWS)
    252 
    253 /**
    254  *  Wait for events (Unix).
     286# else // !RT_OS_DARWIN
     287
     288/**
     289 *  Wait for events (generic XPCOM).
    255290 *
    256291 *  @returns VINF_SUCCESS or VERR_TIMEOUT.
     
    260295 */
    261296static
    262 int waitForEventsOnUnix(nsIEventQueue *pQueue, unsigned cMsTimeout)
     297int waitForEventsOnXPCOM(nsIEventQueue *pQueue, unsigned cMsTimeout)
    263298{
    264299    int     fd = pQueue->GetEventQueueSelectFD();
     
    295330}
    296331
    297 #endif
    298 
    299 #ifdef RT_OS_WINDOWS
     332# endif // !RT_OS_DARWIN
     333#endif // VBOX_WITH_XPCOM
     334
     335#ifndef VBOX_WITH_XPCOM
    300336/**
    301337 *  Process pending events (Windows).
     
    318354    return rc;
    319355}
    320 #else  /* !RT_OS_WINDOWS */
     356#else // VBOX_WITH_XPCOM
    321357/**
    322358 * Process pending XPCOM events.
     
    335371        return VERR_TIMEOUT;
    336372
    337     /** @todo: rethink interruption events, current NULL event approach is bad */
    338373    pQueue->ProcessPendingEvents();
    339374    return VINF_SUCCESS;
    340375}
    341 #endif /* !RT_OS_WINDOWS */
     376#endif // VBOX_WITH_XPCOM
    342377
    343378
     
    362397    CHECK_THREAD_RET(VERR_INVALID_CONTEXT);
    363398
    364 #if defined (VBOX_WITH_XPCOM)
     399#ifdef VBOX_WITH_XPCOM
    365400    /*
    366401     * Process pending events, if none are available and we're not in a
    367402     * poll call, wait for some to appear.  (We have to be a little bit
    368      * careful after waiting for the events since darwin will process
    369      * them as part of the wait, while the unix case will not.)
     403     * careful after waiting for the events since Darwin will process
     404     * them as part of the wait, while the XPCOM case will not.)
    370405     *
    371406     * Note! Unfortunately, WaitForEvent isn't interruptible with Ctrl-C,
     
    377412    {
    378413# ifdef RT_OS_DARWIN
    379         /** @todo check how Ctrl-C works on darwin. */
     414        /** @todo check how Ctrl-C works on Darwin. */
    380415        rc = waitForEventsOnDarwin(cMsTimeout);
    381416        if (rc == VERR_TIMEOUT)
    382417            rc = processPendingEvents(mEventQ);
    383 # else
    384         rc = waitForEventsOnUnix(mEventQ, cMsTimeout);
     418# else // !RT_OS_DARWIN
     419        rc = waitForEventsOnXPCOM(mEventQ, cMsTimeout);
    385420        if (    RT_SUCCESS(rc)
    386421            ||  rc == VERR_TIMEOUT)
    387422            rc = processPendingEvents(mEventQ);
    388 # endif
    389     }
    390 
    391 #else  /* !VBOX_WITH_XPCOM */
     423# endif // !RT_OS_DARWIN
     424    }
     425    if (RT_SUCCESS(rc) && mInterrupted)
     426    {
     427        mInterrupted = false;
     428        rc = VERR_INTERRUPTED;
     429    }
     430
     431#else // !VBOX_WITH_XPCOM
    392432    if (cMsTimeout == RT_INDEFINITE_WAIT)
    393433    {
    394         Event *aEvent = NULL;
    395 
    396         BOOL fHasEvent = waitForEvent(&aEvent);
    397         if (fHasEvent)
     434        BOOL bRet;
     435        MSG Msg;
     436        int rc = VINF_SUCCESS;
     437        while ((bRet = GetMessage(&Msg, NULL /*hWnd*/, WM_USER, WM_USER)))
    398438        {
    399             handleEvent(aEvent);
    400             rc = processPendingEvents();
    401             if (rc == VERR_TIMEOUT)
    402                 rc = VINF_SUCCESS;
     439            if (bRet != -1)
     440                DispatchMessage(&Msg);
    403441        }
    404         else
     442        if (bRet == 0)
    405443            rc = VERR_INTERRUPTED;
    406444    }
     
    422460        }
    423461    }
    424 #endif /* !VBOX_WITH_XPCOM */
     462#endif // !VBOX_WITH_XPCOM
    425463    return rc;
    426464}
     
    433471int EventQueue::interruptEventQueueProcessing()
    434472{
    435     /** @todo: rethink me! */
     473    /* Send a NULL event. This gets us out of the event loop on XPCOM, and
     474     * doesn't hurt on Windows. It is the responsibility of the caller to
     475     * take care of not running the loop again in a way which will hang. */
    436476    postEvent(NULL);
    437477    return VINF_SUCCESS;
     
    446486BOOL EventQueue::postEvent(Event *event)
    447487{
    448 #if defined (RT_OS_WINDOWS)
    449 
    450     return PostThreadMessage (mThreadId, WM_USER, (WPARAM) event, NULL);
    451 
    452 #else
     488#ifndef VBOX_WITH_XPCOM
     489
     490    return PostThreadMessage(mThreadId, WM_USER, (WPARAM)event, NULL);
     491
     492#else // VBOX_WITH_XPCOM
    453493
    454494    if (!mEventQ)
    455495        return FALSE;
    456496
    457     MyPLEvent *ev = new MyPLEvent (event);
    458     mEventQ->InitEvent (ev, this, plEventHandler, plEventDestructor);
    459     HRESULT rc = mEventQ->PostEvent (ev);
     497    MyPLEvent *ev = new MyPLEvent(event);
     498    mEventQ->InitEvent(ev, this, com::EventQueue::plEventHandler,
     499                       com::EventQueue::plEventDestructor);
     500    HRESULT rc = mEventQ->PostEvent(ev);
    460501    return NS_SUCCEEDED(rc);
    461502
    462 #endif
    463 }
    464 
    465 /**
    466  *  Waits for a single event.
    467  *  This method must be called on the same thread where this event queue
    468  *  is created.
    469  *
    470  *  After this method returns TRUE and non-NULL event, the caller should call
    471  *  #handleEvent() in order to process the returned event (otherwise the event
    472  *  is just removed from the queue, but not processed).
    473  *
    474  *  There is a special case when the returned event is NULL (and the method
    475  *  returns TRUE), meaning that this event queue must finish its execution
    476  *  (i.e., quit the event loop),
    477  *
    478  *  @param event    next event removed from the queue
    479  *  @return         TRUE if successful and false otherwise
    480  */
    481 BOOL EventQueue::waitForEvent(Event **event)
    482 {
    483     Assert(event);
    484     if (!event)
    485         return FALSE;
    486 
    487     *event = NULL;
    488 
    489     CHECK_THREAD_RET (FALSE);
    490 
    491 #if defined (RT_OS_WINDOWS)
    492 
    493     MSG msg;
    494     BOOL rc = GetMessage (&msg, NULL, WM_USER, WM_USER);
    495     // check for error
    496     if (rc == -1)
    497         return FALSE;
    498     // check for WM_QUIT
    499     if (!rc)
    500         return TRUE;
    501 
    502     // retrieve our event
    503     *event = (Event *) msg.wParam;
    504 
    505 #else
    506 
    507     PLEvent *ev = NULL;
    508     HRESULT rc;
    509 
    510     mGotEvent = FALSE;
    511 
    512     do
    513     {
    514         rc = mEventQ->WaitForEvent (&ev);
    515         // check for error
    516         if (FAILED(rc))
    517             return FALSE;
    518         // check for EINTR signal
    519         if (!ev)
    520             return TRUE;
    521 
    522         // run PLEvent handler. This will just set mLastEvent if it is an
    523         // MyPLEvent instance, and then delete ev.
    524         mEventQ->HandleEvent (ev);
    525     }
    526     while (!mGotEvent);
    527 
    528     // retrieve our event
    529     *event = mLastEvent;
    530 
    531 #endif
    532 
    533     return TRUE;
    534 }
    535 
    536 /**
    537  *  Handles the given event and |delete|s it.
    538  *  This method must be called on the same thread where this event queue
    539  *  is created.
    540  */
    541 BOOL EventQueue::handleEvent(Event *event)
    542 {
    543     Assert(event);
    544     if (!event)
    545         return FALSE;
    546 
    547     CHECK_THREAD_RET (FALSE);
    548 
    549     event->handler();
    550     delete event;
    551 
    552     return TRUE;
    553 }
     503#endif // VBOX_WITH_XPCOM
     504}
     505
    554506
    555507/**
     
    566518#endif
    567519}
     520
    568521}
    569522/* namespace com */
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette