Changeset 31579 in vbox
- Timestamp:
- Aug 11, 2010 5:21:27 PM (15 years ago)
- svn:sync-xref-src-repo-rev:
- 64686
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/VBox/com/EventQueue.h
r31372 r31579 5 5 6 6 /* 7 * Copyright (C) 2006-20 07Oracle Corporation7 * Copyright (C) 2006-2010 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 28 28 #define ___VBox_com_EventQueue_h 29 29 30 #if !defined (VBOX_WITH_XPCOM)30 #ifndef VBOX_WITH_XPCOM 31 31 # include <Windows.h> 32 #else 32 #else // VBOX_WITH_XPCOM 33 33 # include <nsEventQueueUtils.h> 34 #endif 34 #endif // VBOX_WITH_XPCOM 35 35 36 36 #include <VBox/com/defs.h> … … 43 43 44 44 /** 45 * Base class for all events. Intended to be subclassed to introduce new events46 * and handlers for them.45 * Base class for all events. Intended to be subclassed to introduce new 46 * events and handlers for them. 47 47 * 48 48 * Subclasses usually reimplement virtual #handler() (that does nothing by … … 74 74 * When using XPCOM, this will map onto the default XPCOM queue for the thread. 75 75 * So, if a queue is created on the main thread, it automatically processes 76 * XPCOM/IPC events while waiting for its own (Event) events.76 * XPCOM/IPC events while waiting. 77 77 * 78 78 * When using Windows, Darwin and OS/2, this will map onto the native thread 79 79 * queue/runloop. So, windows messages and what not will be processed while 80 80 * waiting for events. 81 * 82 * @note It is intentional that there is no way to retrieve arbitrary 83 * events and controlling their processing. There is no use case which 84 * warrants introducing the complexity of platform independent events. 81 85 */ 82 86 class EventQueue … … 87 91 ~EventQueue(); 88 92 89 BOOL postEvent (Event *event); 90 BOOL waitForEvent (Event **event); 91 BOOL handleEvent (Event *event); 93 BOOL postEvent(Event *event); 92 94 int processEventQueue(uint32_t cMsTimeout); 93 95 int interruptEventQueueProcessing(); … … 107 109 static EventQueue *mMainQueue; 108 110 109 #if !defined (VBOX_WITH_XPCOM)111 #ifndef VBOX_WITH_XPCOM 110 112 111 113 /** The thread which the queue belongs to. */ … … 114 116 HANDLE mhThread; 115 117 116 #else 118 #else // VBOX_WITH_XPCOM 117 119 118 120 /** Whether it was created (and thus needs destroying) or if a queue already … … 120 122 BOOL mEQCreated; 121 123 124 /** Whether event processing should be interrupted. */ 125 BOOL mInterrupted; 126 122 127 nsCOMPtr <nsIEventQueue> mEventQ; 123 128 nsCOMPtr <nsIEventQueueService> mEventQService; 124 129 125 Event *mLastEvent;126 BOOL mGotEvent;130 static void *PR_CALLBACK plEventHandler(PLEvent *self); 131 static void PR_CALLBACK plEventDestructor(PLEvent *self); 127 132 128 struct MyPLEvent : public PLEvent 129 { 130 MyPLEvent (Event *e) : event (e) {} 131 Event *event; 132 }; 133 134 static void * PR_CALLBACK plEventHandler (PLEvent* self) 135 { 136 // nsIEventQueue doesn't expose PL_GetEventOwner(), so use an internal 137 // field of PLEvent directly (hackish, but doesn' require an extra lib) 138 EventQueue *owner = (EventQueue *) self->owner; 139 Assert (owner); 140 owner->mLastEvent = ((MyPLEvent *) self)->event; 141 owner->mGotEvent = TRUE; 142 return 0; 143 } 144 145 static void PR_CALLBACK plEventDestructor (PLEvent* self) { delete self; } 146 147 #endif 133 #endif // VBOX_WITH_XPCOM 148 134 }; 149 135 -
trunk/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp
r31539 r31579 83 83 static EventQueue *gEventQ = NULL; 84 84 85 /* flag whether frontend should terminate */ 86 static volatile bool g_fTerminateFE = false; 87 85 88 #ifdef VBOX_WITH_VNC 86 89 static VNCFB *g_pFramebufferVNC; … … 89 92 90 93 //////////////////////////////////////////////////////////////////////////////// 91 92 /**93 * State change event.94 */95 class StateChangeEvent : public Event96 {97 public:98 StateChangeEvent(MachineState_T state) : mState(state) {}99 protected:100 void *handler()101 {102 LogFlow(("VBoxHeadless: StateChangeEvent: %d\n", mState));103 /* post the termination event if the machine has been PoweredDown/Saved/Aborted */104 if (mState < MachineState_Running)105 gEventQ->postEvent(NULL);106 return 0;107 }108 private:109 MachineState_T mState;110 };111 94 112 95 /** … … 319 302 scev->COMGETTER(State)(&machineState); 320 303 321 gEventQ->postEvent(new StateChangeEvent(machineState)); 304 /* Terminate any event wait operation if the machine has been 305 * PoweredDown/Saved/Aborted. */ 306 if (machineState < MachineState_Running) 307 { 308 g_fTerminateFE = true; 309 gEventQ->interruptEventQueueProcessing(); 310 } 311 322 312 break; 323 313 } … … 1088 1078 break; 1089 1079 1090 /* Process pending events, then wait for new ones. */ 1080 /* Process pending events, then wait for new ones. Note, this 1081 * processes NULL events signalling event loop termination. */ 1091 1082 gEventQ->processEventQueue(0); 1092 gEventQ->processEventQueue(500); 1093 } 1094 1095 /** @todo The error handling here is kind of peculiar, anyone care 1096 * to comment why this works just fine? (this is old the code) */ 1083 if (!g_fTerminateFE) 1084 gEventQ->processEventQueue(500); 1085 } 1086 1097 1087 if (SUCCEEDED(progress->WaitForCompletion(-1))) 1098 1088 { 1089 /* Figure out if the operation completed with a failed status 1090 * and print the error message. Terminate immediately, and let 1091 * the cleanup code take care of potentially pending events. */ 1099 1092 LONG progressRc; 1100 1093 progress->COMGETTER(ResultCode)(&progressRc); 1101 1094 rc = progressRc; 1102 if (FAILED( progressRc))1095 if (FAILED(rc)) 1103 1096 { 1104 1097 com::ProgressErrorInfo info(progress); … … 1111 1104 RTPrintf("Error: failed to start machine. No error message available!\n"); 1112 1105 } 1106 break; 1113 1107 } 1114 1108 } … … 1132 1126 Log(("VBoxHeadless: Waiting for PowerDown...\n")); 1133 1127 1134 Event *e; 1135 1136 while (gEventQ->waitForEvent(&e) && e) 1137 gEventQ->handleEvent(e); 1128 while ( !g_fTerminateFE 1129 && RT_SUCCESS(gEventQ->processEventQueue(RT_INDEFINITE_WAIT))) 1130 /* nothing */ ; 1138 1131 1139 1132 Log(("VBoxHeadless: event loop has terminated...\n")); -
trunk/src/VBox/Main/VirtualBoxImpl.cpp
r31568 r31579 711 711 { 712 712 /* signal to exit the event loop */ 713 if ( m->pAsyncEventQ->postEvent(NULL))713 if (RT_SUCCESS(m->pAsyncEventQ->interruptEventQueueProcessing())) 714 714 { 715 715 /* 716 * Wait for thread termination (only if we've successfully posted717 * a NULL event!)716 * Wait for thread termination (only after we've successfully 717 * interrupted the event queue processing!) 718 718 */ 719 719 int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL); … … 724 724 else 725 725 { 726 AssertMsgFailed((" postEvent(NULL) failed\n"));726 AssertMsgFailed(("interruptEventQueueProcessing() failed\n")); 727 727 RTThreadWait(m->threadAsyncEvent, 0, NULL); 728 728 } … … 4076 4076 RTThreadUserSignal(thread); 4077 4077 4078 BOOL ok = TRUE; 4079 Event *event = NULL; 4080 4081 while ((ok = eventQ->waitForEvent(&event)) && event) 4082 eventQ->handleEvent(event); 4083 4084 AssertReturn(ok, VERR_GENERAL_FAILURE); 4078 while (RT_SUCCESS(eventQ->processEventQueue(RT_INDEFINITE_WAIT))) 4079 /* nothing */ ; 4085 4080 4086 4081 delete eventQ; -
trunk/src/VBox/Main/glue/EventQueue.cpp
r31371 r31579 8 8 9 9 /* 10 * Copyright (C) 2006-20 07Oracle Corporation10 * Copyright (C) 2006-2010 Oracle Corporation 11 11 * 12 12 * This file is part of VirtualBox Open Source Edition (OSE), as … … 42 42 //////////////////////////////////////////////////////////////////////////////// 43 43 44 #if defined (RT_OS_WINDOWS)44 #ifndef VBOX_WITH_XPCOM 45 45 46 46 #define CHECK_THREAD_RET(ret) \ 47 47 do { \ 48 AssertMsg 48 AssertMsg(GetCurrentThreadId() == mThreadId, ("Must be on event queue thread!")); \ 49 49 if (GetCurrentThreadId() != mThreadId) \ 50 50 return ret; \ 51 51 } while (0) 52 52 53 #else // !defined (RT_OS_WINDOWS)53 #else // VBOX_WITH_XPCOM 54 54 55 55 #define CHECK_THREAD_RET(ret) \ … … 58 58 return ret; \ 59 59 BOOL isOnCurrentThread = FALSE; \ 60 mEventQ->IsOnCurrentThread 61 AssertMsg 60 mEventQ->IsOnCurrentThread(&isOnCurrentThread); \ 61 AssertMsg(isOnCurrentThread, ("Must be on event queue thread!")); \ 62 62 if (!isOnCurrentThread) \ 63 63 return ret; \ 64 64 } while (0) 65 65 66 #endif // !defined (RT_OS_WINDOWS)66 #endif // VBOX_WITH_XPCOM 67 67 68 68 EventQueue *EventQueue::mMainQueue = NULL; 69 70 #ifdef VBOX_WITH_XPCOM 71 struct MyPLEvent : public PLEvent 72 { 73 MyPLEvent(Event *e) : event(e) {} 74 Event *event; 75 }; 76 77 /* static */ 78 void *PR_CALLBACK com::EventQueue::plEventHandler(PLEvent *self) 79 { 80 Event *ev = ((MyPLEvent *)self)->event; 81 if (ev) 82 ev->handler(); 83 else 84 { 85 EventQueue *eq = (EventQueue *)self->owner; 86 Assert(eq); 87 eq->mInterrupted = true; 88 } 89 return NULL; 90 } 91 92 /* static */ 93 void PR_CALLBACK com::EventQueue::plEventDestructor(PLEvent *self) 94 { 95 Event *ev = ((MyPLEvent *)self)->event; 96 if (ev) 97 delete ev; 98 delete self; 99 } 100 101 #endif // VBOX_WITH_XPCOM 69 102 70 103 /** … … 77 110 EventQueue::EventQueue() 78 111 { 79 #if defined (RT_OS_WINDOWS)112 #ifndef VBOX_WITH_XPCOM 80 113 81 114 mThreadId = GetCurrentThreadId(); 82 115 // force the system to create the message queue for the current thread 83 116 MSG msg; 84 PeekMessage 85 86 if (!DuplicateHandle 87 88 89 90 91 92 117 PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); 118 119 if (!DuplicateHandle(GetCurrentProcess(), 120 GetCurrentThread(), 121 GetCurrentProcess(), 122 &mhThread, 123 0 /*dwDesiredAccess*/, 124 FALSE /*bInheritHandle*/, 125 DUPLICATE_SAME_ACCESS)) 93 126 mhThread = INVALID_HANDLE_VALUE; 94 127 95 #else 128 #else // VBOX_WITH_XPCOM 96 129 97 130 mEQCreated = FALSE; 98 99 mLastEvent = NULL; 100 mGotEvent = FALSE; 131 mInterrupted = FALSE; 101 132 102 133 // Here we reference the global nsIEventQueueService instance and hold it … … 112 143 // NULL to the event thread (because it stopped accepting events). 113 144 114 nsresult rc = NS_GetEventQueueService (getter_AddRefs(mEventQService));145 nsresult rc = NS_GetEventQueueService(getter_AddRefs(mEventQService)); 115 146 116 147 if (NS_SUCCEEDED(rc)) 117 148 { 118 rc = mEventQService->GetThreadEventQueue 119 getter_AddRefs(mEventQ));149 rc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, 150 getter_AddRefs(mEventQ)); 120 151 if (rc == NS_ERROR_NOT_AVAILABLE) 121 152 { 122 rc = mEventQService->Create MonitoredThreadEventQueue();153 rc = mEventQService->CreateThreadEventQueue(); 123 154 if (NS_SUCCEEDED(rc)) 124 155 { 125 156 mEQCreated = TRUE; 126 rc = mEventQService->GetThreadEventQueue 127 getter_AddRefs(mEventQ));157 rc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, 158 getter_AddRefs(mEventQ)); 128 159 } 129 160 } 130 161 } 131 AssertComRC 132 133 #endif 162 AssertComRC(rc); 163 164 #endif // VBOX_WITH_XPCOM 134 165 } 135 166 136 167 EventQueue::~EventQueue() 137 168 { 138 #if defined (RT_OS_WINDOWS)169 #ifndef VBOX_WITH_XPCOM 139 170 if (mhThread != INVALID_HANDLE_VALUE) 140 171 { 141 CloseHandle 172 CloseHandle(mhThread); 142 173 mhThread = INVALID_HANDLE_VALUE; 143 174 } 144 #else 175 #else // VBOX_WITH_XPCOM 145 176 // process all pending events before destruction 146 177 if (mEventQ) … … 155 186 mEventQService = nsnull; 156 187 } 157 #endif 188 #endif // VBOX_WITH_XPCOM 158 189 } 159 190 … … 175 206 mMainQueue = new EventQueue(); 176 207 177 #if defined (VBOX_WITH_XPCOM)208 #ifdef VBOX_WITH_XPCOM 178 209 /* Check that it actually is the main event queue, i.e. that 179 210 we're called on the right thread. */ … … 187 218 rv = mMainQueue->mEventQ->IsQueueNative(&fIsNative); 188 219 Assert(NS_SUCCEEDED(rv) && fIsNative); 189 #endif 220 #endif // VBOX_WITH_XPCOM 190 221 191 222 return VINF_SUCCESS; … … 200 231 { 201 232 Assert(mMainQueue); 233 /* Must process all events to make sure that no NULL event is left 234 * after this point. It would need to modify the state of mMainQueue. */ 235 mMainQueue->processEventQueue(0); 202 236 delete mMainQueue; 203 237 mMainQueue = NULL; … … 216 250 } 217 251 218 #ifdef RT_OS_DARWIN 252 #ifdef VBOX_WITH_XPCOM 253 # ifdef RT_OS_DARWIN 219 254 /** 220 255 * Wait for events and process them (Darwin). … … 249 284 return VERR_TIMEOUT; 250 285 } 251 # elif !defined(RT_OS_WINDOWS)252 253 /** 254 * Wait for events ( Unix).286 # else // !RT_OS_DARWIN 287 288 /** 289 * Wait for events (generic XPCOM). 255 290 * 256 291 * @returns VINF_SUCCESS or VERR_TIMEOUT. … … 260 295 */ 261 296 static 262 int waitForEventsOn Unix(nsIEventQueue *pQueue, unsigned cMsTimeout)297 int waitForEventsOnXPCOM(nsIEventQueue *pQueue, unsigned cMsTimeout) 263 298 { 264 299 int fd = pQueue->GetEventQueueSelectFD(); … … 295 330 } 296 331 297 #endif 298 299 #ifdef RT_OS_WINDOWS 332 # endif // !RT_OS_DARWIN 333 #endif // VBOX_WITH_XPCOM 334 335 #ifndef VBOX_WITH_XPCOM 300 336 /** 301 337 * Process pending events (Windows). … … 318 354 return rc; 319 355 } 320 #else /* !RT_OS_WINDOWS */356 #else // VBOX_WITH_XPCOM 321 357 /** 322 358 * Process pending XPCOM events. … … 335 371 return VERR_TIMEOUT; 336 372 337 /** @todo: rethink interruption events, current NULL event approach is bad */338 373 pQueue->ProcessPendingEvents(); 339 374 return VINF_SUCCESS; 340 375 } 341 #endif / * !RT_OS_WINDOWS */376 #endif // VBOX_WITH_XPCOM 342 377 343 378 … … 362 397 CHECK_THREAD_RET(VERR_INVALID_CONTEXT); 363 398 364 #if defined (VBOX_WITH_XPCOM)399 #ifdef VBOX_WITH_XPCOM 365 400 /* 366 401 * Process pending events, if none are available and we're not in a 367 402 * poll call, wait for some to appear. (We have to be a little bit 368 * careful after waiting for the events since darwin will process369 * them as part of the wait, while the unixcase will not.)403 * careful after waiting for the events since Darwin will process 404 * them as part of the wait, while the XPCOM case will not.) 370 405 * 371 406 * Note! Unfortunately, WaitForEvent isn't interruptible with Ctrl-C, … … 377 412 { 378 413 # ifdef RT_OS_DARWIN 379 /** @todo check how Ctrl-C works on darwin. */414 /** @todo check how Ctrl-C works on Darwin. */ 380 415 rc = waitForEventsOnDarwin(cMsTimeout); 381 416 if (rc == VERR_TIMEOUT) 382 417 rc = processPendingEvents(mEventQ); 383 # else 384 rc = waitForEventsOn Unix(mEventQ, cMsTimeout);418 # else // !RT_OS_DARWIN 419 rc = waitForEventsOnXPCOM(mEventQ, cMsTimeout); 385 420 if ( RT_SUCCESS(rc) 386 421 || rc == VERR_TIMEOUT) 387 422 rc = processPendingEvents(mEventQ); 388 # endif 389 } 390 391 #else /* !VBOX_WITH_XPCOM */ 423 # endif // !RT_OS_DARWIN 424 } 425 if (RT_SUCCESS(rc) && mInterrupted) 426 { 427 mInterrupted = false; 428 rc = VERR_INTERRUPTED; 429 } 430 431 #else // !VBOX_WITH_XPCOM 392 432 if (cMsTimeout == RT_INDEFINITE_WAIT) 393 433 { 394 Event *aEvent = NULL;395 396 BOOL fHasEvent = waitForEvent(&aEvent);397 if (fHasEvent)434 BOOL bRet; 435 MSG Msg; 436 int rc = VINF_SUCCESS; 437 while ((bRet = GetMessage(&Msg, NULL /*hWnd*/, WM_USER, WM_USER))) 398 438 { 399 handleEvent(aEvent); 400 rc = processPendingEvents(); 401 if (rc == VERR_TIMEOUT) 402 rc = VINF_SUCCESS; 439 if (bRet != -1) 440 DispatchMessage(&Msg); 403 441 } 404 else442 if (bRet == 0) 405 443 rc = VERR_INTERRUPTED; 406 444 } … … 422 460 } 423 461 } 424 #endif / * !VBOX_WITH_XPCOM */462 #endif // !VBOX_WITH_XPCOM 425 463 return rc; 426 464 } … … 433 471 int EventQueue::interruptEventQueueProcessing() 434 472 { 435 /** @todo: rethink me! */ 473 /* Send a NULL event. This gets us out of the event loop on XPCOM, and 474 * doesn't hurt on Windows. It is the responsibility of the caller to 475 * take care of not running the loop again in a way which will hang. */ 436 476 postEvent(NULL); 437 477 return VINF_SUCCESS; … … 446 486 BOOL EventQueue::postEvent(Event *event) 447 487 { 448 #if defined (RT_OS_WINDOWS)449 450 return PostThreadMessage (mThreadId, WM_USER, (WPARAM)event, NULL);451 452 #else 488 #ifndef VBOX_WITH_XPCOM 489 490 return PostThreadMessage(mThreadId, WM_USER, (WPARAM)event, NULL); 491 492 #else // VBOX_WITH_XPCOM 453 493 454 494 if (!mEventQ) 455 495 return FALSE; 456 496 457 MyPLEvent *ev = new MyPLEvent (event); 458 mEventQ->InitEvent (ev, this, plEventHandler, plEventDestructor); 459 HRESULT rc = mEventQ->PostEvent (ev); 497 MyPLEvent *ev = new MyPLEvent(event); 498 mEventQ->InitEvent(ev, this, com::EventQueue::plEventHandler, 499 com::EventQueue::plEventDestructor); 500 HRESULT rc = mEventQ->PostEvent(ev); 460 501 return NS_SUCCEEDED(rc); 461 502 462 #endif 463 } 464 465 /** 466 * Waits for a single event. 467 * This method must be called on the same thread where this event queue 468 * is created. 469 * 470 * After this method returns TRUE and non-NULL event, the caller should call 471 * #handleEvent() in order to process the returned event (otherwise the event 472 * is just removed from the queue, but not processed). 473 * 474 * There is a special case when the returned event is NULL (and the method 475 * returns TRUE), meaning that this event queue must finish its execution 476 * (i.e., quit the event loop), 477 * 478 * @param event next event removed from the queue 479 * @return TRUE if successful and false otherwise 480 */ 481 BOOL EventQueue::waitForEvent(Event **event) 482 { 483 Assert(event); 484 if (!event) 485 return FALSE; 486 487 *event = NULL; 488 489 CHECK_THREAD_RET (FALSE); 490 491 #if defined (RT_OS_WINDOWS) 492 493 MSG msg; 494 BOOL rc = GetMessage (&msg, NULL, WM_USER, WM_USER); 495 // check for error 496 if (rc == -1) 497 return FALSE; 498 // check for WM_QUIT 499 if (!rc) 500 return TRUE; 501 502 // retrieve our event 503 *event = (Event *) msg.wParam; 504 505 #else 506 507 PLEvent *ev = NULL; 508 HRESULT rc; 509 510 mGotEvent = FALSE; 511 512 do 513 { 514 rc = mEventQ->WaitForEvent (&ev); 515 // check for error 516 if (FAILED(rc)) 517 return FALSE; 518 // check for EINTR signal 519 if (!ev) 520 return TRUE; 521 522 // run PLEvent handler. This will just set mLastEvent if it is an 523 // MyPLEvent instance, and then delete ev. 524 mEventQ->HandleEvent (ev); 525 } 526 while (!mGotEvent); 527 528 // retrieve our event 529 *event = mLastEvent; 530 531 #endif 532 533 return TRUE; 534 } 535 536 /** 537 * Handles the given event and |delete|s it. 538 * This method must be called on the same thread where this event queue 539 * is created. 540 */ 541 BOOL EventQueue::handleEvent(Event *event) 542 { 543 Assert(event); 544 if (!event) 545 return FALSE; 546 547 CHECK_THREAD_RET (FALSE); 548 549 event->handler(); 550 delete event; 551 552 return TRUE; 553 } 503 #endif // VBOX_WITH_XPCOM 504 } 505 554 506 555 507 /** … … 566 518 #endif 567 519 } 520 568 521 } 569 522 /* namespace com */
Note:
See TracChangeset
for help on using the changeset viewer.