VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/ClientWatcher.cpp@ 60061

Last change on this file since 60061 was 60061, checked in by vboxsync, 9 years ago

ClientWatcher: Disabled code for watching more than 63 sessions on windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.5 KB
Line 
1/* $Id: ClientWatcher.cpp 60061 2016-03-16 14:47:01Z vboxsync $ */
2/** @file
3 * VirtualBox API client session crash watcher
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN
19#include <iprt/asm.h>
20#include <iprt/assert.h>
21#include <iprt/semaphore.h>
22#include <iprt/process.h>
23
24#include <VBox/log.h>
25#include <VBox/com/defs.h>
26
27#include <vector>
28
29#include "VirtualBoxBase.h"
30#include "AutoCaller.h"
31#include "ClientWatcher.h"
32#include "ClientToken.h"
33#include "VirtualBoxImpl.h"
34#include "MachineImpl.h"
35
36#if defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) || defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
37/** Table for adaptive timeouts. After an update the counter starts at the
38 * maximum value and decreases to 0, i.e. first the short timeouts are used
39 * and then the longer ones. This minimizes the detection latency in the
40 * cases where a change is expected, for crashes. */
41static const RTMSINTERVAL s_aUpdateTimeoutSteps[] = { 500, 200, 100, 50, 20, 10, 5 };
42#endif
43
44
45
46VirtualBox::ClientWatcher::ClientWatcher() :
47 mLock(LOCKCLASS_OBJECTSTATE)
48{
49 AssertReleaseFailed();
50}
51
52VirtualBox::ClientWatcher::~ClientWatcher()
53{
54 if (mThread != NIL_RTTHREAD)
55 {
56 /* signal the client watcher thread, should be exiting now */
57 update();
58 /* wait for termination */
59 RTThreadWait(mThread, RT_INDEFINITE_WAIT, NULL);
60 mThread = NIL_RTTHREAD;
61 }
62 mProcesses.clear();
63#if defined(RT_OS_WINDOWS)
64 if (mUpdateReq != NULL)
65 {
66 ::CloseHandle(mUpdateReq);
67 mUpdateReq = NULL;
68 }
69#elif defined(RT_OS_OS2) || defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) || defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
70 if (mUpdateReq != NIL_RTSEMEVENT)
71 {
72 RTSemEventDestroy(mUpdateReq);
73 mUpdateReq = NIL_RTSEMEVENT;
74 }
75#else
76# error "Port me!"
77#endif
78}
79
80VirtualBox::ClientWatcher::ClientWatcher(const ComObjPtr<VirtualBox> &pVirtualBox) :
81 mVirtualBox(pVirtualBox),
82 mThread(NIL_RTTHREAD),
83 mUpdateReq(CWUPDATEREQARG),
84 mLock(LOCKCLASS_OBJECTSTATE)
85{
86#if defined(RT_OS_WINDOWS)
87# ifdef CW_WITH_BIRD_WATCHING
88 /* Misc state. */
89 mfTerminate = false;
90 mcMsWait = INFINITE;
91 mcActiveSubworkers = 0;
92
93 /* Update request. The UpdateReq event is also used to wake up subthreads. */
94 mfUpdateReq = false;
95 mUpdateReq = ::CreateEvent(NULL /*pSecAttr*/, TRUE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pszName*/);
96 AssertRelease(mUpdateReq != NULL);
97
98 /* Initialize the handle array. */
99 for (uint32_t i = 0; i < RT_ELEMENTS(mahWaitHandles); i++)
100 mahWaitHandles[i] = NULL;
101 for (uint32_t i = 0; i < RT_ELEMENTS(mahWaitHandles); i += CW_MAX_HANDLES_PER_THREAD)
102 mahWaitHandles[i] = mUpdateReq;
103 mcWaitHandles = 1;
104
105# else
106 mUpdateReq = ::CreateEvent(NULL, FALSE, FALSE, NULL);
107# endif
108#elif defined(RT_OS_OS2)
109 RTSemEventCreate(&mUpdateReq);
110#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) || defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
111 RTSemEventCreate(&mUpdateReq);
112 /* start with high timeouts, nothing to do */
113 ASMAtomicUoWriteU8(&mUpdateAdaptCtr, 0);
114#else
115# error "Port me!"
116#endif
117
118 int vrc = RTThreadCreate(&mThread,
119 worker,
120 (void *)this,
121 0,
122 RTTHREADTYPE_MAIN_WORKER,
123 RTTHREADFLAGS_WAITABLE,
124 "Watcher");
125 AssertRC(vrc);
126}
127
128bool VirtualBox::ClientWatcher::isReady()
129{
130 return mThread != NIL_RTTHREAD;
131}
132
133/**
134 * Sends a signal to the thread to rescan the clients/VMs having open sessions.
135 */
136void VirtualBox::ClientWatcher::update()
137{
138 AssertReturnVoid(mThread != NIL_RTTHREAD);
139 LogFlowFunc(("ping!\n"));
140
141 /* sent an update request */
142#if defined(RT_OS_WINDOWS)
143#ifdef CW_WITH_BIRD_WATCHING
144 ASMAtomicWriteBool(&mfUpdateReq, true);
145#endif
146 ::SetEvent(mUpdateReq);
147#elif defined(RT_OS_OS2)
148 RTSemEventSignal(mUpdateReq);
149#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
150 /* use short timeouts, as we expect changes */
151 ASMAtomicUoWriteU8(&mUpdateAdaptCtr, RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1);
152 RTSemEventSignal(mUpdateReq);
153#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
154 RTSemEventSignal(mUpdateReq);
155#else
156# error "Port me!"
157#endif
158}
159
160/**
161 * Adds a process to the list of processes to be reaped. This call should be
162 * followed by a call to update() to cause the necessary actions immediately,
163 * in case the process crashes straight away.
164 */
165void VirtualBox::ClientWatcher::addProcess(RTPROCESS pid)
166{
167 AssertReturnVoid(mThread != NIL_RTTHREAD);
168 /* @todo r=klaus, do the reaping on all platforms! */
169#ifndef RT_OS_WINDOWS
170 AutoWriteLock alock(mLock COMMA_LOCKVAL_SRC_POS);
171 mProcesses.push_back(pid);
172#endif
173}
174
175#ifdef CW_WITH_BIRD_WATCHING
176
177/**
178 * Closes all the client process handles in mahWaitHandles.
179 *
180 * The array is divided into two ranges, first range are mutext handles of
181 * established sessions, the second range is zero or more process handles of
182 * spawning sessions. It's the latter that we close here, the former will just
183 * be NULLed out.
184 *
185 * @param cProcHandles The number of process handles.
186 */
187void VirtualBox::ClientWatcher::winResetHandleArray(uint32_t cProcHandles)
188{
189 uint32_t idxHandle = mcWaitHandles;
190 Assert(cProcHandles < idxHandle);
191 Assert(idxHandle > 0);
192
193 /* Spawning process handles. */
194 while (cProcHandles-- > 0 && idxHandle > 0)
195 {
196 idxHandle--;
197 if (idxHandle % CW_MAX_HANDLES_PER_THREAD)
198 {
199 Assert(mahWaitHandles[idxHandle] != mUpdateReq);
200 LogFlow(("UPDATE: closing %p\n", mahWaitHandles[idxHandle]));
201 CloseHandle(mahWaitHandles[idxHandle]);
202 mahWaitHandles[idxHandle] = NULL;
203 }
204 else
205 Assert(mahWaitHandles[idxHandle] == mUpdateReq);
206 }
207
208 /* Mutex handles (not to be closed). */
209 while (idxHandle-- > 0)
210 if (idxHandle % CW_MAX_HANDLES_PER_THREAD)
211 {
212 Assert(mahWaitHandles[idxHandle] != mUpdateReq);
213 mahWaitHandles[idxHandle] = NULL;
214 }
215 else
216 Assert(mahWaitHandles[idxHandle] == mUpdateReq);
217
218 /* Reset the handle count. */
219 mcWaitHandles = 1;
220}
221
222/**
223 * Does the waiting on a section of the handle array.
224 *
225 * @param pSubworker Pointer to the calling thread's data.
226 * @param cMsWait Number of milliseconds to wait.
227 */
228void VirtualBox::ClientWatcher::subworkerWait(VirtualBox::ClientWatcher::PerSubworker *pSubworker, uint32_t cMsWait)
229{
230 /*
231 * Figure out what section to wait on and do the waiting.
232 */
233 uint32_t idxHandle = pSubworker->iSubworker * CW_MAX_HANDLES_PER_THREAD;
234 uint32_t cHandles = CW_MAX_HANDLES_PER_THREAD;
235 if (idxHandle + cHandles > mcWaitHandles)
236 {
237 cHandles = mcWaitHandles - idxHandle;
238 AssertStmt(idxHandle < mcWaitHandles, cHandles = 1);
239 }
240 Assert(mahWaitHandles[idxHandle] == mUpdateReq);
241
242 DWORD dwWait = ::WaitForMultipleObjects(cHandles,
243 &mahWaitHandles[idxHandle],
244 FALSE /*fWaitAll*/,
245 cMsWait);
246 pSubworker->dwWait = dwWait;
247
248 /*
249 * If we didn't wake up because of the UpdateReq handle, signal it to make
250 * sure everyone else wakes up too.
251 */
252 if (dwWait != WAIT_OBJECT_0)
253 {
254 BOOL fRc = SetEvent(mUpdateReq);
255 Assert(fRc); NOREF(fRc);
256 }
257
258 /*
259 * Last one signals the main thread.
260 */
261 if (ASMAtomicDecU32(&mcActiveSubworkers) == 0)
262 {
263 int vrc = RTThreadUserSignal(maSubworkers[0].hThread);
264 AssertLogRelMsg(RT_SUCCESS(vrc), ("RTThreadUserSignal -> %Rrc\n", vrc));
265 }
266
267}
268
269/**
270 * Thread worker function that watches the termination of all client processes
271 * that have open sessions using IMachine::LockMachine()
272 */
273/*static*/
274DECLCALLBACK(int) VirtualBox::ClientWatcher::subworkerThread(RTTHREAD hThreadSelf, void *pvUser)
275{
276 VirtualBox::ClientWatcher::PerSubworker *pSubworker = (VirtualBox::ClientWatcher::PerSubworker *)pvUser;
277 VirtualBox::ClientWatcher *pThis = pSubworker->pSelf;
278 int vrc;
279 while (!pThis->mfTerminate)
280 {
281 /* Before we start waiting, reset the event semaphore. */
282 vrc = RTThreadUserReset(pSubworker->hThread);
283 AssertLogRelMsg(RT_SUCCESS(vrc), ("RTThreadUserReset [iSubworker=%#u] -> %Rrc", pSubworker->iSubworker, vrc));
284
285 /* Do the job. */
286 pThis->subworkerWait(pSubworker, pThis->mcMsWait);
287
288 /* Wait for the next job. */
289 do
290 {
291 vrc = RTThreadUserWaitNoResume(hThreadSelf, RT_INDEFINITE_WAIT);
292 Assert(vrc == VINF_SUCCESS || vrc == VERR_INTERRUPTED);
293 }
294 while ( vrc != VINF_SUCCESS
295 && !pThis->mfTerminate);
296 }
297 return VINF_SUCCESS;
298}
299
300
301#endif /* CW_WITH_BIRD_WATCHING */
302
303/**
304 * Thread worker function that watches the termination of all client processes
305 * that have open sessions using IMachine::LockMachine()
306 */
307/*static*/
308DECLCALLBACK(int) VirtualBox::ClientWatcher::worker(RTTHREAD hThreadSelf, void *pvUser)
309{
310 LogFlowFuncEnter();
311 NOREF(hThreadSelf);
312
313 VirtualBox::ClientWatcher *that = (VirtualBox::ClientWatcher *)pvUser;
314 Assert(that);
315
316 typedef std::vector<ComObjPtr<Machine> > MachineVector;
317 typedef std::vector<ComObjPtr<SessionMachine> > SessionMachineVector;
318
319 SessionMachineVector machines;
320 MachineVector spawnedMachines;
321
322 size_t cnt = 0;
323 size_t cntSpawned = 0;
324
325 VirtualBoxBase::initializeComForThread();
326
327#if defined(RT_OS_WINDOWS)
328
329 /// @todo (dmik) processes reaping!
330
331#ifdef CW_WITH_BIRD_WATCHING
332 int vrc;
333
334 /* Initialize all the subworker data. */
335 that->maSubworkers[0].hThread = hThreadSelf;
336 for (uint32_t iSubworker = 1; iSubworker < RT_ELEMENTS(that->maSubworkers); iSubworker++)
337 that->maSubworkers[iSubworker].hThread = NIL_RTTHREAD;
338 for (uint32_t iSubworker = 0; iSubworker < RT_ELEMENTS(that->maSubworkers); iSubworker++)
339 {
340 that->maSubworkers[iSubworker].pSelf = that;
341 that->maSubworkers[iSubworker].iSubworker = iSubworker;
342 }
343#else
344 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
345 handles[0] = that->mUpdateReq;
346#endif
347
348 do
349 {
350 AutoCaller autoCaller(that->mVirtualBox);
351 /* VirtualBox has been early uninitialized, terminate */
352 if (!autoCaller.isOk())
353 break;
354
355 bool fPidRace = false;
356 do
357 {
358 /* release the caller to let uninit() ever proceed */
359 autoCaller.release();
360
361#ifdef CW_WITH_BIRD_WATCHING
362 /* Kick of the waiting. */
363 uint32_t const cSubworkers = (that->mcWaitHandles + CW_MAX_HANDLES_PER_THREAD - 1) / CW_MAX_HANDLES_PER_THREAD;
364 uint32_t const cMsWait = !fPidRace ? INFINITE : 500;
365 LogFlowFunc(("UPDATE: Waiting. %u handles, %u subworkers, %u ms wait\n", that->mcWaitHandles, cSubworkers, cMsWait));
366
367 that->mcMsWait = cMsWait;
368 ASMAtomicWriteU32(&that->mcActiveSubworkers, cSubworkers);
369 RTThreadUserReset(hThreadSelf);
370
371 for (uint32_t iSubworker = 1; iSubworker < cSubworkers; iSubworker++)
372 {
373 if (that->maSubworkers[iSubworker].hThread != NIL_RTTHREAD)
374 {
375 vrc = RTThreadUserSignal(that->maSubworkers[iSubworker].hThread);
376 AssertLogRelMsg(RT_SUCCESS(vrc), ("RTThreadUserSignal -> %Rrc\n", vrc));
377 }
378 else
379 {
380 vrc = RTThreadCreateF(&that->maSubworkers[iSubworker].hThread,
381 VirtualBox::ClientWatcher::subworkerThread, &that->maSubworkers[iSubworker],
382 _128K, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "Watcher%u", iSubworker);
383 AssertLogRelMsgStmt(RT_SUCCESS(vrc), ("%Rrc iSubworker=%u\n", vrc, iSubworker),
384 that->maSubworkers[iSubworker].hThread = NIL_RTTHREAD);
385 }
386 if (RT_FAILURE(vrc))
387 that->subworkerWait(&that->maSubworkers[iSubworker], 1);
388 }
389
390 /* Wait ourselves. */
391 that->subworkerWait(&that->maSubworkers[0], cMsWait);
392
393 /* Make sure all waiters are done waiting. */
394 BOOL fRc = SetEvent(that->mUpdateReq);
395 Assert(fRc); NOREF(fRc);
396
397 vrc = RTThreadUserWait(hThreadSelf, RT_INDEFINITE_WAIT);
398 AssertLogRelMsg(RT_SUCCESS(vrc), ("RTThreadUserWait -> %Rrc\n", vrc));
399 Assert(that->mcActiveSubworkers == 0);
400
401 /* Consume pending update request before proceeding with processing the wait results. */
402 fRc = ResetEvent(that->mUpdateReq);
403 Assert(fRc);
404
405 bool update = ASMAtomicXchgBool(&that->mfUpdateReq, false);
406 if (update)
407 LogFlowFunc(("UPDATE: Update request pending\n"));
408 update |= fPidRace;
409
410 /* Process the wait results. */
411 autoCaller.add();
412 if (!autoCaller.isOk())
413 break;
414 for (uint32_t iSubworker = 0; iSubworker < cSubworkers; iSubworker++)
415 {
416 DWORD dwWait = that->maSubworkers[iSubworker].dwWait;
417 LogFlowFunc(("UPDATE: subworker #%u: dwWait=%#x\n", iSubworker, dwWait));
418 if ( (dwWait > WAIT_OBJECT_0 && dwWait < WAIT_OBJECT_0 + CW_MAX_HANDLES_PER_THREAD)
419 || (dwWait > WAIT_ABANDONED_0 && dwWait < WAIT_ABANDONED_0 + CW_MAX_HANDLES_PER_THREAD) )
420 {
421 uint32_t idxHandle = iSubworker * CW_MAX_HANDLES_PER_THREAD;
422 if (dwWait > WAIT_OBJECT_0 && dwWait < WAIT_OBJECT_0 + CW_MAX_HANDLES_PER_THREAD)
423 idxHandle += dwWait - WAIT_OBJECT_0;
424 else
425 idxHandle += dwWait - WAIT_ABANDONED_0;
426
427 uint32_t const idxMachine = idxHandle - (iSubworker + 1);
428 if (idxMachine < cnt)
429 {
430 /* Machine mutex is released or abandond due to client process termination. */
431 LogFlowFunc(("UPDATE: Calling i_checkForDeath on idxMachine=%u (idxHandle=%u) dwWait=%#x\n",
432 idxMachine, idxHandle, dwWait));
433 (machines[idxMachine])->i_checkForDeath();
434 }
435 else if (idxMachine < cnt + cntSpawned)
436 {
437 /* Spawned VM process has terminated normally. */
438 Assert(dwWait < WAIT_ABANDONED_0);
439 LogFlowFunc(("UPDATE: Calling i_checkForSpawnFailure on idxMachine=%u/%u idxHandle=%u dwWait=%#x\n",
440 idxMachine, idxMachine - cnt, idxHandle, dwWait));
441 (spawnedMachines[idxMachine - cnt])->i_checkForSpawnFailure();
442 }
443 else
444 AssertFailed();
445 update = true;
446 }
447 else
448 Assert(dwWait == WAIT_OBJECT_0 || dwWait == WAIT_TIMEOUT);
449 }
450
451#else
452 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
453 handles,
454 FALSE,
455 !fPidRace ? INFINITE : 500);
456 LogFlowFunc(("UPDATE: dwWait=%#x\n", rc));
457
458 /* Restore the caller before using VirtualBox. If it fails, this
459 * means VirtualBox is being uninitialized and we must terminate. */
460 autoCaller.add();
461 if (!autoCaller.isOk())
462 break;
463
464 bool update = fPidRace;
465
466 if (rc == WAIT_OBJECT_0)
467 {
468 /* update event is signaled */
469 update = true;
470 }
471 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
472 {
473 /* machine mutex is released */
474 LogFlowFunc(("UPDATE: Calling i_checkForDeath on #%u dwWait=%#x\n", rc - WAIT_OBJECT_0 - 1, rc));
475 (machines[rc - WAIT_OBJECT_0 - 1])->i_checkForDeath();
476 update = true;
477 }
478 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
479 {
480 /* machine mutex is abandoned due to client process termination */
481 LogFlowFunc(("UPDATE: Calling i_checkForDeath on #%u dwWait=%#x\n", rc - WAIT_OBJECT_0 - 1, rc));
482 (machines[rc - WAIT_ABANDONED_0 - 1])->i_checkForDeath();
483 update = true;
484 }
485 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
486 {
487 /* spawned VM process has terminated (normally or abnormally) */
488 LogFlowFunc(("UPDATE: Calling i_checkForSpawnFailure on #%u dwWait=%#x\n", rc - WAIT_OBJECT_0 - cnt - 1, rc));
489 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
490 i_checkForSpawnFailure();
491 update = true;
492 }
493#endif
494
495 if (update)
496 {
497 LogFlowFunc(("UPDATE: Update pending (cnt=%u cntSpawned=%u)...\n", cnt, cntSpawned));
498
499 /* close old process handles */
500#ifdef CW_WITH_BIRD_WATCHING
501 that->winResetHandleArray((uint32_t)cntSpawned);
502#else
503 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
504 CloseHandle(handles[i]);
505#endif
506
507 // get reference to the machines list in VirtualBox
508 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();
509
510 // lock the machines list for reading
511 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
512
513 /* obtain a new set of opened machines */
514 cnt = 0;
515 machines.clear();
516#ifdef CW_WITH_BIRD_WATCHING
517 uint32_t idxHandle = 0;
518#endif
519
520 for (MachinesOList::iterator it = allMachines.begin();
521 it != allMachines.end();
522 ++it)
523 {
524#ifdef CW_WITH_BIRD_WATCHING
525 AssertMsgBreak(idxHandle < CW_MAX_CLIENTS, ("CW_MAX_CLIENTS reached"));
526#else
527 /// @todo handle situations with more than 64 objects
528 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
529 ("MAXIMUM_WAIT_OBJECTS reached"));
530#endif
531
532 ComObjPtr<SessionMachine> sm;
533 if ((*it)->i_isSessionOpenOrClosing(sm))
534 {
535 AutoCaller smCaller(sm);
536 if (smCaller.isOk())
537 {
538 AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS);
539 Machine::ClientToken *ct = sm->i_getClientToken();
540 if (ct)
541 {
542 HANDLE ipcSem = ct->getToken();
543 machines.push_back(sm);
544#ifdef CW_WITH_BIRD_WATCHING
545 if (!(idxHandle % CW_MAX_HANDLES_PER_THREAD))
546 idxHandle++;
547 that->mahWaitHandles[idxHandle++] = ipcSem;
548#else
549 handles[1 + cnt] = ipcSem;
550#endif
551 ++cnt;
552 }
553 }
554 }
555 }
556
557 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
558
559 /* obtain a new set of spawned machines */
560 fPidRace = false;
561 cntSpawned = 0;
562 spawnedMachines.clear();
563
564 for (MachinesOList::iterator it = allMachines.begin();
565 it != allMachines.end();
566 ++it)
567 {
568#ifdef CW_WITH_BIRD_WATCHING
569 AssertMsgBreak(idxHandle < CW_MAX_CLIENTS, ("CW_MAX_CLIENTS reached"));
570#else
571 /// @todo handle situations with more than 64 objects
572 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
573 ("MAXIMUM_WAIT_OBJECTS reached"));
574#endif
575
576 if ((*it)->i_isSessionSpawning())
577 {
578 ULONG pid;
579 HRESULT hrc = (*it)->COMGETTER(SessionPID)(&pid);
580 if (SUCCEEDED(hrc))
581 {
582 if (pid != NIL_RTPROCESS)
583 {
584 HANDLE hProc = OpenProcess(SYNCHRONIZE, FALSE, pid);
585 AssertMsg(hProc != NULL, ("OpenProcess (pid=%d) failed with %d\n", pid, GetLastError()));
586 if (hProc != NULL)
587 {
588 spawnedMachines.push_back(*it);
589#ifdef CW_WITH_BIRD_WATCHING
590 if (!(idxHandle % CW_MAX_HANDLES_PER_THREAD))
591 idxHandle++;
592 that->mahWaitHandles[idxHandle++] = hProc;
593#else
594 handles[1 + cnt + cntSpawned] = hProc;
595#endif
596 ++cntSpawned;
597 }
598 }
599 else
600 fPidRace = true;
601 }
602 }
603 }
604
605 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
606
607#ifdef CW_WITH_BIRD_WATCHING
608 /* Update mcWaitHandles and make sure there is at least one handle to wait on. */
609 that->mcWaitHandles = RT_MAX(idxHandle, 1);
610#endif
611
612 // machines lock unwinds here
613 }
614 else
615 LogFlowFunc(("UPDATE: No update pending.\n"));
616 }
617 while (true);
618 }
619 while (0);
620
621#ifdef CW_WITH_BIRD_WATCHING
622 /* Terminate subworker threads. */
623 ASMAtomicWriteBool(&that->mfTerminate, true);
624 for (uint32_t iSubworker = 1; iSubworker < RT_ELEMENTS(that->maSubworkers); iSubworker++)
625 if (that->maSubworkers[iSubworker].hThread != NIL_RTTHREAD)
626 RTThreadUserSignal(that->maSubworkers[iSubworker].hThread);
627 for (uint32_t iSubworker = 1; iSubworker < RT_ELEMENTS(that->maSubworkers); iSubworker++)
628 if (that->maSubworkers[iSubworker].hThread != NIL_RTTHREAD)
629 {
630 vrc = RTThreadWait(that->maSubworkers[iSubworker].hThread, RT_MS_1MIN, NULL /*prc*/);
631 if (RT_SUCCESS(vrc))
632 that->maSubworkers[iSubworker].hThread = NIL_RTTHREAD;
633 else
634 AssertLogRelMsgFailed(("RTThreadWait -> %Rrc\n", vrc));
635 }
636#endif
637
638 /* close old process handles */
639#ifdef CW_WITH_BIRD_WATCHING
640 that->winResetHandleArray((uint32_t)cntSpawned);
641#else
642 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
643 CloseHandle(handles[i]);
644#endif
645
646 /* release sets of machines if any */
647 machines.clear();
648 spawnedMachines.clear();
649
650 ::CoUninitialize();
651
652#elif defined(RT_OS_OS2)
653
654 /// @todo (dmik) processes reaping!
655
656 /* according to PMREF, 64 is the maximum for the muxwait list */
657 SEMRECORD handles[64];
658
659 HMUX muxSem = NULLHANDLE;
660
661 do
662 {
663 AutoCaller autoCaller(that->mVirtualBox);
664 /* VirtualBox has been early uninitialized, terminate */
665 if (!autoCaller.isOk())
666 break;
667
668 do
669 {
670 /* release the caller to let uninit() ever proceed */
671 autoCaller.release();
672
673 int vrc = RTSemEventWait(that->mUpdateReq, 500);
674
675 /* Restore the caller before using VirtualBox. If it fails, this
676 * means VirtualBox is being uninitialized and we must terminate. */
677 autoCaller.add();
678 if (!autoCaller.isOk())
679 break;
680
681 bool update = false;
682 bool updateSpawned = false;
683
684 if (RT_SUCCESS(vrc))
685 {
686 /* update event is signaled */
687 update = true;
688 updateSpawned = true;
689 }
690 else
691 {
692 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
693 ("RTSemEventWait returned %Rrc\n", vrc));
694
695 /* are there any mutexes? */
696 if (cnt > 0)
697 {
698 /* figure out what's going on with machines */
699
700 unsigned long semId = 0;
701 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
702 SEM_IMMEDIATE_RETURN, &semId);
703
704 if (arc == NO_ERROR)
705 {
706 /* machine mutex is normally released */
707 Assert(semId >= 0 && semId < cnt);
708 if (semId >= 0 && semId < cnt)
709 {
710#if 0//def DEBUG
711 {
712 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
713 LogFlowFunc(("released mutex: machine='%ls'\n",
714 machines[semId]->name().raw()));
715 }
716#endif
717 machines[semId]->i_checkForDeath();
718 }
719 update = true;
720 }
721 else if (arc == ERROR_SEM_OWNER_DIED)
722 {
723 /* machine mutex is abandoned due to client process
724 * termination; find which mutex is in the Owner Died
725 * state */
726 for (size_t i = 0; i < cnt; ++i)
727 {
728 PID pid; TID tid;
729 unsigned long reqCnt;
730 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
731 if (arc == ERROR_SEM_OWNER_DIED)
732 {
733 /* close the dead mutex as asked by PMREF */
734 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
735
736 Assert(i >= 0 && i < cnt);
737 if (i >= 0 && i < cnt)
738 {
739#if 0//def DEBUG
740 {
741 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
742 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
743 machines[i]->name().raw()));
744 }
745#endif
746 machines[i]->i_checkForDeath();
747 }
748 }
749 }
750 update = true;
751 }
752 else
753 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
754 ("DosWaitMuxWaitSem returned %d\n", arc));
755 }
756
757 /* are there any spawning sessions? */
758 if (cntSpawned > 0)
759 {
760 for (size_t i = 0; i < cntSpawned; ++i)
761 updateSpawned |= (spawnedMachines[i])->
762 i_checkForSpawnFailure();
763 }
764 }
765
766 if (update || updateSpawned)
767 {
768 // get reference to the machines list in VirtualBox
769 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();
770
771 // lock the machines list for reading
772 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
773
774 if (update)
775 {
776 /* close the old muxsem */
777 if (muxSem != NULLHANDLE)
778 ::DosCloseMuxWaitSem(muxSem);
779
780 /* obtain a new set of opened machines */
781 cnt = 0;
782 machines.clear();
783
784 for (MachinesOList::iterator it = allMachines.begin();
785 it != allMachines.end(); ++it)
786 {
787 /// @todo handle situations with more than 64 objects
788 AssertMsg(cnt <= 64 /* according to PMREF */,
789 ("maximum of 64 mutex semaphores reached (%d)",
790 cnt));
791
792 ComObjPtr<SessionMachine> sm;
793 if ((*it)->i_isSessionOpenOrClosing(sm))
794 {
795 AutoCaller smCaller(sm);
796 if (smCaller.isOk())
797 {
798 AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS);
799 ClientToken *ct = sm->i_getClientToken();
800 if (ct)
801 {
802 HMTX ipcSem = ct->getToken();
803 machines.push_back(sm);
804 handles[cnt].hsemCur = (HSEM)ipcSem;
805 handles[cnt].ulUser = cnt;
806 ++cnt;
807 }
808 }
809 }
810 }
811
812 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
813
814 if (cnt > 0)
815 {
816 /* create a new muxsem */
817 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
818 handles,
819 DCMW_WAIT_ANY);
820 AssertMsg(arc == NO_ERROR,
821 ("DosCreateMuxWaitSem returned %d\n", arc));
822 NOREF(arc);
823 }
824 }
825
826 if (updateSpawned)
827 {
828 /* obtain a new set of spawned machines */
829 spawnedMachines.clear();
830
831 for (MachinesOList::iterator it = allMachines.begin();
832 it != allMachines.end(); ++it)
833 {
834 if ((*it)->i_isSessionSpawning())
835 spawnedMachines.push_back(*it);
836 }
837
838 cntSpawned = spawnedMachines.size();
839 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
840 }
841 }
842 }
843 while (true);
844 }
845 while (0);
846
847 /* close the muxsem */
848 if (muxSem != NULLHANDLE)
849 ::DosCloseMuxWaitSem(muxSem);
850
851 /* release sets of machines if any */
852 machines.clear();
853 spawnedMachines.clear();
854
855#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
856
857 bool update = false;
858 bool updateSpawned = false;
859
860 do
861 {
862 AutoCaller autoCaller(that->mVirtualBox);
863 if (!autoCaller.isOk())
864 break;
865
866 do
867 {
868 /* release the caller to let uninit() ever proceed */
869 autoCaller.release();
870
871 /* determine wait timeout adaptively: after updating information
872 * relevant to the client watcher, check a few times more
873 * frequently. This ensures good reaction time when the signalling
874 * has to be done a bit before the actual change for technical
875 * reasons, and saves CPU cycles when no activities are expected. */
876 RTMSINTERVAL cMillies;
877 {
878 uint8_t uOld, uNew;
879 do
880 {
881 uOld = ASMAtomicUoReadU8(&that->mUpdateAdaptCtr);
882 uNew = uOld ? uOld - 1 : uOld;
883 } while (!ASMAtomicCmpXchgU8(&that->mUpdateAdaptCtr, uNew, uOld));
884 Assert(uOld <= RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1);
885 cMillies = s_aUpdateTimeoutSteps[uOld];
886 }
887
888 int rc = RTSemEventWait(that->mUpdateReq, cMillies);
889
890 /*
891 * Restore the caller before using VirtualBox. If it fails, this
892 * means VirtualBox is being uninitialized and we must terminate.
893 */
894 autoCaller.add();
895 if (!autoCaller.isOk())
896 break;
897
898 if (RT_SUCCESS(rc) || update || updateSpawned)
899 {
900 /* RT_SUCCESS(rc) means an update event is signaled */
901
902 // get reference to the machines list in VirtualBox
903 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();
904
905 // lock the machines list for reading
906 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
907
908 if (RT_SUCCESS(rc) || update)
909 {
910 /* obtain a new set of opened machines */
911 machines.clear();
912
913 for (MachinesOList::iterator it = allMachines.begin();
914 it != allMachines.end();
915 ++it)
916 {
917 ComObjPtr<SessionMachine> sm;
918 if ((*it)->i_isSessionOpenOrClosing(sm))
919 machines.push_back(sm);
920 }
921
922 cnt = machines.size();
923 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
924 }
925
926 if (RT_SUCCESS(rc) || updateSpawned)
927 {
928 /* obtain a new set of spawned machines */
929 spawnedMachines.clear();
930
931 for (MachinesOList::iterator it = allMachines.begin();
932 it != allMachines.end();
933 ++it)
934 {
935 if ((*it)->i_isSessionSpawning())
936 spawnedMachines.push_back(*it);
937 }
938
939 cntSpawned = spawnedMachines.size();
940 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
941 }
942
943 // machines lock unwinds here
944 }
945
946 update = false;
947 for (size_t i = 0; i < cnt; ++i)
948 update |= (machines[i])->i_checkForDeath();
949
950 updateSpawned = false;
951 for (size_t i = 0; i < cntSpawned; ++i)
952 updateSpawned |= (spawnedMachines[i])->i_checkForSpawnFailure();
953
954 /* reap child processes */
955 {
956 AutoWriteLock alock(that->mLock COMMA_LOCKVAL_SRC_POS);
957 if (that->mProcesses.size())
958 {
959 LogFlowFunc(("UPDATE: child process count = %d\n",
960 that->mProcesses.size()));
961 VirtualBox::ClientWatcher::ProcessList::iterator it = that->mProcesses.begin();
962 while (it != that->mProcesses.end())
963 {
964 RTPROCESS pid = *it;
965 RTPROCSTATUS status;
966 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
967 if (vrc == VINF_SUCCESS)
968 {
969 if ( status.enmReason != RTPROCEXITREASON_NORMAL
970 || status.iStatus != RTEXITCODE_SUCCESS)
971 {
972 switch (status.enmReason)
973 {
974 default:
975 case RTPROCEXITREASON_NORMAL:
976 LogRel(("Reaper: Pid %d (%x) exited normally: %d (%#x)\n",
977 pid, pid, status.iStatus, status.iStatus));
978 break;
979 case RTPROCEXITREASON_ABEND:
980 LogRel(("Reaper: Pid %d (%x) abended: %d (%#x)\n",
981 pid, pid, status.iStatus, status.iStatus));
982 break;
983 case RTPROCEXITREASON_SIGNAL:
984 LogRel(("Reaper: Pid %d (%x) was signalled: %d (%#x)\n",
985 pid, pid, status.iStatus, status.iStatus));
986 break;
987 }
988 }
989 else
990 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
991 pid, pid, status.iStatus,
992 status.enmReason));
993 it = that->mProcesses.erase(it);
994 }
995 else
996 {
997 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
998 pid, pid, vrc));
999 if (vrc != VERR_PROCESS_RUNNING)
1000 {
1001 /* remove the process if it is not already running */
1002 it = that->mProcesses.erase(it);
1003 }
1004 else
1005 ++it;
1006 }
1007 }
1008 }
1009 }
1010 }
1011 while (true);
1012 }
1013 while (0);
1014
1015 /* release sets of machines if any */
1016 machines.clear();
1017 spawnedMachines.clear();
1018
1019#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
1020
1021 bool updateSpawned = false;
1022
1023 do
1024 {
1025 AutoCaller autoCaller(that->mVirtualBox);
1026 if (!autoCaller.isOk())
1027 break;
1028
1029 do
1030 {
1031 /* release the caller to let uninit() ever proceed */
1032 autoCaller.release();
1033
1034 /* determine wait timeout adaptively: after updating information
1035 * relevant to the client watcher, check a few times more
1036 * frequently. This ensures good reaction time when the signalling
1037 * has to be done a bit before the actual change for technical
1038 * reasons, and saves CPU cycles when no activities are expected. */
1039 RTMSINTERVAL cMillies;
1040 {
1041 uint8_t uOld, uNew;
1042 do
1043 {
1044 uOld = ASMAtomicUoReadU8(&that->mUpdateAdaptCtr);
1045 uNew = uOld ? uOld - 1 : uOld;
1046 } while (!ASMAtomicCmpXchgU8(&that->mUpdateAdaptCtr, uNew, uOld));
1047 Assert(uOld <= RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1);
1048 cMillies = s_aUpdateTimeoutSteps[uOld];
1049 }
1050
1051 int rc = RTSemEventWait(that->mUpdateReq, cMillies);
1052
1053 /*
1054 * Restore the caller before using VirtualBox. If it fails, this
1055 * means VirtualBox is being uninitialized and we must terminate.
1056 */
1057 autoCaller.add();
1058 if (!autoCaller.isOk())
1059 break;
1060
1061 /** @todo this quite big effort for catching machines in spawning
1062 * state which can't be caught by the token mechanism (as the token
1063 * can't be in the other process yet) could be eliminated if the
1064 * reaping is made smarter, having cross-reference information
1065 * from the pid to the corresponding machine object. Both cases do
1066 * more or less the same thing anyway. */
1067 if (RT_SUCCESS(rc) || updateSpawned)
1068 {
1069 /* RT_SUCCESS(rc) means an update event is signaled */
1070
1071 // get reference to the machines list in VirtualBox
1072 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();
1073
1074 // lock the machines list for reading
1075 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1076
1077 if (RT_SUCCESS(rc) || updateSpawned)
1078 {
1079 /* obtain a new set of spawned machines */
1080 spawnedMachines.clear();
1081
1082 for (MachinesOList::iterator it = allMachines.begin();
1083 it != allMachines.end();
1084 ++it)
1085 {
1086 if ((*it)->i_isSessionSpawning())
1087 spawnedMachines.push_back(*it);
1088 }
1089
1090 cntSpawned = spawnedMachines.size();
1091 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
1092 }
1093
1094 NOREF(cnt);
1095 // machines lock unwinds here
1096 }
1097
1098 updateSpawned = false;
1099 for (size_t i = 0; i < cntSpawned; ++i)
1100 updateSpawned |= (spawnedMachines[i])->i_checkForSpawnFailure();
1101
1102 /* reap child processes */
1103 {
1104 AutoWriteLock alock(that->mLock COMMA_LOCKVAL_SRC_POS);
1105 if (that->mProcesses.size())
1106 {
1107 LogFlowFunc(("UPDATE: child process count = %d\n",
1108 that->mProcesses.size()));
1109 VirtualBox::ClientWatcher::ProcessList::iterator it = that->mProcesses.begin();
1110 while (it != that->mProcesses.end())
1111 {
1112 RTPROCESS pid = *it;
1113 RTPROCSTATUS status;
1114 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
1115 if (vrc == VINF_SUCCESS)
1116 {
1117 if ( status.enmReason != RTPROCEXITREASON_NORMAL
1118 || status.iStatus != RTEXITCODE_SUCCESS)
1119 {
1120 switch (status.enmReason)
1121 {
1122 default:
1123 case RTPROCEXITREASON_NORMAL:
1124 LogRel(("Reaper: Pid %d (%x) exited normally: %d (%#x)\n",
1125 pid, pid, status.iStatus, status.iStatus));
1126 break;
1127 case RTPROCEXITREASON_ABEND:
1128 LogRel(("Reaper: Pid %d (%x) abended: %d (%#x)\n",
1129 pid, pid, status.iStatus, status.iStatus));
1130 break;
1131 case RTPROCEXITREASON_SIGNAL:
1132 LogRel(("Reaper: Pid %d (%x) was signalled: %d (%#x)\n",
1133 pid, pid, status.iStatus, status.iStatus));
1134 break;
1135 }
1136 }
1137 else
1138 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
1139 pid, pid, status.iStatus,
1140 status.enmReason));
1141 it = that->mProcesses.erase(it);
1142 }
1143 else
1144 {
1145 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
1146 pid, pid, vrc));
1147 if (vrc != VERR_PROCESS_RUNNING)
1148 {
1149 /* remove the process if it is not already running */
1150 it = that->mProcesses.erase(it);
1151 }
1152 else
1153 ++it;
1154 }
1155 }
1156 }
1157 }
1158 }
1159 while (true);
1160 }
1161 while (0);
1162
1163 /* release sets of machines if any */
1164 machines.clear();
1165 spawnedMachines.clear();
1166
1167#else
1168# error "Port me!"
1169#endif
1170
1171 VirtualBoxBase::uninitializeComForThread();
1172
1173 LogFlowFuncLeave();
1174 return 0;
1175}
1176/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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