VirtualBox

Changeset 22847 in vbox


Ignore:
Timestamp:
Sep 8, 2009 8:37:54 PM (15 years ago)
Author:
vboxsync
Message:

Python, COM glue: event processing API

Location:
trunk
Files:
4 edited

Legend:

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

    r22722 r22847  
    9191    BOOL handleEvent (Event *event);
    9292    static int processThreadEventQueue(uint32_t cMsTimeout, bool (*pfnExitCheck)(void *pvUser) = 0,
    93                                        void *pvUser = 0, uint32_t cMsPollInterval = 1000,
    94                                        bool fReturnOnEvent = true);
     93                                     void *pvUser = 0, uint32_t cMsPollInterval = 1000,
     94                                     bool fReturnOnEvent = true);
     95    /**
     96     * Process events pending on this event queue, and wait
     97     * up to given timeout, if nothing is available.
     98     * Must be called on same thread this event queue was created on.
     99     */
     100    int processEventQueue(uint32_t cMsTimeout);
     101    /**
     102     * Interrupt thread waiting on event queue processing.
     103     * Can be called on any thread.
     104     */
     105    int interruptEventQueueProcessing();
     106    /**
     107     * Initialize/deinitialize event queues.
     108     */
     109    static int init();
     110    static int deinit();
     111    /**
     112     * Get main event queue instance.
     113     */
     114    static EventQueue* getMainEventQueue();
    95115
    96116private:
     117    static EventQueue* mMainQueue;
    97118
    98119#if !defined (VBOX_WITH_XPCOM)
  • trunk/src/VBox/Main/glue/EventQueue.cpp

    r22722 r22847  
    6969
    7070#endif // !defined (RT_OS_WINDOWS)
     71
     72EventQueue* EventQueue::mMainQueue = NULL;
    7173
    7274/**
     
    146148}
    147149
     150/* static */ int EventQueue::init()
     151{
     152    mMainQueue = new EventQueue();
     153#if defined (VBOX_WITH_XPCOM)
     154    nsCOMPtr<nsIEventQueue> q;
     155    nsresult rv = NS_GetMainEventQ(getter_AddRefs(q));
     156    Assert(NS_SUCCEEDED(rv));
     157    Assert(q == mMainQueue->mEventQ);
     158    PRBool fIsNative = PR_FALSE;
     159    rv = mMainQueue->mEventQ->IsQueueNative(&fIsNative);
     160    Assert(NS_SUCCEEDED(rv) && fIsNative);
     161#endif
     162    return VINF_SUCCESS;
     163}
     164
     165/* static */ int EventQueue::deinit()
     166{
     167    delete mMainQueue;
     168    mMainQueue = NULL;
     169    return VINF_SUCCESS;
     170}
     171
     172/* static */ EventQueue* EventQueue::getMainEventQueue()
     173{
     174    return mMainQueue;
     175}
     176
     177#ifdef RT_OS_DARWIN
     178static int
     179timedWaitForEventsOnDarwin(nsIEventQueue *pQueue, PRInt32 cMsTimeout)
     180{
     181  // This deals with the common case where the caller is the main
     182  // application thread and the queue is a native one.
     183  OSStatus       orc       = -1;
     184  CFTimeInterval rdTimeout = (double)cMsTimeout / 1000;
     185  orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/);
     186  if (orc == kCFRunLoopRunHandledSource)
     187    orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
     188  if (!orc || orc == kCFRunLoopRunHandledSource)
     189    return VINF_SUCCESS;
     190
     191  if (orc != kCFRunLoopRunTimedOut)
     192  {
     193      NS_WARNING("Unexpected status code from CFRunLoopRunInMode");
     194  }
     195
     196  return VERR_TIMEOUT;
     197}
     198#endif
     199
     200#ifdef RT_OS_WINDOWS
     201static int
     202processPendingEventsOnWindows()
     203{
     204    MSG Msg;
     205    int rc = VERR_TIMEOUT;
     206    while (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE))
     207    {
     208        if (Msg.message == WM_QUIT)
     209            rc = VERR_INTERRUPTED;
     210        DispatchMessage(&Msg);
     211        if (rc == VERR_INTERRUPTED)
     212            break;
     213        rc = VINF_SUCCESS;
     214    }   
     215    return rc;
     216}
     217/** For automatic cleanup,   */
     218class MyThreadHandle
     219{
     220public:
     221  HANDLE mh;
     222 
     223  MyThreadHandle()
     224  {
     225    if (!DuplicateHandle(GetCurrentProcess(),
     226                         GetCurrentThread(),
     227                         GetCurrentProcess(),
     228                         &mh,
     229                         0 /*dwDesiredAccess*/,
     230                         FALSE /*bInheritHandle*/,
     231                         DUPLICATE_SAME_ACCESS))
     232      mh = INVALID_HANDLE_VALUE;
     233  }
     234
     235  ~MyThreadHandle()
     236  {
     237    CloseHandle(mh);
     238    mh = INVALID_HANDLE_VALUE;
     239  }
     240};
     241#endif
     242#include <stdio.h>
     243int EventQueue::processEventQueue(uint32_t cMsTimeout)
     244{
     245    int rc = VINF_SUCCESS;
     246#if defined (VBOX_WITH_XPCOM)
     247    do {
     248      PRBool fHasEvents = PR_FALSE;
     249      nsresult rc;
     250     
     251      rc = mEventQ->PendingEvents(&fHasEvents);
     252      if (NS_FAILED (rc))
     253          return VERR_INTERNAL_ERROR_3;
     254
     255      if (fHasEvents || cMsTimeout == 0)
     256          break;
     257
     258      /**
     259       * Unfortunately, WaitForEvent isn't interruptible with Ctrl-C,
     260       * while select() is.
     261       */
     262
     263      if (cMsTimeout == RT_INDEFINITE_WAIT)
     264      {
     265#if 0
     266          PLEvent *pEvent = NULL;
     267          int rc1 = mEventQ->WaitForEvent(&pEvent);
     268          if (NS_FAILED(rc1) || pEvent == NULL)
     269          {
     270                rc = VERR_INTERRUPTED;
     271                break;
     272          }
     273          mEventQ->HandleEvent(pEvent);
     274          break;
     275#else
     276          /* Pretty close to forever */
     277          cMsTimeout = 0xffff0000;
     278#endif
     279      }
     280     
     281      /* Bit tricky part - perform timed wait */
     282#  ifdef RT_OS_DARWIN
     283      rc = timedWaitForEventsOnDarwin(mEventQ, cMsTimeout);
     284#  else
     285      int fd = mEventQ->GetEventQueueSelectFD();
     286      fd_set fdsetR, fdsetE;
     287      struct timeval tv;
     288     
     289      FD_ZERO(&fdsetR);
     290      FD_SET(fd, &fdsetR);
     291     
     292      fdsetE = fdsetR;
     293      tv.tv_sec = (PRInt64)cMsTimeout / 1000;
     294      tv.tv_usec = ((PRInt64)cMsTimeout % 1000) * 1000;
     295
     296      int aCode = select(fd + 1, &fdsetR, NULL, &fdsetE, &tv);
     297      if (aCode == 0)
     298        rc = VERR_TIMEOUT;
     299      else if (aCode == EINTR)
     300        rc = VERR_INTERRUPTED;
     301      else if (aCode < 0)
     302        rc = VERR_INTERNAL_ERROR_4;
     303     
     304#  endif     
     305    } while (0);
     306
     307    mEventQ->ProcessPendingEvents();
     308#else /* Windows */
     309    do {
     310        int aCode = processPendingEventsOnWindows();
     311        if (aCode != VERR_TIMEOUT || cMsTimeout == 0)
     312        {
     313            rc = aCode;
     314            break;
     315        }
     316       
     317        if (cMsTimeout == RT_INDEFINITE_WAIT)
     318        {
     319            Event* aEvent = NULL;
     320         
     321            fHasEvent = waitForEvent(&aEvent);
     322            if (fHasEvent)
     323              handleEvent(aEvent);
     324            else
     325              rc = VERR_INTERRUPTED;
     326            break;
     327        }
     328
     329        /* Perform timed wait */
     330        MyThreadHandle aHandle;
     331
     332        DWORD aCode = MsgWaitForMultipleObjects(1, &aHandle.mh,
     333                                             TRUE /*fWaitAll*/,
     334                                             0 /*ms*/,
     335                                             QS_ALLINPUT);
     336        if (aCode == WAIT_TIMEOUT)
     337            rc = VERR_TIMEOUT;
     338        else if (aCode == WAIT_OBJECT_0)
     339            rc = VINF_SUCCESS;
     340        else
     341            rc = VERR_INTERNAL_ERROR_4;
     342    } while (0);
     343
     344    processPendingEventsOnWindows();
     345#endif
     346    return rc;
     347
     348}
     349
     350int EventQueue::interruptEventQueueProcessing()
     351{
     352    postEvent(NULL);
     353    return VINF_SUCCESS;
     354}
     355
    148356/**
    149357 *  Posts an event to this event loop asynchronously.
     
    287495
    288496#else
    289 
    290 /** For automatic cleanup.  */
    291 class MyThreadHandle
    292 {
    293 public:
    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 };
    310497
    311498/** COM version of nsIEventQueue::PendingEvents. */
     
    386573        return VERR_NOT_FOUND;
    387574#else
    388     MyThreadHandle q(GetCurrentThread());
     575    MyThreadHandle q;
    389576#endif
    390577
     
    530717    return VINF_SUCCESS;
    531718}
    532 
    533719}
    534720/* namespace com */
  • trunk/src/VBox/Main/glue/initterm.cpp

    r21878 r22847  
    5353#include "VBox/com/com.h"
    5454#include "VBox/com/assert.h"
     55#include "VBox/com/EventQueue.h"
    5556
    5657#include "../include/Logging.h"
     
    497498    AssertComRC (rc);
    498499
     500    EventQueue::init();
     501
    499502    return rc;
    500503}
     
    503506{
    504507    HRESULT rc = S_OK;
     508
     509    EventQueue::deinit();
    505510
    506511#if !defined (VBOX_WITH_XPCOM)
  • trunk/src/libs/xpcom18a4/python/src/module/_xpcom.cpp

    r22829 r22847  
    493493
    494494#ifdef VBOX
    495 //#define USE_EVENTQUEUE  1
    496 
    497 # ifdef USE_EVENTQUEUE
     495
    498496#  include <VBox/com/EventQueue.h>
    499497#  include <iprt/err.h>
    500 # else
    501 #  include <iprt/cdefs.h>
    502 # endif
    503 
    504 static nsIEventQueue* g_mainEventQ = nsnull;
    505 
    506 # ifndef USE_EVENTQUEUE /** @todo Make USE_EVENTQUEUE default. */
    507 // Wrapper that checks if the queue has pending events.
    508 DECLINLINE(bool)
    509 hasEventQueuePendingEvents(nsIEventQueue *pQueue)
    510 {
    511         PRBool fHasEvents = PR_FALSE;
    512         nsresult rc = pQueue->PendingEvents(&fHasEvents);
    513         return NS_SUCCEEDED(rc) && fHasEvents ? true : false;
    514 }
    515 
    516 // Wrapper that checks if the queue is native or not.
    517 DECLINLINE(bool)
    518 isEventQueueNative(nsIEventQueue *pQueue)
    519 {
    520         PRBool fIsNative = PR_FALSE;
    521         nsresult rc = pQueue->IsQueueNative(&fIsNative);
    522         return NS_SUCCEEDED(rc) && fIsNative ? true : false;
    523 }
    524 
    525 # ifdef RT_OS_DARWIN
    526 #  include <iprt/time.h>
    527 #  include <iprt/thread.h>
    528 #  include <iprt/err.h>
    529 #  include <CoreFoundation/CFRunLoop.h>
    530 #  if MAC_OS_X_VERSION_MAX_ALLOWED == 1040 /* ASSUMES this means we're using the 10.4 SDK. */
    531 #   include <CarbonEvents.h>
    532 #  endif
    533 
    534 // Common fallback for waiting (and maybe processing) events. Caller process
    535 // any pending events when 0 is returned.
    536 static nsresult
    537 waitForEventsCommonFallback(nsIEventQueue *pQueue, PRInt32 cMsTimeout)
    538 {
    539         if (cMsTimeout < 0) {
    540                 // WaitForEvent probably does the trick here.
    541                 PLEvent *pEvent = NULL;
    542                 nsresult rc = -1;
    543                 Py_BEGIN_ALLOW_THREADS;
    544                 rc = pQueue->WaitForEvent(&pEvent);
    545                 Py_END_ALLOW_THREADS;
    546                 if (NS_SUCCEEDED(rc)) {
    547                         pQueue->HandleEvent(pEvent);
    548                         return 0;
    549                 }
    550         } else {
    551                 // Poll until time out or event is encountered. We have
    552                 // no other choice here.
    553                 uint64_t const StartMillTS = RTTimeMilliTS();
    554                 for (;;)
    555                 {
    556                         // Any pending events?
    557                         if (hasEventQueuePendingEvents(pQueue))
    558                                 return 0;
    559 
    560                         // Work any native per-thread event loops for good measure.
    561 #  ifdef RT_OS_DARWIN
    562                         CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
    563 #  endif
    564 
    565                         // Any pending events?
    566                         if (hasEventQueuePendingEvents(pQueue))
    567                                 return 0;
    568 
    569                         // Timed out?
    570                         uint64_t cMsElapsed = RTTimeMilliTS() - StartMillTS;
    571                         if (cMsElapsed > (uint32_t)cMsTimeout)
    572                                 break;
    573 
    574                         // Sleep a bit; return 0 if interrupt (see the select code edition).
    575                         uint32_t cMsLeft = (uint32_t)cMsTimeout - (uint32_t)cMsElapsed;
    576                         int rc = RTThreadSleep(RT_MIN(cMsLeft, 250));
    577                         if (rc == VERR_INTERRUPTED)
    578                                 return 0;
    579                 }
    580         }
    581 
    582         return 1;
    583 }
    584 
    585 
    586 // Takes care of the waiting on darwin. Caller process any pending events when
    587 // 0 is returned.
    588 static nsresult
    589 waitForEventsOnDarwin(nsIEventQueue *pQueue, PRInt32 cMsTimeout)
    590 {
    591         // This deals with the common case where the caller is the main
    592         // application thread and the queue is a native one.
    593         if (    isEventQueueNative(pQueue)
    594 #  if MAC_OS_X_VERSION_MAX_ALLOWED == 1040 /* ASSUMES this means we're using the 10.4 SDK. */
    595             &&  GetCurrentEventLoop() == GetMainEventLoop()
    596 #  else
    597             &&  CFRunLoopGetMain() == CFRunLoopGetCurrent()
    598 #  endif
    599         ) {
    600                 OSStatus       orc       = -1;
    601                 CFTimeInterval rdTimeout = cMsTimeout < 0
    602                                          ? 1.0e10
    603                                          : (double)cMsTimeout / 1000;
    604                 Py_BEGIN_ALLOW_THREADS;
    605                 orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/);
    606                 if (orc == kCFRunLoopRunHandledSource)
    607                     orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
    608                 Py_END_ALLOW_THREADS;
    609                 if (!orc || orc == kCFRunLoopRunHandledSource)
    610                         return 0;
    611 
    612                 if (orc != kCFRunLoopRunTimedOut) {
    613                         NS_WARNING("Unexpected status code from CFRunLoopRunInMode");
    614                         RTThreadSleep(1); // throttle
    615                 }
    616 
    617                 return hasEventQueuePendingEvents(pQueue) ? 0 : 1;
    618         }
    619 
    620         // All native queus are driven by the main application loop... Use the
    621         // fallback for now.
    622         return waitForEventsCommonFallback(pQueue, cMsTimeout);
    623 }
    624 
    625 # endif /* RT_OS_DARWIN */
    626 # endif /* !USE_EVENTQUEUE */
    627498
    628499static PyObject*
     
    634505    return NULL; /** @todo throw exception */
    635506
    636   nsIEventQueue* q = g_mainEventQ;
    637   if (q == nsnull)
    638     return NULL; /** @todo throw exception */
    639 
    640 # ifdef USE_EVENTQUEUE
    641507  int rc;
    642  
    643   Py_BEGIN_ALLOW_THREADS;
    644   rc = com::EventQueue::processThreadEventQueue(aTimeout < 0 ? RT_INDEFINITE_WAIT : (uint32_t)aTimeout);
    645   Py_END_ALLOW_THREADS;
    646  
     508  com::EventQueue* aEventQ = com::EventQueue::getMainEventQueue();
     509  NS_WARN_IF_FALSE(aEventQ != nsnull, "Null main event queue");
     510  if (!aEventQ)
     511      return NULL;
     512
     513  Py_BEGIN_ALLOW_THREADS
     514  rc = aEventQ->processEventQueue(aTimeout < 0 ? RT_INDEFINITE_WAIT : (uint32_t)aTimeout);
     515  Py_END_ALLOW_THREADS
    647516  if (RT_SUCCESS(rc))
    648517      return PyInt_FromLong(0);
     518
    649519  if (   rc == VERR_TIMEOUT
    650520      || rc == VERR_INTERRUPTED)
    651521      return PyInt_FromLong(1);
    652   return NULL; /** @todo throw exception */
    653 
    654 # else  /* !USE_EVENTQUEUE */
    655   NS_WARN_IF_FALSE(isEventQueueNative(q), "The event queue must be native!");
    656 
    657   PRInt32 result = 0;
    658 #  ifdef RT_OS_DARWIN
    659   if (aTimeout != 0 && !hasEventQueuePendingEvents(q))
    660     result = waitForEventsOnDarwin(q, aTimeout);
    661   if (result == 0)
    662     q->ProcessPendingEvents();
    663 
    664 #  else  /* !RT_OS_DARWIN */
    665 
    666   PRBool hasEvents = PR_FALSE;
    667   nsresult rc;
    668   PRInt32 fd;
    669 
    670   if (aTimeout == 0)
    671     goto ok;
    672 
    673   rc = q->PendingEvents(&hasEvents);
    674   if (NS_FAILED (rc))
    675     return NULL;
    676 
    677   if (hasEvents)
    678     goto ok;
    679 
    680   fd = q->GetEventQueueSelectFD();
    681   if (fd < 0 && aTimeout < 0)
    682   {
    683     /* fallback */
    684     PLEvent *pEvent = NULL;
    685     Py_BEGIN_ALLOW_THREADS
    686     rc = q->WaitForEvent(&pEvent);
    687     Py_END_ALLOW_THREADS
    688     if (NS_SUCCEEDED(rc))
    689       q->HandleEvent(pEvent);
    690     goto ok;
    691   }
    692 
    693   /* Cannot perform timed wait otherwise */
    694   if (fd < 0)
    695       return NULL;
    696 
    697   {
    698     fd_set fdsetR, fdsetE;
    699     struct timeval tv;
    700 
    701     FD_ZERO(&fdsetR);
    702     FD_SET(fd, &fdsetR);
    703 
    704     fdsetE = fdsetR;
    705     if (aTimeout > 0)
    706       {
    707         tv.tv_sec = (PRInt64)aTimeout / 1000;
    708         tv.tv_usec = ((PRInt64)aTimeout % 1000) * 1000;
    709       }
    710 
    711     /** @todo: What to do for XPCOM platforms w/o select() ? */
    712     Py_BEGIN_ALLOW_THREADS;
    713     int n = select(fd + 1, &fdsetR, NULL, &fdsetE, aTimeout < 0 ? NULL : &tv);
    714     result = (n == 0) ?  1 :  0;
    715     Py_END_ALLOW_THREADS;
    716   }
    717  ok:
    718   q->ProcessPendingEvents();
    719 #  endif /* !RT_OS_DARWIN */
    720   return PyInt_FromLong(result);
    721 # endif /* !USE_EVENTQUEUE */
    722 }
    723 
    724 PR_STATIC_CALLBACK(void *) PyHandleEvent(PLEvent *ev)
    725 {
    726   return nsnull;
    727 }
    728 
    729 PR_STATIC_CALLBACK(void) PyDestroyEvent(PLEvent *ev)
    730 {
    731   delete ev;
     522
     523  return NULL; /** @todo throw correct exception */
    732524}
    733525
     
    735527PyXPCOMMethod_InterruptWait(PyObject *self, PyObject *args)
    736528{
    737   nsIEventQueue* q = g_mainEventQ;
    738   PRInt32 result = 0;
    739   nsresult rc;
    740 
    741   PLEvent *ev = new PLEvent();
    742   if (!ev)
    743   {
    744     result = 1;
    745     goto done;
    746   }
    747   q->InitEvent (ev, NULL, PyHandleEvent, PyDestroyEvent);
    748   rc = q->PostEvent (ev);
    749   if (NS_FAILED(rc))
    750   {
    751     result = 2;
    752     goto done;
    753   }
    754 
    755  done:
    756   return PyInt_FromLong(result);
     529  com::EventQueue* aEventQ = com::EventQueue::getMainEventQueue();
     530  NS_WARN_IF_FALSE(aEventQ != nsnull, "Null main event queue");
     531  if (!aEventQ)
     532      return NULL;
     533
     534  aEventQ->interruptEventQueueProcessing();
     535
     536  return PyInt_FromLong(0);
    757537}
    758538
     
    1004784    rc = com::Initialize();
    1005785
    1006     if (NS_SUCCEEDED(rc))
    1007     {
    1008       NS_GetMainEventQ (&g_mainEventQ);
    1009     }
    1010 
    1011786    init_xpcom();
    1012787  }
     
    1016791void deinitVBoxPython()
    1017792{
    1018 
    1019   if (g_mainEventQ)
    1020     NS_RELEASE(g_mainEventQ);
    1021 
    1022793  com::Shutdown();
    1023794}
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