Changeset 23092 in vbox
- Timestamp:
- Sep 17, 2009 1:20:18 PM (15 years ago)
- Location:
- trunk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/VBox/com/EventQueue.h
r22911 r23092 33 33 34 34 #if !defined (VBOX_WITH_XPCOM) 35 # include <windows.h>35 # include <Windows.h> 36 36 #else 37 # include <nsEventQueueUtils.h>37 # include <nsEventQueueUtils.h> 38 38 #endif 39 39 … … 77 77 * Simple event queue. 78 78 * 79 * On Linux, if this queue is created on the main thread, it automatically 80 * processes XPCOM/IPC events while waiting for its own (Event) events. 79 * When using XPCOM, this will map onto the default XPCOM queue for the thread. 80 * So, if a queue is created on the main thread, it automatically processes 81 * XPCOM/IPC events while waiting for its own (Event) events. 82 * 83 * When using Windows, Darwin and OS/2, this will map onto the native thread 84 * queue/runloop. So, windows messages and want not will be processed while 85 * waiting for events. 81 86 */ 82 87 class EventQueue … … 90 95 BOOL waitForEvent (Event **event); 91 96 BOOL handleEvent (Event *event); 92 /**93 * Process events pending on this event queue, and wait94 * up to given timeout, if nothing is available.95 * Must be called on same thread this event queue was created on.96 */97 97 int processEventQueue(uint32_t cMsTimeout); 98 /**99 * Interrupt thread waiting on event queue processing.100 * Can be called on any thread.101 */102 98 int interruptEventQueueProcessing(); 103 /**104 * Get select()'able selector for this event queue, can be -1105 * on platforms not supporting such functionality.106 */107 99 int getSelectFD(); 108 /**109 * Initialize/deinitialize event queues.110 */111 100 static int init(); 112 static int deinit(); 113 /** 114 * Get main event queue instance. 115 */ 116 static EventQueue* getMainEventQueue(); 101 static int uninit(); 102 static EventQueue *getMainEventQueue(); 117 103 118 104 private: 119 static EventQueue *mMainQueue;105 static EventQueue *mMainQueue; 120 106 121 107 #if !defined (VBOX_WITH_XPCOM) 122 108 109 /** The thread which the queue belongs to. */ 123 110 DWORD mThreadId; 111 /** Duplicated thread handle for MsgWaitForMultipleObjects. */ 112 HANDLE mhThread; 124 113 125 114 #else 126 115 116 /** Whether it was created (and thus needs destroying) or if a queue already 117 * associated with the thread was used. */ 127 118 BOOL mEQCreated; 128 119 -
trunk/src/VBox/Main/glue/EventQueue.cpp
r22916 r23092 70 70 #endif // !defined (RT_OS_WINDOWS) 71 71 72 EventQueue *EventQueue::mMainQueue = NULL;72 EventQueue *EventQueue::mMainQueue = NULL; 73 73 74 74 /** … … 87 87 MSG msg; 88 88 PeekMessage (&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); 89 90 if (!DuplicateHandle (GetCurrentProcess(), 91 GetCurrentThread(), 92 GetCurrentProcess(), 93 &mhThread, 94 0 /*dwDesiredAccess*/, 95 FALSE /*bInheritHandle*/, 96 DUPLICATE_SAME_ACCESS)) 97 mhThread = INVALID_HANDLE_VALUE; 89 98 90 99 #else … … 132 141 { 133 142 #if defined (RT_OS_WINDOWS) 143 if (mhThread != INVALID_HANDLE_VALUE) 144 { 145 CloseHandle (mhThread); 146 mhThread = INVALID_HANDLE_VALUE; 147 } 134 148 #else 135 149 // process all pending events before destruction … … 148 162 } 149 163 150 /* static */ int EventQueue::init() 151 { 164 /** 165 * Initializes the main event queue instance. 166 * @returns VBox status code. 167 * 168 * @remarks If you're using the rest of the COM/XPCOM glue library, 169 * com::Initialize() will take care of initializing and uninitializing 170 * the EventQueue class. If you don't call com::Initialize, you must 171 * make sure to call this method on the same thread that did the 172 * XPCOM initialization or we'll end up using the wrong main queue. 173 */ 174 /* static */ int 175 EventQueue::init() 176 { 177 Assert(mMainQueue == NULL); 152 178 mMainQueue = new EventQueue(); 179 153 180 #if defined (VBOX_WITH_XPCOM) 181 /* Check that it actually is the main event queue, i.e. that 182 we're called on the right thread. */ 154 183 nsCOMPtr<nsIEventQueue> q; 155 184 nsresult rv = NS_GetMainEventQ(getter_AddRefs(q)); 156 185 Assert(NS_SUCCEEDED(rv)); 157 186 Assert(q == mMainQueue->mEventQ); 187 188 /* Check that it's a native queue. */ 158 189 PRBool fIsNative = PR_FALSE; 159 190 rv = mMainQueue->mEventQ->IsQueueNative(&fIsNative); 160 191 Assert(NS_SUCCEEDED(rv) && fIsNative); 161 192 #endif 193 162 194 return VINF_SUCCESS; 163 195 } 164 196 165 /* static */ int EventQueue::deinit() 197 /** 198 * Uninitialize the global resources (i.e. the main event queue instance). 199 * @returns VINF_SUCCESS 200 */ 201 /* static */ int 202 EventQueue::uninit() 166 203 { 167 204 delete mMainQueue; … … 170 207 } 171 208 172 /* static */ EventQueue* EventQueue::getMainEventQueue() 209 /** 210 * Get main event queue instance. 211 * 212 * Depends on init() being called first. 213 */ 214 /* static */ EventQueue * 215 EventQueue::getMainEventQueue() 173 216 { 174 217 return mMainQueue; … … 176 219 177 220 #ifdef RT_OS_DARWIN 221 /** 222 * Wait for events and process them (Darwin). 223 * 224 * @returns VINF_SUCCESS or VERR_TIMEOUT. 225 * 226 * @param cMsTimeout How long to wait, or RT_INDEFINITE_WAIT. 227 */ 178 228 static int 179 timedWaitForEventsOnDarwin(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 } 229 waitForEventsOnDarwin(unsigned cMsTimeout) 230 { 231 /* 232 * Wait for the requested time, if we get a hit we do a poll to process 233 * any other pending messages. 234 * 235 * Note! About 1.0e10: According to the sources anything above 3.1556952e+9 236 * means indefinite wait and 1.0e10 is what CFRunLoopRun() uses. 237 */ 238 CFTimeInterval rdTimeout = cMsTimeout == RT_INDEFINITE_WAIT ? 1e10 : (double)cMsTimeout / 1000; 239 OSStatus orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/); 240 /** @todo Not entire sure if the poll actually processes more than one message. 241 * Feel free to check the sources anyone. */ 242 if (orc == kCFRunLoopRunHandledSource) 243 orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/); 244 if ( orc == 0 245 || orc == kCFRunLoopRunHandledSource) 246 return VINF_SUCCESS; 247 if ( orc == kCFRunLoopRunStopped 248 || orc == kCFRunLoopRunFinished) 249 return VERR_INTERRUPTED; 250 AssertMsg(orc == kCFRunLoopRunTimedOut, ("Unexpected status code from CFRunLoopRunInMode: %#x", orc)); 251 return VERR_TIMEOUT; 252 } 253 #elif !defined(RT_OS_WINDOWS) 254 255 /** 256 * Wait for events (Unix). 257 * 258 * @returns VINF_SUCCESS or VERR_TIMEOUT. 259 * 260 * @param pQueue The queue to wait on. 261 * @param cMsTimeout How long to wait, or RT_INDEFINITE_WAIT. 262 */ 263 static int 264 waitForEventsOnUnix(nsIEventQueue *pQueue, unsigned cMsTimeout) 265 { 266 int fd = pQueue->GetEventQueueSelectFD(); 267 fd_set fdsetR; 268 FD_ZERO(&fdsetR); 269 FD_SET(fd, &fdsetR); 270 271 fd_set fdsetE = fdsetR; 272 273 struct timeval tv = {0,0}; 274 struct timeval *ptv; 275 if (cMsTimeout == RT_INDEFINITE_WAIT) 276 ptv = NULL; 277 else 278 { 279 tv.tv_sec = cMsTimeout / 1000; 280 tv.tv_usec = (cMsTimeout % 1000) * 1000; 281 ptv = &tv; 282 } 283 284 int rc = select(fd + 1, &fdsetR, NULL, &fdsetE, ptv); 285 if (rc > 0) 286 rc = VINF_SUCCESS; 287 else if (rc == 0) 288 rc = VERR_TIMEOUT; 289 else if (errno == EINTR) 290 rc = VERR_INTERRUPTED; 291 else 292 { 293 AssertMsgFailed(("rc=%d errno=%d\n", rc, errno)); 294 rc = VERR_INTERNAL_ERROR_4; 295 } 296 return rc; 297 } 298 196 299 #endif 197 300 198 301 #ifdef RT_OS_WINDOWS 302 /** 303 * Process pending events (Windows). 304 * @returns VINF_SUCCESS, VERR_TIMEOUT or VERR_INTERRUPTED. 305 */ 199 306 static int 200 processPendingEvents( )307 processPendingEvents(void) 201 308 { 202 309 MSG Msg; … … 213 320 return rc; 214 321 } 215 /** For automatic cleanup, */ 216 class MyThreadHandle 217 { 218 public: 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 322 #else /* !RT_OS_WINDOWS */ 323 /** 324 * Process pending XPCOM events. 325 * @param pQueue The queue to process events on. 326 * @returns VINF_SUCCESS or VERR_TIMEOUT. 327 */ 240 328 static int 241 processPendingEvents(nsIEventQueue* pQueue) 242 { 243 /** @todo: rethink interruption events, current NULL event approach is bad */ 244 pQueue->ProcessPendingEvents(); 245 return VINF_SUCCESS; 246 } 247 #endif 329 processPendingEvents(nsIEventQueue *pQueue) 330 { 331 /* Check for timeout condition so the caller can be a bit more lazy. */ 332 PRBool fHasEvents = PR_FALSE; 333 nsresult hr = pQueue->PendingEvents(&fHasEvents); 334 if (NS_FAILED(hr)) 335 return VERR_INTERNAL_ERROR_2; 336 if (!fHasEvents) 337 return VERR_TIMEOUT; 338 339 /** @todo: rethink interruption events, current NULL event approach is bad */ 340 pQueue->ProcessPendingEvents(); 341 return VINF_SUCCESS; 342 } 343 #endif /* !RT_OS_WINDOWS */ 344 345 346 /** 347 * Process events pending on this event queue, and wait up to given timeout, if 348 * nothing is available. 349 * 350 * Must be called on same thread this event queue was created on. 351 * 352 * @param cMsTimeout The timeout specified as milliseconds. Use 353 * RT_INDEFINITE_WAIT to wait till an event is posted on the 354 * queue. 355 * 356 * @returns VBox status code 357 * @retval VINF_SUCCESS 358 * @retval VERR_TIMEOUT 359 * @retval VERR_INVALID_CONTEXT 360 */ 248 361 int EventQueue::processEventQueue(uint32_t cMsTimeout) 249 362 { 250 int rc = VINF_SUCCESS; 251 /** @todo: check that current thread == one we were created on */ 363 int rc; 364 CHECK_THREAD_RET(VERR_INVALID_CONTEXT); 365 252 366 #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; 367 /* 368 * Process pending events, if none are available and we're not in a 369 * poll call, wait for some to appear. (We have to be a little bit 370 * careful after waiting for the events since darwin will process 371 * them as part of the wait, while the unix case will not.) 372 * 373 * Note! Unfortunately, WaitForEvent isn't interruptible with Ctrl-C, 374 * while select() is. So we cannot use it for indefinite waits. 375 */ 376 rc = processPendingEvents(mEventQ); 377 if ( rc == VERR_TIMEOUT 378 && cMsTimeout > 0) 379 { 380 # ifdef RT_OS_DARWIN 381 /** @todo check how Ctrl-C works on darwin. */ 382 rc = waitForEventsOnDarwin(cMsTimeout); 383 if (rc == VERR_TIMEOUT) 384 rc = processPendingEvents(mEventQ); 385 # else 386 rc = waitForEventsOnUnix(mEventQ, cMsTimeout); 387 if ( RT_SUCCESS(rc) 388 || rc == VERR_TIMEOUT) 389 rc = processPendingEvents(mEventQ); 390 # endif 391 } 392 393 #else /* !VBOX_WITH_XPCOM */ 394 if (cMsTimeout == RT_INDEFINITE_WAIT) 395 { 396 Event *aEvent = NULL; 397 398 BOOL fHasEvent = waitForEvent(&aEvent); 399 if (fHasEvent) 400 { 401 handleEvent(aEvent); 402 rc = processPendingEvents(); 403 if (rc == VERR_TIMEOUT) 404 rc = VINF_SUCCESS; 405 } 406 else 407 rc = VERR_INTERRUPTED; 408 } 409 else 410 { 411 uint64_t const StartTS = RTTimeMilliTS(); 412 for (;;) 413 { 414 rc = processPendingEvents(); 415 if ( rc != VERR_TIMEOUT 416 || cMsTimeout == 0) 277 417 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 = processPendingEvents(); 317 if (aCode != VERR_TIMEOUT || cMsTimeout == 0) 318 { 319 rc = aCode; 320 break; 418 419 uint64_t cMsElapsed = RTTimeMilliTS() - StartTS; 420 if (cMsElapsed >= cMsTimeout) 421 { 422 rc = VERR_TIMEOUT; 423 break; 424 } 425 uint32_t cMsLeft = cMsTimeout - (unsigned)cMsElapsed; 426 DWORD rcW = MsgWaitForMultipleObjects(1, 427 &mhThread, 428 TRUE /*fWaitAll*/, 429 cMsLeft, 430 QS_ALLINPUT); 431 AssertMsgBreakStmt(rcW == WAIT_TIMEOUT || rcW == WAIT_OBJECT_0, 432 ("%d\n", rcW), 433 rc = VERR_INTERNAL_ERROR_4); 321 434 } 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 435 } 436 #endif /* !VBOX_WITH_XPCOM */ 352 437 return rc; 353 438 } 354 439 440 /** 441 * Interrupt thread waiting on event queue processing. 442 * 443 * Can be called on any thread. 444 */ 355 445 int EventQueue::interruptEventQueueProcessing() 356 446 { … … 475 565 } 476 566 567 /** 568 * Get select()'able selector for this event queue. 569 * This will return -1 on platforms and queue variants not supporting such 570 * functionality. 571 */ 477 572 int EventQueue::getSelectFD() 478 573 { -
trunk/src/VBox/Main/glue/initterm.cpp
r22847 r23092 498 498 AssertComRC (rc); 499 499 500 EventQueue::init(); 500 /* 501 * Init the main event queue (ASSUMES it cannot fail). 502 */ 503 if (SUCCEEDED(rc)) 504 EventQueue::init(); 501 505 502 506 return rc; … … 507 511 HRESULT rc = S_OK; 508 512 509 EventQueue:: deinit();513 EventQueue::uninit(); 510 514 511 515 #if !defined (VBOX_WITH_XPCOM)
Note:
See TracChangeset
for help on using the changeset viewer.