VirtualBox

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

Last change on this file since 97634 was 97634, checked in by vboxsync, 3 years ago

Main/ClientWatcher: Resolve signals to strings on non-Windows, to make reading the logs (also for the testboxes) a bit easier [build fix].

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