VirtualBox

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

Last change on this file since 97640 was 97640, checked in by vboxsync, 2 years ago

Main/ClientWatcher: Use sigabbrev_np() instead of strsignal(), so that the Validation Kit's vsheriff can detect this easier [build fix for glibc < 2.32].

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