VirtualBox

source: vbox/trunk/src/VBox/Main/glue/NativeEventQueue.cpp

Last change on this file was 98297, checked in by vboxsync, 16 months ago

Main: rc -> hrc/vrc for all but testcases. Enabled scm rc checks accordingly. bugref:10223

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.0 KB
Line 
1/* $Id: NativeEventQueue.cpp 98297 2023-01-25 01:59:25Z vboxsync $ */
2/** @file
3 * MS COM / XPCOM Abstraction Layer:
4 * Main event queue class declaration
5 */
6
7/*
8 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
9 *
10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
27 */
28
29#include "VBox/com/NativeEventQueue.h"
30
31#include <new> /* For bad_alloc. */
32
33#ifdef RT_OS_DARWIN
34# include <CoreFoundation/CFRunLoop.h>
35#endif
36
37#if defined(VBOX_WITH_XPCOM) && !defined(RT_OS_DARWIN) && !defined(RT_OS_OS2)
38# define USE_XPCOM_QUEUE
39#endif
40
41#include <iprt/err.h>
42#include <iprt/time.h>
43#include <iprt/thread.h>
44#include <iprt/log.h>
45#ifdef USE_XPCOM_QUEUE
46# include <errno.h>
47#endif
48
49namespace com
50{
51
52// NativeEventQueue class
53////////////////////////////////////////////////////////////////////////////////
54
55#ifndef VBOX_WITH_XPCOM
56
57# define CHECK_THREAD_RET(ret) \
58 do { \
59 AssertMsg(GetCurrentThreadId() == mThreadId, ("Must be on event queue thread!")); \
60 if (GetCurrentThreadId() != mThreadId) \
61 return ret; \
62 } while (0)
63
64/** Magic LPARAM value for the WM_USER messages that we're posting.
65 * @remarks This magic value is duplicated in
66 * vboxapi/PlatformMSCOM::interruptWaitEvents(). */
67#define EVENTQUEUE_WIN_LPARAM_MAGIC UINT32_C(0xf241b819)
68
69
70#else // VBOX_WITH_XPCOM
71
72# define CHECK_THREAD_RET(ret) \
73 do { \
74 if (!mEventQ) \
75 return ret; \
76 BOOL isOnCurrentThread = FALSE; \
77 mEventQ->IsOnCurrentThread(&isOnCurrentThread); \
78 AssertMsg(isOnCurrentThread, ("Must be on event queue thread!")); \
79 if (!isOnCurrentThread) \
80 return ret; \
81 } while (0)
82
83#endif // VBOX_WITH_XPCOM
84
85/** Pointer to the main event queue. */
86NativeEventQueue *NativeEventQueue::sMainQueue = NULL;
87
88
89#ifdef VBOX_WITH_XPCOM
90
91struct MyPLEvent : public PLEvent
92{
93 MyPLEvent(NativeEvent *e) : event(e) {}
94 NativeEvent *event;
95};
96
97/* static */
98void *PR_CALLBACK com::NativeEventQueue::plEventHandler(PLEvent *self)
99{
100 NativeEvent *ev = ((MyPLEvent *)self)->event;
101 if (ev)
102 ev->handler();
103 else
104 {
105 NativeEventQueue *eq = (NativeEventQueue *)self->owner;
106 Assert(eq);
107 eq->mInterrupted = true;
108 }
109 return NULL;
110}
111
112/* static */
113void PR_CALLBACK com::NativeEventQueue::plEventDestructor(PLEvent *self)
114{
115 NativeEvent *ev = ((MyPLEvent *)self)->event;
116 if (ev)
117 delete ev;
118 delete self;
119}
120
121#endif // VBOX_WITH_XPCOM
122
123/**
124 * Constructs an event queue for the current thread.
125 *
126 * Currently, there can be only one event queue per thread, so if an event
127 * queue for the current thread already exists, this object is simply attached
128 * to the existing event queue.
129 */
130NativeEventQueue::NativeEventQueue()
131{
132#ifndef VBOX_WITH_XPCOM
133
134 mThreadId = GetCurrentThreadId();
135 // force the system to create the message queue for the current thread
136 MSG msg;
137 PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
138
139 if (!DuplicateHandle(GetCurrentProcess(),
140 GetCurrentThread(),
141 GetCurrentProcess(),
142 &mhThread,
143 0 /*dwDesiredAccess*/,
144 FALSE /*bInheritHandle*/,
145 DUPLICATE_SAME_ACCESS))
146 mhThread = INVALID_HANDLE_VALUE;
147
148#else // VBOX_WITH_XPCOM
149
150 mEQCreated = false;
151 mInterrupted = false;
152
153 // Here we reference the global nsIEventQueueService instance and hold it
154 // until we're destroyed. This is necessary to keep NS_ShutdownXPCOM() away
155 // from calling StopAcceptingEvents() on all event queues upon destruction of
156 // nsIEventQueueService, and makes sense when, for some reason, this happens
157 // *before* we're able to send a NULL event to stop our event handler thread
158 // when doing unexpected cleanup caused indirectly by NS_ShutdownXPCOM()
159 // that is performing a global cleanup of everything. A good example of such
160 // situation is when NS_ShutdownXPCOM() is called while the VirtualBox component
161 // is still alive (because it is still referenced): eventually, it results in
162 // a VirtualBox::uninit() call from where it is already not possible to post
163 // NULL to the event thread (because it stopped accepting events).
164
165 nsresult hrc = NS_GetEventQueueService(getter_AddRefs(mEventQService));
166
167 if (NS_SUCCEEDED(hrc))
168 {
169 hrc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQ));
170 if (hrc == NS_ERROR_NOT_AVAILABLE)
171 {
172 hrc = mEventQService->CreateThreadEventQueue();
173 if (NS_SUCCEEDED(hrc))
174 {
175 mEQCreated = true;
176 hrc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQ));
177 }
178 }
179 }
180 AssertComRC(hrc);
181
182#endif // VBOX_WITH_XPCOM
183}
184
185NativeEventQueue::~NativeEventQueue()
186{
187#ifndef VBOX_WITH_XPCOM
188 if (mhThread != INVALID_HANDLE_VALUE)
189 {
190 CloseHandle(mhThread);
191 mhThread = INVALID_HANDLE_VALUE;
192 }
193#else // VBOX_WITH_XPCOM
194 // process all pending events before destruction
195 if (mEventQ)
196 {
197 if (mEQCreated)
198 {
199 mEventQ->StopAcceptingEvents();
200 mEventQ->ProcessPendingEvents();
201 mEventQService->DestroyThreadEventQueue();
202 }
203 mEventQ = nsnull;
204 mEventQService = nsnull;
205 }
206#endif // VBOX_WITH_XPCOM
207}
208
209/**
210 * Initializes the main event queue instance.
211 * @returns VBox status code.
212 *
213 * @remarks If you're using the rest of the COM/XPCOM glue library,
214 * com::Initialize() will take care of initializing and uninitializing
215 * the NativeEventQueue class. If you don't call com::Initialize, you must
216 * make sure to call this method on the same thread that did the
217 * XPCOM initialization or we'll end up using the wrong main queue.
218 */
219/* static */
220int NativeEventQueue::init()
221{
222 Assert(sMainQueue == NULL);
223 Assert(RTThreadIsMain(RTThreadSelf()));
224
225 try
226 {
227 sMainQueue = new NativeEventQueue();
228 AssertPtr(sMainQueue);
229#ifdef VBOX_WITH_XPCOM
230 /* Check that it actually is the main event queue, i.e. that
231 we're called on the right thread. */
232 nsCOMPtr<nsIEventQueue> q;
233 nsresult rv = NS_GetMainEventQ(getter_AddRefs(q));
234 AssertComRCReturn(rv, VERR_INVALID_POINTER);
235 Assert(q == sMainQueue->mEventQ);
236
237 /* Check that it's a native queue. */
238 PRBool fIsNative = PR_FALSE;
239 rv = sMainQueue->mEventQ->IsQueueNative(&fIsNative);
240 Assert(NS_SUCCEEDED(rv) && fIsNative);
241#endif // VBOX_WITH_XPCOM
242 }
243 catch (std::bad_alloc &ba)
244 {
245 NOREF(ba);
246 return VERR_NO_MEMORY;
247 }
248
249 return VINF_SUCCESS;
250}
251
252/**
253 * Uninitialize the global resources (i.e. the main event queue instance).
254 * @returns VINF_SUCCESS
255 */
256/* static */
257int NativeEventQueue::uninit()
258{
259 if (sMainQueue)
260 {
261 /* Must process all events to make sure that no NULL event is left
262 * after this point. It would need to modify the state of sMainQueue. */
263#ifdef RT_OS_DARWIN /* Do not process the native runloop, the toolkit may not be ready for it. */
264 sMainQueue->mEventQ->ProcessPendingEvents();
265#else
266 sMainQueue->processEventQueue(0);
267#endif
268 delete sMainQueue;
269 sMainQueue = NULL;
270 }
271 return VINF_SUCCESS;
272}
273
274/**
275 * Get main event queue instance.
276 *
277 * Depends on init() being called first.
278 */
279/* static */
280NativeEventQueue* NativeEventQueue::getMainEventQueue()
281{
282 return sMainQueue;
283}
284
285#ifdef VBOX_WITH_XPCOM
286# ifdef RT_OS_DARWIN
287/**
288 * Wait for events and process them (Darwin).
289 *
290 * @retval VINF_SUCCESS
291 * @retval VERR_TIMEOUT
292 * @retval VERR_INTERRUPTED
293 *
294 * @param cMsTimeout How long to wait, or RT_INDEFINITE_WAIT.
295 */
296static int waitForEventsOnDarwin(RTMSINTERVAL cMsTimeout)
297{
298 /*
299 * Wait for the requested time, if we get a hit we do a poll to process
300 * any other pending messages.
301 *
302 * Note! About 1.0e10: According to the sources anything above 3.1556952e+9
303 * means indefinite wait and 1.0e10 is what CFRunLoopRun() uses.
304 */
305 CFTimeInterval rdTimeout = cMsTimeout == RT_INDEFINITE_WAIT ? 1e10 : (double)cMsTimeout / 1000;
306 OSStatus orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/);
307 if (orc == kCFRunLoopRunHandledSource)
308 {
309 OSStatus orc2 = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
310 if ( orc2 == kCFRunLoopRunStopped
311 || orc2 == kCFRunLoopRunFinished)
312 orc = orc2;
313 }
314 if ( orc == 0 /*???*/
315 || orc == kCFRunLoopRunHandledSource)
316 return VINF_SUCCESS;
317 if ( orc == kCFRunLoopRunStopped
318 || orc == kCFRunLoopRunFinished)
319 return VERR_INTERRUPTED;
320 AssertMsg(orc == kCFRunLoopRunTimedOut, ("Unexpected status code from CFRunLoopRunInMode: %#x", orc));
321 return VERR_TIMEOUT;
322}
323# else // !RT_OS_DARWIN
324
325/**
326 * Wait for events (generic XPCOM).
327 *
328 * @retval VINF_SUCCESS
329 * @retval VERR_TIMEOUT
330 * @retval VINF_INTERRUPTED
331 * @retval VERR_INTERNAL_ERROR_4
332 *
333 * @param pQueue The queue to wait on.
334 * @param cMsTimeout How long to wait, or RT_INDEFINITE_WAIT.
335 */
336static int waitForEventsOnXPCOM(nsIEventQueue *pQueue, RTMSINTERVAL cMsTimeout)
337{
338 int fd = pQueue->GetEventQueueSelectFD();
339 fd_set fdsetR;
340 FD_ZERO(&fdsetR);
341 FD_SET(fd, &fdsetR);
342
343 fd_set fdsetE = fdsetR;
344
345 struct timeval tv = {0,0};
346 struct timeval *ptv;
347 if (cMsTimeout == RT_INDEFINITE_WAIT)
348 ptv = NULL;
349 else
350 {
351 tv.tv_sec = cMsTimeout / 1000;
352 tv.tv_usec = (cMsTimeout % 1000) * 1000;
353 ptv = &tv;
354 }
355
356 int iRc = select(fd + 1, &fdsetR, NULL, &fdsetE, ptv);
357 int vrc;
358 if (iRc > 0)
359 vrc = VINF_SUCCESS;
360 else if (iRc == 0)
361 vrc = VERR_TIMEOUT;
362 else if (errno == EINTR)
363 vrc = VINF_INTERRUPTED;
364 else
365 {
366 static uint32_t s_ErrorCount = 0;
367 if (s_ErrorCount < 500)
368 {
369 LogRel(("waitForEventsOnXPCOM iRc=%d errno=%d\n", iRc, errno));
370 ++s_ErrorCount;
371 }
372
373 AssertMsgFailed(("iRc=%d errno=%d\n", iRc, errno));
374 vrc = VERR_INTERNAL_ERROR_4;
375 }
376 return vrc;
377}
378
379# endif // !RT_OS_DARWIN
380#endif // VBOX_WITH_XPCOM
381
382#ifndef VBOX_WITH_XPCOM
383
384/**
385 * Dispatch a message on Windows.
386 *
387 * This will pick out our events and handle them specially.
388 *
389 * @returns @a vrc or VERR_INTERRUPTED (WM_QUIT or NULL msg).
390 * @param pMsg The message to dispatch.
391 * @param vrc The current status code.
392 */
393/*static*/
394int NativeEventQueue::dispatchMessageOnWindows(MSG const *pMsg, int vrc)
395{
396 /*
397 * Check for and dispatch our events.
398 */
399 if ( pMsg->hwnd == NULL
400 && pMsg->message == WM_USER)
401 {
402 if (pMsg->lParam == EVENTQUEUE_WIN_LPARAM_MAGIC)
403 {
404 NativeEvent *pEvent = (NativeEvent *)pMsg->wParam;
405 if (pEvent)
406 {
407 pEvent->handler();
408 delete pEvent;
409 }
410 else
411 vrc = VERR_INTERRUPTED;
412 return vrc;
413 }
414 AssertMsgFailed(("lParam=%p wParam=%p\n", pMsg->lParam, pMsg->wParam));
415 }
416
417 /*
418 * Check for the quit message and dispatch the message the normal way.
419 */
420 if (pMsg->message == WM_QUIT)
421 vrc = VERR_INTERRUPTED;
422 TranslateMessage(pMsg);
423 DispatchMessage(pMsg);
424
425 return vrc;
426}
427
428
429/**
430 * Process pending events (Windows).
431 *
432 * @retval VINF_SUCCESS
433 * @retval VERR_TIMEOUT
434 * @retval VERR_INTERRUPTED.
435 */
436static int processPendingEvents(void)
437{
438 int vrc = VERR_TIMEOUT;
439 MSG Msg;
440 if (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE))
441 {
442 vrc = VINF_SUCCESS;
443 do
444 vrc = NativeEventQueue::dispatchMessageOnWindows(&Msg, vrc);
445 while (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE));
446 }
447 return vrc;
448}
449
450#else // VBOX_WITH_XPCOM
451
452/**
453 * Process pending XPCOM events.
454 * @param pQueue The queue to process events on.
455 * @retval VINF_SUCCESS
456 * @retval VERR_TIMEOUT
457 * @retval VERR_INTERRUPTED (darwin only)
458 * @retval VERR_INTERNAL_ERROR_2
459 */
460static int processPendingEvents(nsIEventQueue *pQueue)
461{
462 /* ProcessPendingEvents doesn't report back what it did, so check here. */
463 PRBool fHasEvents = PR_FALSE;
464 nsresult hrc = pQueue->PendingEvents(&fHasEvents);
465 if (NS_FAILED(hrc))
466 return VERR_INTERNAL_ERROR_2;
467
468 /* Process pending events. */
469 int vrc = VINF_SUCCESS;
470 if (fHasEvents)
471 pQueue->ProcessPendingEvents();
472 else
473 vrc = VERR_TIMEOUT;
474
475# ifdef RT_OS_DARWIN
476 /* Process pending native events. */
477 int vrc2 = waitForEventsOnDarwin(0);
478 if (vrc == VERR_TIMEOUT || vrc2 == VERR_INTERRUPTED)
479 vrc = vrc2;
480# endif
481
482 return vrc;
483}
484
485#endif // VBOX_WITH_XPCOM
486
487/**
488 * Process events pending on this event queue, and wait up to given timeout, if
489 * nothing is available.
490 *
491 * Must be called on same thread this event queue was created on.
492 *
493 * @param cMsTimeout The timeout specified as milliseconds. Use
494 * RT_INDEFINITE_WAIT to wait till an event is posted on the
495 * queue.
496 *
497 * @returns VBox status code
498 * @retval VINF_SUCCESS if one or more messages was processed.
499 * @retval VERR_TIMEOUT if cMsTimeout expired.
500 * @retval VERR_INVALID_CONTEXT if called on the wrong thread.
501 * @retval VERR_INTERRUPTED if interruptEventQueueProcessing was called.
502 * On Windows will also be returned when WM_QUIT is encountered.
503 * On Darwin this may also be returned when the native queue is
504 * stopped or destroyed/finished.
505 * @retval VINF_INTERRUPTED if the native system call was interrupted by a
506 * an asynchronous event delivery (signal) or just felt like returning
507 * out of bounds. On darwin it will also be returned if the queue is
508 * stopped.
509 *
510 * @note On darwin this function will not return when the thread receives a
511 * signal, it will just resume the wait.
512 */
513int NativeEventQueue::processEventQueue(RTMSINTERVAL cMsTimeout)
514{
515 int vrc;
516 CHECK_THREAD_RET(VERR_INVALID_CONTEXT);
517
518#ifdef VBOX_WITH_XPCOM
519 /*
520 * Process pending events, if none are available and we're not in a
521 * poll call, wait for some to appear. (We have to be a little bit
522 * careful after waiting for the events since Darwin will process
523 * them as part of the wait, while the XPCOM case will not.)
524 *
525 * Note! Unfortunately, WaitForEvent isn't interruptible with Ctrl-C,
526 * while select() is. So we cannot use it for indefinite waits.
527 */
528 vrc = processPendingEvents(mEventQ);
529 if ( vrc == VERR_TIMEOUT
530 && cMsTimeout > 0)
531 {
532# ifdef RT_OS_DARWIN
533 /** @todo check how Ctrl-C works on Darwin.
534 * Update: It doesn't work. MACH_RCV_INTERRUPT could perhaps be returned
535 * to __CFRunLoopServiceMachPort, but neither it nor __CFRunLoopRun
536 * has any way of expressing it via their return values. So, if
537 * Ctrl-C handling is important, signal needs to be handled on
538 * a different thread or something. */
539 vrc = waitForEventsOnDarwin(cMsTimeout);
540# else // !RT_OS_DARWIN
541 vrc = waitForEventsOnXPCOM(mEventQ, cMsTimeout);
542# endif // !RT_OS_DARWIN
543 if ( RT_SUCCESS(vrc)
544 || vrc == VERR_TIMEOUT)
545 {
546 int vrc2 = processPendingEvents(mEventQ);
547 /* If the wait was successful don't fail the whole operation. */
548 if (RT_FAILURE(vrc) && RT_FAILURE(vrc2))
549 vrc = vrc2;
550 }
551 }
552
553 if ( ( RT_SUCCESS(vrc)
554 || vrc == VERR_INTERRUPTED
555 || vrc == VERR_TIMEOUT)
556 && mInterrupted)
557 {
558 mInterrupted = false;
559 vrc = VERR_INTERRUPTED;
560 }
561
562#else // !VBOX_WITH_XPCOM
563 if (cMsTimeout == RT_INDEFINITE_WAIT)
564 {
565 BOOL fRet = 0; /* Shut up MSC */
566 MSG Msg;
567 vrc = VINF_SUCCESS;
568 while ( vrc != VERR_INTERRUPTED
569 && (fRet = GetMessage(&Msg, NULL /*hWnd*/, WM_USER, WM_USER))
570 && fRet != -1)
571 vrc = NativeEventQueue::dispatchMessageOnWindows(&Msg, vrc);
572 if (fRet == 0)
573 vrc = VERR_INTERRUPTED;
574 else if (fRet == -1)
575 vrc = RTErrConvertFromWin32(GetLastError());
576 }
577 else
578 {
579 vrc = processPendingEvents();
580 if ( vrc == VERR_TIMEOUT
581 && cMsTimeout != 0)
582 {
583 DWORD rcW = MsgWaitForMultipleObjects(1,
584 &mhThread,
585 TRUE /*fWaitAll*/,
586 cMsTimeout,
587 QS_ALLINPUT);
588 AssertMsgReturn(rcW == WAIT_TIMEOUT || rcW == WAIT_OBJECT_0,
589 ("%d\n", rcW),
590 VERR_INTERNAL_ERROR_4);
591 vrc = processPendingEvents();
592 }
593 }
594#endif // !VBOX_WITH_XPCOM
595
596 Assert(vrc != VERR_TIMEOUT || cMsTimeout != RT_INDEFINITE_WAIT);
597 return vrc;
598}
599
600/**
601 * Interrupt thread waiting on event queue processing.
602 *
603 * Can be called on any thread.
604 *
605 * @returns VBox status code.
606 */
607int NativeEventQueue::interruptEventQueueProcessing()
608{
609 /* Send a NULL event. This event will be picked up and handled specially
610 * both for XPCOM and Windows. It is the responsibility of the caller to
611 * take care of not running the loop again in a way which will hang. */
612 postEvent(NULL);
613 return VINF_SUCCESS;
614}
615
616/**
617 * Posts an event to this event loop asynchronously.
618 *
619 * @param pEvent the event to post, must be allocated using |new|
620 * @return @c TRUE if successful and false otherwise
621 */
622BOOL NativeEventQueue::postEvent(NativeEvent *pEvent)
623{
624#ifndef VBOX_WITH_XPCOM
625 /* Note! The event == NULL case is duplicated in vboxapi/PlatformMSCOM::interruptWaitEvents(). */
626 BOOL fRc = PostThreadMessage(mThreadId, WM_USER, (WPARAM)pEvent, EVENTQUEUE_WIN_LPARAM_MAGIC);
627 if (!fRc)
628 {
629 static int s_cBitchedAboutFullNativeEventQueue = 0;
630 if ( GetLastError() == ERROR_NOT_ENOUGH_QUOTA
631 && s_cBitchedAboutFullNativeEventQueue < 10)
632 LogRel(("Warning: Asynchronous event queue (%p, thread %RI32) full, event (%p) not delivered (%d/10)\n",
633 this, mThreadId, pEvent, ++s_cBitchedAboutFullNativeEventQueue));
634 else
635 AssertFailed();
636 }
637 return fRc;
638#else // VBOX_WITH_XPCOM
639 if (!mEventQ)
640 return FALSE;
641
642 try
643 {
644 MyPLEvent *pMyEvent = new MyPLEvent(pEvent);
645 mEventQ->InitEvent(pMyEvent, this, com::NativeEventQueue::plEventHandler,
646 com::NativeEventQueue::plEventDestructor);
647 HRESULT hrc = mEventQ->PostEvent(pMyEvent);
648 return NS_SUCCEEDED(hrc);
649 }
650 catch (std::bad_alloc &ba)
651 {
652 AssertMsgFailed(("Out of memory while allocating memory for event=%p: %s\n",
653 pEvent, ba.what()));
654 }
655
656 return FALSE;
657#endif // VBOX_WITH_XPCOM
658}
659
660/**
661 * Get select()'able selector for this event queue.
662 * This will return -1 on platforms and queue variants not supporting such
663 * functionality.
664 */
665int NativeEventQueue::getSelectFD()
666{
667#ifdef VBOX_WITH_XPCOM
668 return mEventQ->GetEventQueueSelectFD();
669#else
670 return -1;
671#endif
672}
673
674}
675/* namespace com */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use