VirtualBox

source: vbox/trunk/src/VBox/Main/glue/EventQueue.cpp@ 22873

Last change on this file since 22873 was 22849, checked in by vboxsync, 15 years ago

typos

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.7 KB
Line 
1/* $Id: EventQueue.cpp 22849 2009-09-08 21:16:41Z vboxsync $ */
2
3/** @file
4 *
5 * MS COM / XPCOM Abstraction Layer:
6 * Event and EventQueue class declaration
7 */
8
9/*
10 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 *
20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
21 * Clara, CA 95054 USA or visit http://www.sun.com if you need
22 * additional information or have any questions.
23 */
24
25#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
41
42namespace com
43{
44
45// EventQueue class
46////////////////////////////////////////////////////////////////////////////////
47
48#if defined (RT_OS_WINDOWS)
49
50#define CHECK_THREAD_RET(ret) \
51 do { \
52 AssertMsg (GetCurrentThreadId() == mThreadId, ("Must be on event queue thread!")); \
53 if (GetCurrentThreadId() != mThreadId) \
54 return ret; \
55 } while (0)
56
57#else // !defined (RT_OS_WINDOWS)
58
59#define CHECK_THREAD_RET(ret) \
60 do { \
61 if (!mEventQ) \
62 return ret; \
63 BOOL isOnCurrentThread = FALSE; \
64 mEventQ->IsOnCurrentThread (&isOnCurrentThread); \
65 AssertMsg (isOnCurrentThread, ("Must be on event queue thread!")); \
66 if (!isOnCurrentThread) \
67 return ret; \
68 } while (0)
69
70#endif // !defined (RT_OS_WINDOWS)
71
72EventQueue* EventQueue::mMainQueue = NULL;
73
74/**
75 * Constructs an event queue for the current thread.
76 *
77 * Currently, there can be only one event queue per thread, so if an event
78 * queue for the current thread already exists, this object is simply attached
79 * to the existing event queue.
80 */
81EventQueue::EventQueue()
82{
83#if defined (RT_OS_WINDOWS)
84
85 mThreadId = GetCurrentThreadId();
86 // force the system to create the message queue for the current thread
87 MSG msg;
88 PeekMessage (&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
89
90#else
91
92 mEQCreated = FALSE;
93
94 mLastEvent = NULL;
95 mGotEvent = FALSE;
96
97 // Here we reference the global nsIEventQueueService instance and hold it
98 // until we're destroyed. This is necessary to keep NS_ShutdownXPCOM() away
99 // from calling StopAcceptingEvents() on all event queues upon destruction of
100 // nsIEventQueueService, and makes sense when, for some reason, this happens
101 // *before* we're able to send a NULL event to stop our event handler thread
102 // when doing unexpected cleanup caused indirectly by NS_ShutdownXPCOM()
103 // that is performing a global cleanup of everything. A good example of such
104 // situation is when NS_ShutdownXPCOM() is called while the VirtualBox component
105 // is still alive (because it is still referenced): eventually, it results in
106 // a VirtualBox::uninit() call from where it is already not possible to post
107 // NULL to the event thread (because it stopped accepting events).
108
109 nsresult rc = NS_GetEventQueueService (getter_AddRefs (mEventQService));
110
111 if (NS_SUCCEEDED(rc))
112 {
113 rc = mEventQService->GetThreadEventQueue (NS_CURRENT_THREAD,
114 getter_AddRefs (mEventQ));
115 if (rc == NS_ERROR_NOT_AVAILABLE)
116 {
117 rc = mEventQService->CreateMonitoredThreadEventQueue();
118 if (NS_SUCCEEDED(rc))
119 {
120 mEQCreated = TRUE;
121 rc = mEventQService->GetThreadEventQueue (NS_CURRENT_THREAD,
122 getter_AddRefs (mEventQ));
123 }
124 }
125 }
126 AssertComRC (rc);
127
128#endif
129}
130
131EventQueue::~EventQueue()
132{
133#if defined (RT_OS_WINDOWS)
134#else
135 // process all pending events before destruction
136 if (mEventQ)
137 {
138 if (mEQCreated)
139 {
140 mEventQ->StopAcceptingEvents();
141 mEventQ->ProcessPendingEvents();
142 mEventQService->DestroyThreadEventQueue();
143 }
144 mEventQ = nsnull;
145 mEventQService = nsnull;
146 }
147#endif
148}
149
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 BOOL 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 aCode2 = MsgWaitForMultipleObjects(1, &aHandle.mh,
333 TRUE /*fWaitAll*/,
334 0 /*ms*/,
335 QS_ALLINPUT);
336 if (aCode2 == WAIT_TIMEOUT)
337 rc = VERR_TIMEOUT;
338 else if (aCode2 == 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
356/**
357 * Posts an event to this event loop asynchronously.
358 *
359 * @param event the event to post, must be allocated using |new|
360 * @return TRUE if successful and false otherwise
361 */
362BOOL EventQueue::postEvent (Event *event)
363{
364#if defined (RT_OS_WINDOWS)
365
366 return PostThreadMessage (mThreadId, WM_USER, (WPARAM) event, NULL);
367
368#else
369
370 if (!mEventQ)
371 return FALSE;
372
373 MyPLEvent *ev = new MyPLEvent (event);
374 mEventQ->InitEvent (ev, this, plEventHandler, plEventDestructor);
375 HRESULT rc = mEventQ->PostEvent (ev);
376 return NS_SUCCEEDED(rc);
377
378#endif
379}
380
381/**
382 * Waits for a single event.
383 * This method must be called on the same thread where this event queue
384 * is created.
385 *
386 * After this method returns TRUE and non-NULL event, the caller should call
387 * #handleEvent() in order to process the returned event (otherwise the event
388 * is just removed from the queue, but not processed).
389 *
390 * There is a special case when the returned event is NULL (and the method
391 * returns TRUE), meaning that this event queue must finish its execution
392 * (i.e., quit the event loop),
393 *
394 * @param event next event removed from the queue
395 * @return TRUE if successful and false otherwise
396 */
397BOOL EventQueue::waitForEvent (Event **event)
398{
399 Assert (event);
400 if (!event)
401 return FALSE;
402
403 *event = NULL;
404
405 CHECK_THREAD_RET (FALSE);
406
407#if defined (RT_OS_WINDOWS)
408
409 MSG msg;
410 BOOL rc = GetMessage (&msg, NULL, WM_USER, WM_USER);
411 // check for error
412 if (rc == -1)
413 return FALSE;
414 // check for WM_QUIT
415 if (!rc)
416 return TRUE;
417
418 // retrieve our event
419 *event = (Event *) msg.wParam;
420
421#else
422
423 PLEvent *ev = NULL;
424 HRESULT rc;
425
426 mGotEvent = FALSE;
427
428 do
429 {
430 rc = mEventQ->WaitForEvent (&ev);
431 // check for error
432 if (FAILED (rc))
433 return FALSE;
434 // check for EINTR signal
435 if (!ev)
436 return TRUE;
437
438 // run PLEvent handler. This will just set mLastEvent if it is an
439 // MyPLEvent instance, and then delete ev.
440 mEventQ->HandleEvent (ev);
441 }
442 while (!mGotEvent);
443
444 // retrieve our event
445 *event = mLastEvent;
446
447#endif
448
449 return TRUE;
450}
451
452/**
453 * Handles the given event and |delete|s it.
454 * This method must be called on the same thread where this event queue
455 * is created.
456 */
457BOOL EventQueue::handleEvent (Event *event)
458{
459 Assert (event);
460 if (!event)
461 return FALSE;
462
463 CHECK_THREAD_RET (FALSE);
464
465 event->handler();
466 delete event;
467
468 return TRUE;
469}
470
471
472#ifdef VBOX_WITH_XPCOM
473
474/** Wrapper around nsIEventQueue::PendingEvents. */
475DECLINLINE(bool) hasEventQueuePendingEvents(nsIEventQueue *pQueue)
476{
477 PRBool fHasEvents = PR_FALSE;
478 nsresult rc = pQueue->PendingEvents(&fHasEvents);
479 return NS_SUCCEEDED(rc) && fHasEvents ? true : false;
480}
481
482/** Wrapper around nsIEventQueue::IsQueueNative. */
483DECLINLINE(bool) isEventQueueNative(nsIEventQueue *pQueue)
484{
485 PRBool fIsNative = PR_FALSE;
486 nsresult rc = pQueue->IsQueueNative(&fIsNative);
487 return NS_SUCCEEDED(rc) && fIsNative ? true : false;
488}
489
490/** Wrapper around nsIEventQueue::ProcessPendingEvents. */
491DECLINLINE(void) processPendingEvents(nsIEventQueue *pQueue)
492{
493 pQueue->ProcessPendingEvents();
494}
495
496#else
497
498/** COM version of nsIEventQueue::PendingEvents. */
499DECLINLINE(bool) hasEventQueuePendingEvents(MyThreadHandle &Handle)
500{
501 DWORD rc = MsgWaitForMultipleObjects(1, &Handle.mh, TRUE /*fWaitAll*/, 0 /*ms*/, QS_ALLINPUT);
502 return rc == WAIT_OBJECT_0;
503}
504
505/** COM version of nsIEventQueue::IsQueueNative, the question doesn't make
506 * sense and we have to return false for the code below to work. */
507DECLINLINE(bool) isEventQueueNative(MyThreadHandle const &Handle)
508{
509 return false;
510}
511
512/** COM version of nsIEventQueue::ProcessPendingEvents. */
513static void processPendingEvents(MyThreadHandle const &Handle)
514{
515 /*
516 * Process pending thead messages.
517 */
518 MSG Msg;
519 while (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE))
520 {
521 if (Msg.message == WM_QUIT)
522 return /*VERR_INTERRUPTED*/;
523 DispatchMessage(&Msg);
524 }
525}
526
527#endif /* VBOX_WITH_XPCOM */
528
529/**
530 * Processes events for the current thread.
531 *
532 * @param cMsTimeout The timeout in milliseconds or RT_INDEFINITE_WAIT.
533 * @param pfnExitCheck Optional callback for checking for some exit condition
534 * while looping. Note that this may be called
535 * @param pvUser User argument for pfnExitCheck.
536 * @param cMsPollInterval The interval cMsTimeout should be called at. 0 means
537 * never default.
538 * @param fReturnOnEvent If true, return immediately after some events has
539 * been processed. If false, process events until we
540 * time out, pfnExitCheck returns true, interrupted or
541 * the queue receives some kind of quit message.
542 *
543 * @returns VBox status code.
544 * @retval VINF_SUCCESS if events were processed.
545 * @retval VERR_TIMEOUT if no events before cMsTimeout elapsed.
546 * @retval VERR_INTERRUPTED if the wait was interrupted by a signal or other
547 * async event.
548 * @retval VERR_NOT_FOUND if the thread has no event queue.
549 * @retval VERR_CALLBACK_RETURN if the callback indicates return.
550 *
551 * @todo This is just a quick approximation of what we need. Feel free to
552 * improve the interface and make it fit better in with the EventQueue
553 * class.
554 */
555/*static*/ int
556EventQueue::processThreadEventQueue(uint32_t cMsTimeout, bool (*pfnExitCheck)(void *pvUser) /*= 0*/,
557 void *pvUser /*= 0*/, uint32_t cMsPollInterval /*= 1000*/,
558 bool fReturnOnEvent /*= true*/)
559{
560 uint64_t const StartMsTS = RTTimeMilliTS();
561
562 /* set default. */
563 if (cMsPollInterval == 0)
564 cMsPollInterval = 1000;
565
566 /*
567 * Get the event queue / thread.
568 */
569#ifdef VBOX_WITH_XPCOM
570 nsCOMPtr<nsIEventQueue> q;
571 nsresult rv = NS_GetCurrentEventQ(getter_AddRefs(q));
572 if (NS_FAILED(rv))
573 return VERR_NOT_FOUND;
574#else
575 MyThreadHandle q;
576#endif
577
578 /*
579 * Check for pending before setting up the wait.
580 */
581 if ( !hasEventQueuePendingEvents(q)
582 || !fReturnOnEvent)
583 {
584 bool fIsNative = isEventQueueNative(q);
585 if ( fIsNative
586 || cMsTimeout != RT_INDEFINITE_WAIT
587 || pfnExitCheck
588 || !fReturnOnEvent /** @todo !fReturnOnEvent and cMsTimeout RT_INDEFINITE_WAIT can be handled in else */)
589 {
590#ifdef USE_XPCOM_QUEUE
591 int const fdQueue = fIsNative ? q->GetEventQueueSelectFD() : -1;
592 if (fIsNative && fdQueue == -1)
593 return VERR_INTERNAL_ERROR_4;
594#endif
595 for (;;)
596 {
597 /*
598 * Check for events.
599 */
600 if (hasEventQueuePendingEvents(q))
601 {
602 if (fReturnOnEvent)
603 break;
604 processPendingEvents(q);
605 }
606
607 /*
608 * Check the user exit.
609 */
610 if ( pfnExitCheck
611 && pfnExitCheck(pvUser))
612 return VERR_CALLBACK_RETURN;
613
614 /*
615 * Figure out how much we have left to wait and if we've timed out already.
616 */
617 uint32_t cMsLeft;
618 if (cMsTimeout == RT_INDEFINITE_WAIT)
619 cMsLeft = RT_INDEFINITE_WAIT;
620 else
621 {
622 uint64_t cMsElapsed = RTTimeMilliTS() - StartMsTS;
623 if (cMsElapsed >= cMsTimeout)
624 break; /* timeout */
625 cMsLeft = cMsTimeout - (uint32_t)cMsElapsed;
626 }
627
628 /*
629 * Wait in a queue & platform specific manner.
630 */
631#ifdef VBOX_WITH_XPCOM
632 if (!fIsNative)
633 RTThreadSleep(250 /*ms*/);
634 else
635 {
636# ifdef USE_XPCOM_QUEUE
637 fd_set fdset;
638 FD_ZERO(&fdset);
639 FD_SET(fdQueue, &fdset);
640 struct timeval tv;
641 if ( cMsLeft == RT_INDEFINITE_WAIT
642 || cMsLeft >= cMsPollInterval)
643 {
644 tv.tv_sec = cMsPollInterval / 1000;
645 tv.tv_usec = (cMsPollInterval % 1000) * 1000;
646 }
647 else
648 {
649 tv.tv_sec = cMsLeft / 1000;
650 tv.tv_usec = (cMsLeft % 1000) * 1000;
651 }
652 int prc = select(fdQueue + 1, &fdset, NULL, NULL, &tv);
653 if (prc == -1)
654 return RTErrConvertFromErrno(errno);
655
656# elif defined(RT_OS_DARWIN)
657 CFTimeInterval rdTimeout = (double)RT_MIN(cMsLeft, cMsPollInterval) / 1000;
658 OSStatus orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/);
659 if (orc == kCFRunLoopRunHandledSource)
660 orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
661 if ( orc != 0
662 && orc != kCFRunLoopRunHandledSource
663 && orc != kCFRunLoopRunTimedOut)
664 return orc == kCFRunLoopRunStopped || orc == kCFRunLoopRunFinished
665 ? VERR_INTERRUPTED
666 : RTErrConvertFromDarwin(orc);
667# else
668# warning "PORTME:"
669 RTThreadSleep(250);
670# endif
671 }
672
673#else /* !VBOX_WITH_XPCOM */
674 DWORD rc = MsgWaitForMultipleObjects(1, &q.mh, TRUE /*fWaitAll*/, RT_MIN(cMsLeft, cMsPollInterval), QS_ALLINPUT);
675 if (rc == WAIT_OBJECT_0)
676 {
677 if (fReturnOnEvent)
678 break;
679 processPendingEvents(q);
680 }
681 else if (rc == WAIT_FAILED)
682 return RTErrConvertFromWin32(GetLastError());
683 else if (rc != WAIT_TIMEOUT)
684 return VERR_INTERNAL_ERROR_4;
685#endif /* !VBOX_WITH_XPCOM */
686 } /* for (;;) */
687 }
688 else
689 {
690 /*
691 * Indefinite wait without any complications.
692 */
693#ifdef VBOX_WITH_XPCOM
694 PLEvent *pEvent = NULL;
695 rv = q->WaitForEvent(&pEvent);
696 if (NS_FAILED(rv))
697 return VERR_INTERRUPTED;
698 q->HandleEvent(pEvent);
699#else
700 DWORD rc = MsgWaitForMultipleObjects(1, &q.mh, TRUE /*fWaitAll*/, INFINITE, QS_ALLINPUT);
701 if (rc != WAIT_OBJECT_0)
702 {
703 if (rc == WAIT_FAILED)
704 return RTErrConvertFromWin32(GetLastError());
705 return VERR_INTERNAL_ERROR_3;
706 }
707#endif
708 }
709 }
710
711 /*
712 * We have/had events in the queue. Process pending events and
713 * return successfully.
714 */
715 processPendingEvents(q);
716
717 return VINF_SUCCESS;
718}
719}
720/* namespace com */
721
Note: See TracBrowser for help on using the repository browser.

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