summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/glue/NativeEventQueue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/glue/NativeEventQueue.cpp')
-rw-r--r--src/VBox/Main/glue/NativeEventQueue.cpp676
1 files changed, 676 insertions, 0 deletions
diff --git a/src/VBox/Main/glue/NativeEventQueue.cpp b/src/VBox/Main/glue/NativeEventQueue.cpp
new file mode 100644
index 00000000..fc075c6b
--- /dev/null
+++ b/src/VBox/Main/glue/NativeEventQueue.cpp
@@ -0,0 +1,676 @@
+/* $Id: NativeEventQueue.cpp $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer:
+ * Main event queue class declaration
+ */
+
+/*
+ * Copyright (C) 2006-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "VBox/com/NativeEventQueue.h"
+
+#include <new> /* For bad_alloc. */
+
+#ifdef RT_OS_DARWIN
+# include <CoreFoundation/CFRunLoop.h>
+#endif
+
+#if defined(VBOX_WITH_XPCOM) && !defined(RT_OS_DARWIN) && !defined(RT_OS_OS2)
+# define USE_XPCOM_QUEUE
+#endif
+
+#include <iprt/err.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#ifdef USE_XPCOM_QUEUE
+# include <errno.h>
+#endif
+
+namespace com
+{
+
+// NativeEventQueue class
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef VBOX_WITH_XPCOM
+
+# define CHECK_THREAD_RET(ret) \
+ do { \
+ AssertMsg(GetCurrentThreadId() == mThreadId, ("Must be on event queue thread!")); \
+ if (GetCurrentThreadId() != mThreadId) \
+ return ret; \
+ } while (0)
+
+/** Magic LPARAM value for the WM_USER messages that we're posting.
+ * @remarks This magic value is duplicated in
+ * vboxapi/PlatformMSCOM::interruptWaitEvents(). */
+#define EVENTQUEUE_WIN_LPARAM_MAGIC UINT32_C(0xf241b819)
+
+
+#else // VBOX_WITH_XPCOM
+
+# define CHECK_THREAD_RET(ret) \
+ do { \
+ if (!mEventQ) \
+ return ret; \
+ BOOL isOnCurrentThread = FALSE; \
+ mEventQ->IsOnCurrentThread(&isOnCurrentThread); \
+ AssertMsg(isOnCurrentThread, ("Must be on event queue thread!")); \
+ if (!isOnCurrentThread) \
+ return ret; \
+ } while (0)
+
+#endif // VBOX_WITH_XPCOM
+
+/** Pointer to the main event queue. */
+NativeEventQueue *NativeEventQueue::sMainQueue = NULL;
+
+
+#ifdef VBOX_WITH_XPCOM
+
+struct MyPLEvent : public PLEvent
+{
+ MyPLEvent(NativeEvent *e) : event(e) {}
+ NativeEvent *event;
+};
+
+/* static */
+void *PR_CALLBACK com::NativeEventQueue::plEventHandler(PLEvent *self)
+{
+ NativeEvent *ev = ((MyPLEvent *)self)->event;
+ if (ev)
+ ev->handler();
+ else
+ {
+ NativeEventQueue *eq = (NativeEventQueue *)self->owner;
+ Assert(eq);
+ eq->mInterrupted = true;
+ }
+ return NULL;
+}
+
+/* static */
+void PR_CALLBACK com::NativeEventQueue::plEventDestructor(PLEvent *self)
+{
+ NativeEvent *ev = ((MyPLEvent *)self)->event;
+ if (ev)
+ delete ev;
+ delete self;
+}
+
+#endif // VBOX_WITH_XPCOM
+
+/**
+ * Constructs an event queue for the current thread.
+ *
+ * Currently, there can be only one event queue per thread, so if an event
+ * queue for the current thread already exists, this object is simply attached
+ * to the existing event queue.
+ */
+NativeEventQueue::NativeEventQueue()
+{
+#ifndef VBOX_WITH_XPCOM
+
+ mThreadId = GetCurrentThreadId();
+ // force the system to create the message queue for the current thread
+ MSG msg;
+ PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ if (!DuplicateHandle(GetCurrentProcess(),
+ GetCurrentThread(),
+ GetCurrentProcess(),
+ &mhThread,
+ 0 /*dwDesiredAccess*/,
+ FALSE /*bInheritHandle*/,
+ DUPLICATE_SAME_ACCESS))
+ mhThread = INVALID_HANDLE_VALUE;
+
+#else // VBOX_WITH_XPCOM
+
+ mEQCreated = false;
+ mInterrupted = false;
+
+ // Here we reference the global nsIEventQueueService instance and hold it
+ // until we're destroyed. This is necessary to keep NS_ShutdownXPCOM() away
+ // from calling StopAcceptingEvents() on all event queues upon destruction of
+ // nsIEventQueueService, and makes sense when, for some reason, this happens
+ // *before* we're able to send a NULL event to stop our event handler thread
+ // when doing unexpected cleanup caused indirectly by NS_ShutdownXPCOM()
+ // that is performing a global cleanup of everything. A good example of such
+ // situation is when NS_ShutdownXPCOM() is called while the VirtualBox component
+ // is still alive (because it is still referenced): eventually, it results in
+ // a VirtualBox::uninit() call from where it is already not possible to post
+ // NULL to the event thread (because it stopped accepting events).
+
+ nsresult rc = NS_GetEventQueueService(getter_AddRefs(mEventQService));
+
+ if (NS_SUCCEEDED(rc))
+ {
+ rc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD,
+ getter_AddRefs(mEventQ));
+ if (rc == NS_ERROR_NOT_AVAILABLE)
+ {
+ rc = mEventQService->CreateThreadEventQueue();
+ if (NS_SUCCEEDED(rc))
+ {
+ mEQCreated = true;
+ rc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD,
+ getter_AddRefs(mEventQ));
+ }
+ }
+ }
+ AssertComRC(rc);
+
+#endif // VBOX_WITH_XPCOM
+}
+
+NativeEventQueue::~NativeEventQueue()
+{
+#ifndef VBOX_WITH_XPCOM
+ if (mhThread != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(mhThread);
+ mhThread = INVALID_HANDLE_VALUE;
+ }
+#else // VBOX_WITH_XPCOM
+ // process all pending events before destruction
+ if (mEventQ)
+ {
+ if (mEQCreated)
+ {
+ mEventQ->StopAcceptingEvents();
+ mEventQ->ProcessPendingEvents();
+ mEventQService->DestroyThreadEventQueue();
+ }
+ mEventQ = nsnull;
+ mEventQService = nsnull;
+ }
+#endif // VBOX_WITH_XPCOM
+}
+
+/**
+ * Initializes the main event queue instance.
+ * @returns VBox status code.
+ *
+ * @remarks If you're using the rest of the COM/XPCOM glue library,
+ * com::Initialize() will take care of initializing and uninitializing
+ * the NativeEventQueue class. If you don't call com::Initialize, you must
+ * make sure to call this method on the same thread that did the
+ * XPCOM initialization or we'll end up using the wrong main queue.
+ */
+/* static */
+int NativeEventQueue::init()
+{
+ Assert(sMainQueue == NULL);
+ Assert(RTThreadIsMain(RTThreadSelf()));
+
+ try
+ {
+ sMainQueue = new NativeEventQueue();
+ AssertPtr(sMainQueue);
+#ifdef VBOX_WITH_XPCOM
+ /* Check that it actually is the main event queue, i.e. that
+ we're called on the right thread. */
+ nsCOMPtr<nsIEventQueue> q;
+ nsresult rv = NS_GetMainEventQ(getter_AddRefs(q));
+ AssertComRCReturn(rv, VERR_INVALID_POINTER);
+ Assert(q == sMainQueue->mEventQ);
+
+ /* Check that it's a native queue. */
+ PRBool fIsNative = PR_FALSE;
+ rv = sMainQueue->mEventQ->IsQueueNative(&fIsNative);
+ Assert(NS_SUCCEEDED(rv) && fIsNative);
+#endif // VBOX_WITH_XPCOM
+ }
+ catch (std::bad_alloc &ba)
+ {
+ NOREF(ba);
+ return VERR_NO_MEMORY;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Uninitialize the global resources (i.e. the main event queue instance).
+ * @returns VINF_SUCCESS
+ */
+/* static */
+int NativeEventQueue::uninit()
+{
+ if (sMainQueue)
+ {
+ /* Must process all events to make sure that no NULL event is left
+ * after this point. It would need to modify the state of sMainQueue. */
+#ifdef RT_OS_DARWIN /* Do not process the native runloop, the toolkit may not be ready for it. */
+ sMainQueue->mEventQ->ProcessPendingEvents();
+#else
+ sMainQueue->processEventQueue(0);
+#endif
+ delete sMainQueue;
+ sMainQueue = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Get main event queue instance.
+ *
+ * Depends on init() being called first.
+ */
+/* static */
+NativeEventQueue* NativeEventQueue::getMainEventQueue()
+{
+ return sMainQueue;
+}
+
+#ifdef VBOX_WITH_XPCOM
+# ifdef RT_OS_DARWIN
+/**
+ * Wait for events and process them (Darwin).
+ *
+ * @retval VINF_SUCCESS
+ * @retval VERR_TIMEOUT
+ * @retval VERR_INTERRUPTED
+ *
+ * @param cMsTimeout How long to wait, or RT_INDEFINITE_WAIT.
+ */
+static int waitForEventsOnDarwin(RTMSINTERVAL cMsTimeout)
+{
+ /*
+ * Wait for the requested time, if we get a hit we do a poll to process
+ * any other pending messages.
+ *
+ * Note! About 1.0e10: According to the sources anything above 3.1556952e+9
+ * means indefinite wait and 1.0e10 is what CFRunLoopRun() uses.
+ */
+ CFTimeInterval rdTimeout = cMsTimeout == RT_INDEFINITE_WAIT ? 1e10 : (double)cMsTimeout / 1000;
+ OSStatus orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/);
+ if (orc == kCFRunLoopRunHandledSource)
+ {
+ OSStatus orc2 = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
+ if ( orc2 == kCFRunLoopRunStopped
+ || orc2 == kCFRunLoopRunFinished)
+ orc = orc2;
+ }
+ if ( orc == 0 /*???*/
+ || orc == kCFRunLoopRunHandledSource)
+ return VINF_SUCCESS;
+ if ( orc == kCFRunLoopRunStopped
+ || orc == kCFRunLoopRunFinished)
+ return VERR_INTERRUPTED;
+ AssertMsg(orc == kCFRunLoopRunTimedOut, ("Unexpected status code from CFRunLoopRunInMode: %#x", orc));
+ return VERR_TIMEOUT;
+}
+# else // !RT_OS_DARWIN
+
+/**
+ * Wait for events (generic XPCOM).
+ *
+ * @retval VINF_SUCCESS
+ * @retval VERR_TIMEOUT
+ * @retval VINF_INTERRUPTED
+ * @retval VERR_INTERNAL_ERROR_4
+ *
+ * @param pQueue The queue to wait on.
+ * @param cMsTimeout How long to wait, or RT_INDEFINITE_WAIT.
+ */
+static int waitForEventsOnXPCOM(nsIEventQueue *pQueue, RTMSINTERVAL cMsTimeout)
+{
+ int fd = pQueue->GetEventQueueSelectFD();
+ fd_set fdsetR;
+ FD_ZERO(&fdsetR);
+ FD_SET(fd, &fdsetR);
+
+ fd_set fdsetE = fdsetR;
+
+ struct timeval tv = {0,0};
+ struct timeval *ptv;
+ if (cMsTimeout == RT_INDEFINITE_WAIT)
+ ptv = NULL;
+ else
+ {
+ tv.tv_sec = cMsTimeout / 1000;
+ tv.tv_usec = (cMsTimeout % 1000) * 1000;
+ ptv = &tv;
+ }
+
+ int rc = select(fd + 1, &fdsetR, NULL, &fdsetE, ptv);
+ if (rc > 0)
+ rc = VINF_SUCCESS;
+ else if (rc == 0)
+ rc = VERR_TIMEOUT;
+ else if (errno == EINTR)
+ rc = VINF_INTERRUPTED;
+ else
+ {
+ static uint32_t s_ErrorCount = 0;
+ if (s_ErrorCount < 500)
+ {
+ LogRel(("waitForEventsOnXPCOM rc=%d errno=%d\n", rc, errno));
+ ++s_ErrorCount;
+ }
+
+ AssertMsgFailed(("rc=%d errno=%d\n", rc, errno));
+ rc = VERR_INTERNAL_ERROR_4;
+ }
+ return rc;
+}
+
+# endif // !RT_OS_DARWIN
+#endif // VBOX_WITH_XPCOM
+
+#ifndef VBOX_WITH_XPCOM
+
+/**
+ * Dispatch a message on Windows.
+ *
+ * This will pick out our events and handle them specially.
+ *
+ * @returns @a rc or VERR_INTERRUPTED (WM_QUIT or NULL msg).
+ * @param pMsg The message to dispatch.
+ * @param rc The current status code.
+ */
+/*static*/
+int NativeEventQueue::dispatchMessageOnWindows(MSG const *pMsg, int rc)
+{
+ /*
+ * Check for and dispatch our events.
+ */
+ if ( pMsg->hwnd == NULL
+ && pMsg->message == WM_USER)
+ {
+ if (pMsg->lParam == EVENTQUEUE_WIN_LPARAM_MAGIC)
+ {
+ NativeEvent *pEvent = (NativeEvent *)pMsg->wParam;
+ if (pEvent)
+ {
+ pEvent->handler();
+ delete pEvent;
+ }
+ else
+ rc = VERR_INTERRUPTED;
+ return rc;
+ }
+ AssertMsgFailed(("lParam=%p wParam=%p\n", pMsg->lParam, pMsg->wParam));
+ }
+
+ /*
+ * Check for the quit message and dispatch the message the normal way.
+ */
+ if (pMsg->message == WM_QUIT)
+ rc = VERR_INTERRUPTED;
+ TranslateMessage(pMsg);
+ DispatchMessage(pMsg);
+
+ return rc;
+}
+
+
+/**
+ * Process pending events (Windows).
+ *
+ * @retval VINF_SUCCESS
+ * @retval VERR_TIMEOUT
+ * @retval VERR_INTERRUPTED.
+ */
+static int processPendingEvents(void)
+{
+ int rc = VERR_TIMEOUT;
+ MSG Msg;
+ if (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE))
+ {
+ rc = VINF_SUCCESS;
+ do
+ rc = NativeEventQueue::dispatchMessageOnWindows(&Msg, rc);
+ while (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE));
+ }
+ return rc;
+}
+
+#else // VBOX_WITH_XPCOM
+
+/**
+ * Process pending XPCOM events.
+ * @param pQueue The queue to process events on.
+ * @retval VINF_SUCCESS
+ * @retval VERR_TIMEOUT
+ * @retval VERR_INTERRUPTED (darwin only)
+ * @retval VERR_INTERNAL_ERROR_2
+ */
+static int processPendingEvents(nsIEventQueue *pQueue)
+{
+ /* ProcessPendingEvents doesn't report back what it did, so check here. */
+ PRBool fHasEvents = PR_FALSE;
+ nsresult hr = pQueue->PendingEvents(&fHasEvents);
+ if (NS_FAILED(hr))
+ return VERR_INTERNAL_ERROR_2;
+
+ /* Process pending events. */
+ int rc = VINF_SUCCESS;
+ if (fHasEvents)
+ pQueue->ProcessPendingEvents();
+ else
+ rc = VERR_TIMEOUT;
+
+# ifdef RT_OS_DARWIN
+ /* Process pending native events. */
+ int rc2 = waitForEventsOnDarwin(0);
+ if (rc == VERR_TIMEOUT || rc2 == VERR_INTERRUPTED)
+ rc = rc2;
+# endif
+
+ return rc;
+}
+
+#endif // VBOX_WITH_XPCOM
+
+/**
+ * Process events pending on this event queue, and wait up to given timeout, if
+ * nothing is available.
+ *
+ * Must be called on same thread this event queue was created on.
+ *
+ * @param cMsTimeout The timeout specified as milliseconds. Use
+ * RT_INDEFINITE_WAIT to wait till an event is posted on the
+ * queue.
+ *
+ * @returns VBox status code
+ * @retval VINF_SUCCESS if one or more messages was processed.
+ * @retval VERR_TIMEOUT if cMsTimeout expired.
+ * @retval VERR_INVALID_CONTEXT if called on the wrong thread.
+ * @retval VERR_INTERRUPTED if interruptEventQueueProcessing was called.
+ * On Windows will also be returned when WM_QUIT is encountered.
+ * On Darwin this may also be returned when the native queue is
+ * stopped or destroyed/finished.
+ * @retval VINF_INTERRUPTED if the native system call was interrupted by a
+ * an asynchronous event delivery (signal) or just felt like returning
+ * out of bounds. On darwin it will also be returned if the queue is
+ * stopped.
+ *
+ * @note On darwin this function will not return when the thread receives a
+ * signal, it will just resume the wait.
+ */
+int NativeEventQueue::processEventQueue(RTMSINTERVAL cMsTimeout)
+{
+ int rc;
+ CHECK_THREAD_RET(VERR_INVALID_CONTEXT);
+
+#ifdef VBOX_WITH_XPCOM
+ /*
+ * Process pending events, if none are available and we're not in a
+ * poll call, wait for some to appear. (We have to be a little bit
+ * careful after waiting for the events since Darwin will process
+ * them as part of the wait, while the XPCOM case will not.)
+ *
+ * Note! Unfortunately, WaitForEvent isn't interruptible with Ctrl-C,
+ * while select() is. So we cannot use it for indefinite waits.
+ */
+ rc = processPendingEvents(mEventQ);
+ if ( rc == VERR_TIMEOUT
+ && cMsTimeout > 0)
+ {
+# ifdef RT_OS_DARWIN
+ /** @todo check how Ctrl-C works on Darwin.
+ * Update: It doesn't work. MACH_RCV_INTERRUPT could perhaps be returned
+ * to __CFRunLoopServiceMachPort, but neither it nor __CFRunLoopRun
+ * has any way of expressing it via their return values. So, if
+ * Ctrl-C handling is important, signal needs to be handled on
+ * a different thread or something. */
+ rc = waitForEventsOnDarwin(cMsTimeout);
+# else // !RT_OS_DARWIN
+ rc = waitForEventsOnXPCOM(mEventQ, cMsTimeout);
+# endif // !RT_OS_DARWIN
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_TIMEOUT)
+ {
+ int rc2 = processPendingEvents(mEventQ);
+ /* If the wait was successful don't fail the whole operation. */
+ if (RT_FAILURE(rc) && RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ }
+
+ if ( ( RT_SUCCESS(rc)
+ || rc == VERR_INTERRUPTED
+ || rc == VERR_TIMEOUT)
+ && mInterrupted)
+ {
+ mInterrupted = false;
+ rc = VERR_INTERRUPTED;
+ }
+
+#else // !VBOX_WITH_XPCOM
+ if (cMsTimeout == RT_INDEFINITE_WAIT)
+ {
+ BOOL fRet = 0; /* Shut up MSC */
+ MSG Msg;
+ rc = VINF_SUCCESS;
+ while ( rc != VERR_INTERRUPTED
+ && (fRet = GetMessage(&Msg, NULL /*hWnd*/, WM_USER, WM_USER))
+ && fRet != -1)
+ rc = NativeEventQueue::dispatchMessageOnWindows(&Msg, rc);
+ if (fRet == 0)
+ rc = VERR_INTERRUPTED;
+ else if (fRet == -1)
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ rc = processPendingEvents();
+ if ( rc == VERR_TIMEOUT
+ && cMsTimeout != 0)
+ {
+ DWORD rcW = MsgWaitForMultipleObjects(1,
+ &mhThread,
+ TRUE /*fWaitAll*/,
+ cMsTimeout,
+ QS_ALLINPUT);
+ AssertMsgReturn(rcW == WAIT_TIMEOUT || rcW == WAIT_OBJECT_0,
+ ("%d\n", rcW),
+ VERR_INTERNAL_ERROR_4);
+ rc = processPendingEvents();
+ }
+ }
+#endif // !VBOX_WITH_XPCOM
+
+ Assert(rc != VERR_TIMEOUT || cMsTimeout != RT_INDEFINITE_WAIT);
+ return rc;
+}
+
+/**
+ * Interrupt thread waiting on event queue processing.
+ *
+ * Can be called on any thread.
+ *
+ * @returns VBox status code.
+ */
+int NativeEventQueue::interruptEventQueueProcessing()
+{
+ /* Send a NULL event. This event will be picked up and handled specially
+ * both for XPCOM and Windows. It is the responsibility of the caller to
+ * take care of not running the loop again in a way which will hang. */
+ postEvent(NULL);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Posts an event to this event loop asynchronously.
+ *
+ * @param pEvent the event to post, must be allocated using |new|
+ * @return @c TRUE if successful and false otherwise
+ */
+BOOL NativeEventQueue::postEvent(NativeEvent *pEvent)
+{
+#ifndef VBOX_WITH_XPCOM
+ /* Note! The event == NULL case is duplicated in vboxapi/PlatformMSCOM::interruptWaitEvents(). */
+ BOOL fRc = PostThreadMessage(mThreadId, WM_USER, (WPARAM)pEvent, EVENTQUEUE_WIN_LPARAM_MAGIC);
+ if (!fRc)
+ {
+ static int s_cBitchedAboutFullNativeEventQueue = 0;
+ if ( GetLastError() == ERROR_NOT_ENOUGH_QUOTA
+ && s_cBitchedAboutFullNativeEventQueue < 10)
+ LogRel(("Warning: Asynchronous event queue (%p, thread %RI32) full, event (%p) not delivered (%d/10)\n",
+ this, mThreadId, pEvent, ++s_cBitchedAboutFullNativeEventQueue));
+ else
+ AssertFailed();
+ }
+ return fRc;
+#else // VBOX_WITH_XPCOM
+ if (!mEventQ)
+ return FALSE;
+
+ try
+ {
+ MyPLEvent *pMyEvent = new MyPLEvent(pEvent);
+ mEventQ->InitEvent(pMyEvent, this, com::NativeEventQueue::plEventHandler,
+ com::NativeEventQueue::plEventDestructor);
+ HRESULT rc = mEventQ->PostEvent(pMyEvent);
+ return NS_SUCCEEDED(rc);
+ }
+ catch (std::bad_alloc &ba)
+ {
+ AssertMsgFailed(("Out of memory while allocating memory for event=%p: %s\n",
+ pEvent, ba.what()));
+ }
+
+ return FALSE;
+#endif // VBOX_WITH_XPCOM
+}
+
+/**
+ * Get select()'able selector for this event queue.
+ * This will return -1 on platforms and queue variants not supporting such
+ * functionality.
+ */
+int NativeEventQueue::getSelectFD()
+{
+#ifdef VBOX_WITH_XPCOM
+ return mEventQ->GetEventQueueSelectFD();
+#else
+ return -1;
+#endif
+}
+
+}
+/* namespace com */