VirtualBox

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


Ignore:
Timestamp:
Sep 2, 2009 3:05:57 PM (15 years ago)
Author:
vboxsync
Message:

VBoxCOM,VBoxManage,WebService: Event queue fun.

File:
1 edited

Legend:

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

    r21878 r22722  
    2424
    2525#include "VBox/com/EventQueue.h"
     26
     27#ifdef RT_OS_DARWIN
     28# include <CoreFoundation/CFRunLoop.h>
     29#endif
     30
     31#if defined(VBOX_WITH_XPCOM) && !defined(RT_OS_DARWIN) && !defined(RT_OS_OS2)
     32# define USE_XPCOM_QUEUE
     33#endif
     34
     35#include <iprt/err.h>
     36#include <iprt/time.h>
     37#include <iprt/thread.h>
     38#ifdef USE_XPCOM_QUEUE
     39# include <errno.h>
     40#endif
    2641
    2742namespace com
     
    246261}
    247262
    248 } /* namespace com */
    249 
     263
     264#ifdef VBOX_WITH_XPCOM
     265
     266/** Wrapper around nsIEventQueue::PendingEvents. */
     267DECLINLINE(bool) hasEventQueuePendingEvents(nsIEventQueue *pQueue)
     268{
     269    PRBool fHasEvents = PR_FALSE;
     270    nsresult rc = pQueue->PendingEvents(&fHasEvents);
     271    return NS_SUCCEEDED(rc) && fHasEvents ? true : false;
     272}
     273
     274/** Wrapper around nsIEventQueue::IsQueueNative. */
     275DECLINLINE(bool) isEventQueueNative(nsIEventQueue *pQueue)
     276{
     277    PRBool fIsNative = PR_FALSE;
     278    nsresult rc = pQueue->IsQueueNative(&fIsNative);
     279    return NS_SUCCEEDED(rc) && fIsNative ? true : false;
     280}
     281
     282/** Wrapper around nsIEventQueue::ProcessPendingEvents. */
     283DECLINLINE(void) processPendingEvents(nsIEventQueue *pQueue)
     284{
     285    pQueue->ProcessPendingEvents();
     286}
     287
     288#else
     289
     290/** For automatic cleanup.  */
     291class MyThreadHandle
     292{
     293public:
     294    HANDLE mh;
     295
     296    MyThreadHandle(HANDLE hThread)
     297    {
     298        if (!DuplicateHandle(GetCurrentProcess(), hThread, GetCurrentProcess(),
     299                             &mh, 0 /*dwDesiredAccess*/, FALSE /*bInheritHandle*/,
     300                             DUPLICATE_SAME_ACCESS))
     301            mh = INVALID_HANDLE_VALUE;
     302    }
     303
     304    ~MyThreadHandle()
     305    {
     306        CloseHandle(mh);
     307        mh = INVALID_HANDLE_VALUE;
     308    }
     309};
     310
     311/** COM version of nsIEventQueue::PendingEvents. */
     312DECLINLINE(bool) hasEventQueuePendingEvents(MyThreadHandle &Handle)
     313{
     314    DWORD rc = MsgWaitForMultipleObjects(1, &Handle.mh, TRUE /*fWaitAll*/, 0 /*ms*/, QS_ALLINPUT);
     315    return rc == WAIT_OBJECT_0;
     316}
     317
     318/** COM version of nsIEventQueue::IsQueueNative, the question doesn't make
     319 *  sense and we have to return false for the code below to work. */
     320DECLINLINE(bool) isEventQueueNative(MyThreadHandle const &Handle)
     321{
     322    return false;
     323}
     324
     325/** COM version of nsIEventQueue::ProcessPendingEvents. */
     326static void processPendingEvents(MyThreadHandle const &Handle)
     327{
     328    /*
     329     * Process pending thead messages.
     330     */
     331    MSG Msg;
     332    while (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE))
     333    {
     334        if (Msg.message == WM_QUIT)
     335            return /*VERR_INTERRUPTED*/;
     336        DispatchMessage(&Msg);
     337    }
     338}
     339
     340#endif /* VBOX_WITH_XPCOM */
     341
     342/**
     343 *  Processes events for the current thread.
     344 *
     345 *  @param cMsTimeout       The timeout in milliseconds or RT_INDEFINITE_WAIT.
     346 *  @param pfnExitCheck     Optional callback for checking for some exit condition
     347 *                          while looping.  Note that this may be called
     348 *  @param pvUser           User argument for pfnExitCheck.
     349 *  @param cMsPollInterval  The interval cMsTimeout should be called at. 0 means
     350 *                          never default.
     351 *  @param fReturnOnEvent   If true, return immediately after some events has
     352 *                          been processed. If false, process events until we
     353 *                          time out, pfnExitCheck returns true, interrupted or
     354 *                          the queue receives some kind of quit message.
     355 *
     356 *  @returns VBox status code.
     357 *  @retval VINF_SUCCESS if events were processed.
     358 *  @retval VERR_TIMEOUT if no events before cMsTimeout elapsed.
     359 *  @retval VERR_INTERRUPTED if the wait was interrupted by a signal or other
     360 *          async event.
     361 *  @retval VERR_NOT_FOUND if the thread has no event queue.
     362 *  @retval VERR_CALLBACK_RETURN if the callback indicates return.
     363 *
     364 *  @todo This is just a quick approximation of what we need. Feel free to
     365 *        improve the interface and make it fit better in with the EventQueue
     366 *        class.
     367 */
     368/*static*/ int
     369EventQueue::processThreadEventQueue(uint32_t cMsTimeout, bool (*pfnExitCheck)(void *pvUser) /*= 0*/,
     370                                    void *pvUser /*= 0*/, uint32_t cMsPollInterval /*= 1000*/,
     371                                    bool fReturnOnEvent /*= true*/)
     372{
     373    uint64_t const StartMsTS = RTTimeMilliTS();
     374
     375    /* set default. */
     376    if (cMsPollInterval == 0)
     377        cMsPollInterval = 1000;
     378
     379    /*
     380     * Get the event queue / thread.
     381     */
     382#ifdef VBOX_WITH_XPCOM
     383    nsCOMPtr<nsIEventQueue> q;
     384    nsresult rv = NS_GetCurrentEventQ(getter_AddRefs(q));
     385    if (NS_FAILED(rv))
     386        return VERR_NOT_FOUND;
     387#else
     388    MyThreadHandle q(GetCurrentThread());
     389#endif
     390
     391    /*
     392     * Check for pending before setting up the wait.
     393     */
     394    if (    !hasEventQueuePendingEvents(q)
     395        ||  !fReturnOnEvent)
     396    {
     397        bool fIsNative = isEventQueueNative(q);
     398        if (    fIsNative
     399            ||  cMsTimeout != RT_INDEFINITE_WAIT
     400            ||  pfnExitCheck
     401            ||  !fReturnOnEvent /** @todo !fReturnOnEvent and cMsTimeout RT_INDEFINITE_WAIT can be handled in else */)
     402        {
     403#ifdef USE_XPCOM_QUEUE
     404            int const fdQueue = fIsNative ? q->GetEventQueueSelectFD() : -1;
     405            if (fIsNative && fdQueue == -1)
     406                return VERR_INTERNAL_ERROR_4;
     407#endif
     408            for (;;)
     409            {
     410                /*
     411                 * Check for events.
     412                 */
     413                if (hasEventQueuePendingEvents(q))
     414                {
     415                    if (fReturnOnEvent)
     416                        break;
     417                    processPendingEvents(q);
     418                }
     419
     420                /*
     421                 * Check the user exit.
     422                 */
     423                if (   pfnExitCheck
     424                    && pfnExitCheck(pvUser))
     425                    return VERR_CALLBACK_RETURN;
     426
     427                /*
     428                 * Figure out how much we have left to wait and if we've timed out already.
     429                 */
     430                uint32_t cMsLeft;
     431                if (cMsTimeout == RT_INDEFINITE_WAIT)
     432                    cMsLeft = RT_INDEFINITE_WAIT;
     433                else
     434                {
     435                    uint64_t cMsElapsed = RTTimeMilliTS() - StartMsTS;
     436                    if (cMsElapsed >= cMsTimeout)
     437                        break; /* timeout */
     438                    cMsLeft = cMsTimeout - (uint32_t)cMsElapsed;
     439                }
     440
     441                /*
     442                 * Wait in a queue & platform specific manner.
     443                 */
     444#ifdef VBOX_WITH_XPCOM
     445                if (!fIsNative)
     446                    RTThreadSleep(250 /*ms*/);
     447                else
     448                {
     449# ifdef USE_XPCOM_QUEUE
     450                    fd_set fdset;
     451                    FD_ZERO(&fdset);
     452                    FD_SET(fdQueue, &fdset);
     453                    struct timeval tv;
     454                    if (    cMsLeft == RT_INDEFINITE_WAIT
     455                        ||  cMsLeft >= cMsPollInterval)
     456                    {
     457                        tv.tv_sec = cMsPollInterval / 1000;
     458                        tv.tv_usec = (cMsPollInterval % 1000) * 1000;
     459                    }
     460                    else
     461                    {
     462                        tv.tv_sec = cMsLeft / 1000;
     463                        tv.tv_usec = (cMsLeft % 1000) * 1000;
     464                    }
     465                    int prc = select(fdQueue + 1, &fdset, NULL, NULL, &tv);
     466                    if (prc == -1)
     467                        return RTErrConvertFromErrno(errno);
     468
     469# elif defined(RT_OS_DARWIN)
     470                    CFTimeInterval rdTimeout = (double)RT_MIN(cMsLeft, cMsPollInterval) / 1000;
     471                    OSStatus orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/);
     472                    if (orc == kCFRunLoopRunHandledSource)
     473                        orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
     474                    if (   orc != 0
     475                        && orc != kCFRunLoopRunHandledSource
     476                        && orc != kCFRunLoopRunTimedOut)
     477                        return orc == kCFRunLoopRunStopped || orc == kCFRunLoopRunFinished
     478                             ? VERR_INTERRUPTED
     479                             : RTErrConvertFromDarwin(orc);
     480# else
     481#  warning "PORTME:"
     482                    RTThreadSleep(250);
     483# endif
     484                }
     485
     486#else  /* !VBOX_WITH_XPCOM */
     487                DWORD rc = MsgWaitForMultipleObjects(1, &q.mh, TRUE /*fWaitAll*/, RT_MIN(cMsLeft, cMsPollInterval), QS_ALLINPUT);
     488                if (rc == WAIT_OBJECT_0)
     489                {
     490                    if (fReturnOnEvent)
     491                        break;
     492                    processPendingEvents(q);
     493                }
     494                else if (rc == WAIT_FAILED)
     495                    return RTErrConvertFromWin32(GetLastError());
     496                else if (rc != WAIT_TIMEOUT)
     497                    return VERR_INTERNAL_ERROR_4;
     498#endif /* !VBOX_WITH_XPCOM */
     499            } /* for (;;) */
     500        }
     501        else
     502        {
     503            /*
     504             * Indefinite wait without any complications.
     505             */
     506#ifdef VBOX_WITH_XPCOM
     507            PLEvent *pEvent = NULL;
     508            rv = q->WaitForEvent(&pEvent);
     509            if (NS_FAILED(rv))
     510                return VERR_INTERRUPTED;
     511            q->HandleEvent(pEvent);
     512#else
     513            DWORD rc = MsgWaitForMultipleObjects(1, &q.mh, TRUE /*fWaitAll*/, INFINITE, QS_ALLINPUT);
     514            if (rc != WAIT_OBJECT_0)
     515            {
     516                if (rc == WAIT_FAILED)
     517                    return RTErrConvertFromWin32(GetLastError());
     518                return VERR_INTERNAL_ERROR_3;
     519            }
     520#endif
     521        }
     522    }
     523
     524    /*
     525     * We have/had events in the queue. Process pending events and
     526     * return successfully.
     527     */
     528    processPendingEvents(q);
     529
     530    return VINF_SUCCESS;
     531}
     532
     533}
     534/* namespace com */
     535
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