VirtualBox

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

Last change on this file since 22911 was 22911, checked in by vboxsync, 16 years ago

event queues cleaned up

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.4 KB
Line 
1/* $Id: EventQueue.cpp 22911 2009-09-10 12:02:36Z 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 OSStatus orc = -1;
182 CFTimeInterval rdTimeout = (double)cMsTimeout / 1000;
183 orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/);
184 if (orc == kCFRunLoopRunHandledSource)
185 orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
186 if (!orc || orc == kCFRunLoopRunHandledSource)
187 return VINF_SUCCESS;
188
189 if (orc != kCFRunLoopRunTimedOut)
190 {
191 NS_WARNING("Unexpected status code from CFRunLoopRunInMode");
192 }
193
194 return VERR_TIMEOUT;
195}
196#endif
197
198#ifdef RT_OS_WINDOWS
199static int
200processPendingEvents()
201{
202 MSG Msg;
203 int rc = VERR_TIMEOUT;
204 while (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE))
205 {
206 if (Msg.message == WM_QUIT)
207 rc = VERR_INTERRUPTED;
208 DispatchMessage(&Msg);
209 if (rc == VERR_INTERRUPTED)
210 break;
211 rc = VINF_SUCCESS;
212 }
213 return rc;
214}
215/** For automatic cleanup, */
216class MyThreadHandle
217{
218public:
219 HANDLE mh;
220
221 MyThreadHandle()
222 {
223 if (!DuplicateHandle(GetCurrentProcess(),
224 GetCurrentThread(),
225 GetCurrentProcess(),
226 &mh,
227 0 /*dwDesiredAccess*/,
228 FALSE /*bInheritHandle*/,
229 DUPLICATE_SAME_ACCESS))
230 mh = INVALID_HANDLE_VALUE;
231 }
232
233 ~MyThreadHandle()
234 {
235 CloseHandle(mh);
236 mh = INVALID_HANDLE_VALUE;
237 }
238};
239#else
240static int
241processPendingEvents(nsIEventQueue* pQueue)
242{
243 /** @todo: rethink interruption events, current NULL event approach is bad */
244 pQueue->ProcessPendingEvents();
245 return VINF_SUCCESS;
246}
247#endif
248int EventQueue::processEventQueue(uint32_t cMsTimeout)
249{
250 int rc = VINF_SUCCESS;
251 /** @todo: check that current thread == one we were created on */
252#if defined (VBOX_WITH_XPCOM)
253 do {
254 PRBool fHasEvents = PR_FALSE;
255 nsresult rc;
256
257 rc = mEventQ->PendingEvents(&fHasEvents);
258 if (NS_FAILED (rc))
259 return VERR_INTERNAL_ERROR_3;
260
261 if (fHasEvents || cMsTimeout == 0)
262 break;
263
264 /**
265 * Unfortunately, WaitForEvent isn't interruptible with Ctrl-C,
266 * while select() is.
267 */
268
269 if (cMsTimeout == RT_INDEFINITE_WAIT)
270 {
271#if 0
272 PLEvent *pEvent = NULL;
273 int rc1 = mEventQ->WaitForEvent(&pEvent);
274 if (NS_FAILED(rc1) || pEvent == NULL)
275 {
276 rc = VERR_INTERRUPTED;
277 break;
278 }
279 mEventQ->HandleEvent(pEvent);
280 break;
281#else
282 /* Pretty close to forever */
283 cMsTimeout = 0xffff0000;
284#endif
285 }
286
287 /* Bit tricky part - perform timed wait */
288# ifdef RT_OS_DARWIN
289 rc = timedWaitForEventsOnDarwin(mEventQ, cMsTimeout);
290# else
291 int fd = mEventQ->GetEventQueueSelectFD();
292 fd_set fdsetR, fdsetE;
293 struct timeval tv;
294
295 FD_ZERO(&fdsetR);
296 FD_SET(fd, &fdsetR);
297
298 fdsetE = fdsetR;
299 tv.tv_sec = (PRInt64)cMsTimeout / 1000;
300 tv.tv_usec = ((PRInt64)cMsTimeout % 1000) * 1000;
301
302 int aCode = select(fd + 1, &fdsetR, NULL, &fdsetE, &tv);
303 if (aCode == 0)
304 rc = VERR_TIMEOUT;
305 else if (aCode == EINTR)
306 rc = VERR_INTERRUPTED;
307 else if (aCode < 0)
308 rc = VERR_INTERNAL_ERROR_4;
309
310# endif
311 } while (0);
312
313 rc = processPendingEvents(mEventQ);
314#else /* Windows */
315 do {
316 int aCode = processPendingEventsOnWindows();
317 if (aCode != VERR_TIMEOUT || cMsTimeout == 0)
318 {
319 rc = aCode;
320 break;
321 }
322
323 if (cMsTimeout == RT_INDEFINITE_WAIT)
324 {
325 Event* aEvent = NULL;
326
327 BOOL fHasEvent = waitForEvent(&aEvent);
328 if (fHasEvent)
329 handleEvent(aEvent);
330 else
331 rc = VERR_INTERRUPTED;
332 break;
333 }
334
335 /* Perform timed wait */
336 MyThreadHandle aHandle;
337
338 DWORD aCode2 = MsgWaitForMultipleObjects(1, &aHandle.mh,
339 TRUE /*fWaitAll*/,
340 0 /*ms*/,
341 QS_ALLINPUT);
342 if (aCode2 == WAIT_TIMEOUT)
343 rc = VERR_TIMEOUT;
344 else if (aCode2 == WAIT_OBJECT_0)
345 rc = VINF_SUCCESS;
346 else
347 rc = VERR_INTERNAL_ERROR_4;
348 } while (0);
349
350 rc = processPendingEvents();
351#endif
352 return rc;
353}
354
355int EventQueue::interruptEventQueueProcessing()
356{
357 /** @todo: rethink me! */
358 postEvent(NULL);
359 return VINF_SUCCESS;
360}
361
362/**
363 * Posts an event to this event loop asynchronously.
364 *
365 * @param event the event to post, must be allocated using |new|
366 * @return TRUE if successful and false otherwise
367 */
368BOOL EventQueue::postEvent (Event *event)
369{
370#if defined (RT_OS_WINDOWS)
371
372 return PostThreadMessage (mThreadId, WM_USER, (WPARAM) event, NULL);
373
374#else
375
376 if (!mEventQ)
377 return FALSE;
378
379 MyPLEvent *ev = new MyPLEvent (event);
380 mEventQ->InitEvent (ev, this, plEventHandler, plEventDestructor);
381 HRESULT rc = mEventQ->PostEvent (ev);
382 return NS_SUCCEEDED(rc);
383
384#endif
385}
386
387/**
388 * Waits for a single event.
389 * This method must be called on the same thread where this event queue
390 * is created.
391 *
392 * After this method returns TRUE and non-NULL event, the caller should call
393 * #handleEvent() in order to process the returned event (otherwise the event
394 * is just removed from the queue, but not processed).
395 *
396 * There is a special case when the returned event is NULL (and the method
397 * returns TRUE), meaning that this event queue must finish its execution
398 * (i.e., quit the event loop),
399 *
400 * @param event next event removed from the queue
401 * @return TRUE if successful and false otherwise
402 */
403BOOL EventQueue::waitForEvent (Event **event)
404{
405 Assert (event);
406 if (!event)
407 return FALSE;
408
409 *event = NULL;
410
411 CHECK_THREAD_RET (FALSE);
412
413#if defined (RT_OS_WINDOWS)
414
415 MSG msg;
416 BOOL rc = GetMessage (&msg, NULL, WM_USER, WM_USER);
417 // check for error
418 if (rc == -1)
419 return FALSE;
420 // check for WM_QUIT
421 if (!rc)
422 return TRUE;
423
424 // retrieve our event
425 *event = (Event *) msg.wParam;
426
427#else
428
429 PLEvent *ev = NULL;
430 HRESULT rc;
431
432 mGotEvent = FALSE;
433
434 do
435 {
436 rc = mEventQ->WaitForEvent (&ev);
437 // check for error
438 if (FAILED (rc))
439 return FALSE;
440 // check for EINTR signal
441 if (!ev)
442 return TRUE;
443
444 // run PLEvent handler. This will just set mLastEvent if it is an
445 // MyPLEvent instance, and then delete ev.
446 mEventQ->HandleEvent (ev);
447 }
448 while (!mGotEvent);
449
450 // retrieve our event
451 *event = mLastEvent;
452
453#endif
454
455 return TRUE;
456}
457
458/**
459 * Handles the given event and |delete|s it.
460 * This method must be called on the same thread where this event queue
461 * is created.
462 */
463BOOL EventQueue::handleEvent (Event *event)
464{
465 Assert (event);
466 if (!event)
467 return FALSE;
468
469 CHECK_THREAD_RET (FALSE);
470
471 event->handler();
472 delete event;
473
474 return TRUE;
475}
476
477int EventQueue::getSelectFD()
478{
479#ifdef VBOX_WITH_XPCOM
480 return mEventQ->GetEventQueueSelectFD();
481#else
482 return -1;
483#endif
484}
485}
486/* namespace com */
487
Note: See TracBrowser for help on using the repository browser.

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