VirtualBox

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

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

ClientWatcher: Moved the process reaping code into a separate method, stopping the tratition of duplicating the code exactly for each platform. Make Windows and OS/2 call it too, adjusting the windows loop to only wait 5 seconds after session close to reduce the chance of dead child hanging around for a long time. (Not reaping processes on windows had the consequence of potentially leaking one process handle with associated kernel object, and have an ever growing process handle table in IPRT.)

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

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