VirtualBox

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

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