VirtualBox

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

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

ClientWatcher: Enabled the new code watching more than 63 sessions on windows, stripping related #ifdefs and old code.

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