VirtualBox

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

Last change on this file since 55747 was 55401, checked in by vboxsync, 10 years ago

added a couple of missing Id headers

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.5 KB
Line 
1/* $Id: ClientWatcher.cpp 55401 2015-04-23 10:03:17Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox API client session crash watcher
5 */
6
7/*
8 * Copyright (C) 2006-2014 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/asm.h>
20#include <iprt/assert.h>
21#include <iprt/log.h>
22#include <iprt/semaphore.h>
23#include <iprt/process.h>
24
25#include <VBox/com/defs.h>
26
27#include <vector>
28
29#include "VirtualBoxBase.h"
30#include "AutoCaller.h"
31#include "ClientWatcher.h"
32#include "ClientToken.h"
33#include "VirtualBoxImpl.h"
34#include "MachineImpl.h"
35
36#if defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) || defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
37/** Table for adaptive timeouts. After an update the counter starts at the
38 * maximum value and decreases to 0, i.e. first the short timeouts are used
39 * and then the longer ones. This minimizes the detection latency in the
40 * cases where a change is expected, for crashes. */
41static const RTMSINTERVAL s_aUpdateTimeoutSteps[] = { 500, 200, 100, 50, 20, 10, 5 };
42#endif
43
44
45
46VirtualBox::ClientWatcher::ClientWatcher() :
47 mLock(LOCKCLASS_OBJECTSTATE)
48{
49 AssertReleaseFailed();
50}
51
52VirtualBox::ClientWatcher::~ClientWatcher()
53{
54 if (mThread != NIL_RTTHREAD)
55 {
56 /* signal the client watcher thread, should be exiting now */
57 update();
58 /* wait for termination */
59 RTThreadWait(mThread, RT_INDEFINITE_WAIT, NULL);
60 mThread = NIL_RTTHREAD;
61 }
62 mProcesses.clear();
63#if defined(RT_OS_WINDOWS)
64 if (mUpdateReq != NULL)
65 {
66 ::CloseHandle(mUpdateReq);
67 mUpdateReq = NULL;
68 }
69#elif defined(RT_OS_OS2) || defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) || defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
70 if (mUpdateReq != NIL_RTSEMEVENT)
71 {
72 RTSemEventDestroy(mUpdateReq);
73 mUpdateReq = NIL_RTSEMEVENT;
74 }
75#else
76# error "Port me!"
77#endif
78}
79
80VirtualBox::ClientWatcher::ClientWatcher(const ComObjPtr<VirtualBox> &pVirtualBox) :
81 mVirtualBox(pVirtualBox),
82 mThread(NIL_RTTHREAD),
83 mUpdateReq(CWUPDATEREQARG),
84 mLock(LOCKCLASS_OBJECTSTATE)
85{
86#if defined(RT_OS_WINDOWS)
87 mUpdateReq = ::CreateEvent(NULL, FALSE, FALSE, NULL);
88#elif defined(RT_OS_OS2)
89 RTSemEventCreate(&mUpdateReq);
90#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) || defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
91 RTSemEventCreate(&mUpdateReq);
92 /* start with high timeouts, nothing to do */
93 ASMAtomicUoWriteU8(&mUpdateAdaptCtr, 0);
94#else
95# error "Port me!"
96#endif
97
98 int vrc = RTThreadCreate(&mThread,
99 worker,
100 (void *)this,
101 0,
102 RTTHREADTYPE_MAIN_WORKER,
103 RTTHREADFLAGS_WAITABLE,
104 "Watcher");
105 AssertRC(vrc);
106}
107
108bool VirtualBox::ClientWatcher::isReady()
109{
110 return mThread != NIL_RTTHREAD;
111}
112
113/**
114 * Sends a signal to the thread to rescan the clients/VMs having open sessions.
115 */
116void VirtualBox::ClientWatcher::update()
117{
118 AssertReturnVoid(mThread != NIL_RTTHREAD);
119
120 /* sent an update request */
121#if defined(RT_OS_WINDOWS)
122 ::SetEvent(mUpdateReq);
123#elif defined(RT_OS_OS2)
124 RTSemEventSignal(mUpdateReq);
125#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
126 /* use short timeouts, as we expect changes */
127 ASMAtomicUoWriteU8(&mUpdateAdaptCtr, RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1);
128 RTSemEventSignal(mUpdateReq);
129#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
130 RTSemEventSignal(mUpdateReq);
131#else
132# error "Port me!"
133#endif
134}
135
136/**
137 * Adds a process to the list of processes to be reaped. This call should be
138 * followed by a call to update() to cause the necessary actions immediately,
139 * in case the process crashes straight away.
140 */
141void VirtualBox::ClientWatcher::addProcess(RTPROCESS pid)
142{
143 AssertReturnVoid(mThread != NIL_RTTHREAD);
144 /* @todo r=klaus, do the reaping on all platforms! */
145#ifndef RT_OS_WINDOWS
146 AutoWriteLock alock(mLock COMMA_LOCKVAL_SRC_POS);
147 mProcesses.push_back(pid);
148#endif
149}
150
151/**
152 * Thread worker function that watches the termination of all client processes
153 * that have open sessions using IMachine::LockMachine()
154 */
155/*static*/
156DECLCALLBACK(int) VirtualBox::ClientWatcher::worker(RTTHREAD /* thread */, void *pvUser)
157{
158 LogFlowFuncEnter();
159
160 VirtualBox::ClientWatcher *that = (VirtualBox::ClientWatcher *)pvUser;
161 Assert(that);
162
163 typedef std::vector<ComObjPtr<Machine> > MachineVector;
164 typedef std::vector<ComObjPtr<SessionMachine> > SessionMachineVector;
165
166 SessionMachineVector machines;
167 MachineVector spawnedMachines;
168
169 size_t cnt = 0;
170 size_t cntSpawned = 0;
171
172 VirtualBoxBase::initializeComForThread();
173
174#if defined(RT_OS_WINDOWS)
175
176 /// @todo (dmik) processes reaping!
177
178 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
179 handles[0] = that->mUpdateReq;
180
181 do
182 {
183 AutoCaller autoCaller(that->mVirtualBox);
184 /* VirtualBox has been early uninitialized, terminate */
185 if (!autoCaller.isOk())
186 break;
187
188 do
189 {
190 /* release the caller to let uninit() ever proceed */
191 autoCaller.release();
192
193 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
194 handles,
195 FALSE,
196 INFINITE);
197
198 /* Restore the caller before using VirtualBox. If it fails, this
199 * means VirtualBox is being uninitialized and we must terminate. */
200 autoCaller.add();
201 if (!autoCaller.isOk())
202 break;
203
204 bool update = false;
205
206 if (rc == WAIT_OBJECT_0)
207 {
208 /* update event is signaled */
209 update = true;
210 }
211 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
212 {
213 /* machine mutex is released */
214 (machines[rc - WAIT_OBJECT_0 - 1])->i_checkForDeath();
215 update = true;
216 }
217 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
218 {
219 /* machine mutex is abandoned due to client process termination */
220 (machines[rc - WAIT_ABANDONED_0 - 1])->i_checkForDeath();
221 update = true;
222 }
223 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
224 {
225 /* spawned VM process has terminated (normally or abnormally) */
226 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
227 i_checkForSpawnFailure();
228 update = true;
229 }
230
231 if (update)
232 {
233 /* close old process handles */
234 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
235 CloseHandle(handles[i]);
236
237 // get reference to the machines list in VirtualBox
238 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();
239
240 // lock the machines list for reading
241 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
242
243 /* obtain a new set of opened machines */
244 cnt = 0;
245 machines.clear();
246
247 for (MachinesOList::iterator it = allMachines.begin();
248 it != allMachines.end();
249 ++it)
250 {
251 /// @todo handle situations with more than 64 objects
252 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
253 ("MAXIMUM_WAIT_OBJECTS reached"));
254
255 ComObjPtr<SessionMachine> sm;
256 if ((*it)->i_isSessionOpenOrClosing(sm))
257 {
258 AutoCaller smCaller(sm);
259 if (smCaller.isOk())
260 {
261 AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS);
262 Machine::ClientToken *ct = sm->i_getClientToken();
263 if (ct)
264 {
265 HANDLE ipcSem = ct->getToken();
266 machines.push_back(sm);
267 handles[1 + cnt] = ipcSem;
268 ++cnt;
269 }
270 }
271 }
272 }
273
274 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
275
276 /* obtain a new set of spawned machines */
277 cntSpawned = 0;
278 spawnedMachines.clear();
279
280 for (MachinesOList::iterator it = allMachines.begin();
281 it != allMachines.end();
282 ++it)
283 {
284 /// @todo handle situations with more than 64 objects
285 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
286 ("MAXIMUM_WAIT_OBJECTS reached"));
287
288 if ((*it)->i_isSessionSpawning())
289 {
290 ULONG pid;
291 HRESULT hrc = (*it)->COMGETTER(SessionPID)(&pid);
292 if (SUCCEEDED(hrc))
293 {
294 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
295 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
296 pid, GetLastError()));
297 if (ph != NULL)
298 {
299 spawnedMachines.push_back(*it);
300 handles[1 + cnt + cntSpawned] = ph;
301 ++cntSpawned;
302 }
303 }
304 }
305 }
306
307 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
308
309 // machines lock unwinds here
310 }
311 }
312 while (true);
313 }
314 while (0);
315
316 /* close old process handles */
317 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
318 CloseHandle(handles[i]);
319
320 /* release sets of machines if any */
321 machines.clear();
322 spawnedMachines.clear();
323
324 ::CoUninitialize();
325
326#elif defined(RT_OS_OS2)
327
328 /// @todo (dmik) processes reaping!
329
330 /* according to PMREF, 64 is the maximum for the muxwait list */
331 SEMRECORD handles[64];
332
333 HMUX muxSem = NULLHANDLE;
334
335 do
336 {
337 AutoCaller autoCaller(that->mVirtualBox);
338 /* VirtualBox has been early uninitialized, terminate */
339 if (!autoCaller.isOk())
340 break;
341
342 do
343 {
344 /* release the caller to let uninit() ever proceed */
345 autoCaller.release();
346
347 int vrc = RTSemEventWait(that->mUpdateReq, 500);
348
349 /* Restore the caller before using VirtualBox. If it fails, this
350 * means VirtualBox is being uninitialized and we must terminate. */
351 autoCaller.add();
352 if (!autoCaller.isOk())
353 break;
354
355 bool update = false;
356 bool updateSpawned = false;
357
358 if (RT_SUCCESS(vrc))
359 {
360 /* update event is signaled */
361 update = true;
362 updateSpawned = true;
363 }
364 else
365 {
366 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
367 ("RTSemEventWait returned %Rrc\n", vrc));
368
369 /* are there any mutexes? */
370 if (cnt > 0)
371 {
372 /* figure out what's going on with machines */
373
374 unsigned long semId = 0;
375 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
376 SEM_IMMEDIATE_RETURN, &semId);
377
378 if (arc == NO_ERROR)
379 {
380 /* machine mutex is normally released */
381 Assert(semId >= 0 && semId < cnt);
382 if (semId >= 0 && semId < cnt)
383 {
384#if 0//def DEBUG
385 {
386 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
387 LogFlowFunc(("released mutex: machine='%ls'\n",
388 machines[semId]->name().raw()));
389 }
390#endif
391 machines[semId]->i_checkForDeath();
392 }
393 update = true;
394 }
395 else if (arc == ERROR_SEM_OWNER_DIED)
396 {
397 /* machine mutex is abandoned due to client process
398 * termination; find which mutex is in the Owner Died
399 * state */
400 for (size_t i = 0; i < cnt; ++i)
401 {
402 PID pid; TID tid;
403 unsigned long reqCnt;
404 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
405 if (arc == ERROR_SEM_OWNER_DIED)
406 {
407 /* close the dead mutex as asked by PMREF */
408 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
409
410 Assert(i >= 0 && i < cnt);
411 if (i >= 0 && i < cnt)
412 {
413#if 0//def DEBUG
414 {
415 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
416 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
417 machines[i]->name().raw()));
418 }
419#endif
420 machines[i]->i_checkForDeath();
421 }
422 }
423 }
424 update = true;
425 }
426 else
427 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
428 ("DosWaitMuxWaitSem returned %d\n", arc));
429 }
430
431 /* are there any spawning sessions? */
432 if (cntSpawned > 0)
433 {
434 for (size_t i = 0; i < cntSpawned; ++i)
435 updateSpawned |= (spawnedMachines[i])->
436 i_checkForSpawnFailure();
437 }
438 }
439
440 if (update || updateSpawned)
441 {
442 // get reference to the machines list in VirtualBox
443 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();
444
445 // lock the machines list for reading
446 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
447
448 if (update)
449 {
450 /* close the old muxsem */
451 if (muxSem != NULLHANDLE)
452 ::DosCloseMuxWaitSem(muxSem);
453
454 /* obtain a new set of opened machines */
455 cnt = 0;
456 machines.clear();
457
458 for (MachinesOList::iterator it = allMachines.begin();
459 it != allMachines.end(); ++it)
460 {
461 /// @todo handle situations with more than 64 objects
462 AssertMsg(cnt <= 64 /* according to PMREF */,
463 ("maximum of 64 mutex semaphores reached (%d)",
464 cnt));
465
466 ComObjPtr<SessionMachine> sm;
467 if ((*it)->i_isSessionOpenOrClosing(sm))
468 {
469 AutoCaller smCaller(sm);
470 if (smCaller.isOk())
471 {
472 AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS);
473 ClientToken *ct = sm->i_getClientToken();
474 if (ct)
475 {
476 HMTX ipcSem = ct->getToken();
477 machines.push_back(sm);
478 handles[cnt].hsemCur = (HSEM)ipcSem;
479 handles[cnt].ulUser = cnt;
480 ++cnt;
481 }
482 }
483 }
484 }
485
486 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
487
488 if (cnt > 0)
489 {
490 /* create a new muxsem */
491 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
492 handles,
493 DCMW_WAIT_ANY);
494 AssertMsg(arc == NO_ERROR,
495 ("DosCreateMuxWaitSem returned %d\n", arc));
496 NOREF(arc);
497 }
498 }
499
500 if (updateSpawned)
501 {
502 /* obtain a new set of spawned machines */
503 spawnedMachines.clear();
504
505 for (MachinesOList::iterator it = allMachines.begin();
506 it != allMachines.end(); ++it)
507 {
508 if ((*it)->i_isSessionSpawning())
509 spawnedMachines.push_back(*it);
510 }
511
512 cntSpawned = spawnedMachines.size();
513 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
514 }
515 }
516 }
517 while (true);
518 }
519 while (0);
520
521 /* close the muxsem */
522 if (muxSem != NULLHANDLE)
523 ::DosCloseMuxWaitSem(muxSem);
524
525 /* release sets of machines if any */
526 machines.clear();
527 spawnedMachines.clear();
528
529#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
530
531 bool update = false;
532 bool updateSpawned = false;
533
534 do
535 {
536 AutoCaller autoCaller(that->mVirtualBox);
537 if (!autoCaller.isOk())
538 break;
539
540 do
541 {
542 /* release the caller to let uninit() ever proceed */
543 autoCaller.release();
544
545 /* determine wait timeout adaptively: after updating information
546 * relevant to the client watcher, check a few times more
547 * frequently. This ensures good reaction time when the signalling
548 * has to be done a bit before the actual change for technical
549 * reasons, and saves CPU cycles when no activities are expected. */
550 RTMSINTERVAL cMillies;
551 {
552 uint8_t uOld, uNew;
553 do
554 {
555 uOld = ASMAtomicUoReadU8(&that->mUpdateAdaptCtr);
556 uNew = uOld ? uOld - 1 : uOld;
557 } while (!ASMAtomicCmpXchgU8(&that->mUpdateAdaptCtr, uNew, uOld));
558 Assert(uOld <= RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1);
559 cMillies = s_aUpdateTimeoutSteps[uOld];
560 }
561
562 int rc = RTSemEventWait(that->mUpdateReq, cMillies);
563
564 /*
565 * Restore the caller before using VirtualBox. If it fails, this
566 * means VirtualBox is being uninitialized and we must terminate.
567 */
568 autoCaller.add();
569 if (!autoCaller.isOk())
570 break;
571
572 if (RT_SUCCESS(rc) || update || updateSpawned)
573 {
574 /* RT_SUCCESS(rc) means an update event is signaled */
575
576 // get reference to the machines list in VirtualBox
577 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();
578
579 // lock the machines list for reading
580 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
581
582 if (RT_SUCCESS(rc) || update)
583 {
584 /* obtain a new set of opened machines */
585 machines.clear();
586
587 for (MachinesOList::iterator it = allMachines.begin();
588 it != allMachines.end();
589 ++it)
590 {
591 ComObjPtr<SessionMachine> sm;
592 if ((*it)->i_isSessionOpenOrClosing(sm))
593 machines.push_back(sm);
594 }
595
596 cnt = machines.size();
597 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
598 }
599
600 if (RT_SUCCESS(rc) || updateSpawned)
601 {
602 /* obtain a new set of spawned machines */
603 spawnedMachines.clear();
604
605 for (MachinesOList::iterator it = allMachines.begin();
606 it != allMachines.end();
607 ++it)
608 {
609 if ((*it)->i_isSessionSpawning())
610 spawnedMachines.push_back(*it);
611 }
612
613 cntSpawned = spawnedMachines.size();
614 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
615 }
616
617 // machines lock unwinds here
618 }
619
620 update = false;
621 for (size_t i = 0; i < cnt; ++i)
622 update |= (machines[i])->i_checkForDeath();
623
624 updateSpawned = false;
625 for (size_t i = 0; i < cntSpawned; ++i)
626 updateSpawned |= (spawnedMachines[i])->i_checkForSpawnFailure();
627
628 /* reap child processes */
629 {
630 AutoWriteLock alock(that->mLock COMMA_LOCKVAL_SRC_POS);
631 if (that->mProcesses.size())
632 {
633 LogFlowFunc(("UPDATE: child process count = %d\n",
634 that->mProcesses.size()));
635 VirtualBox::ClientWatcher::ProcessList::iterator it = that->mProcesses.begin();
636 while (it != that->mProcesses.end())
637 {
638 RTPROCESS pid = *it;
639 RTPROCSTATUS status;
640 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
641 if (vrc == VINF_SUCCESS)
642 {
643 if ( status.enmReason != RTPROCEXITREASON_NORMAL
644 || status.iStatus != RTEXITCODE_SUCCESS)
645 {
646 switch (status.enmReason)
647 {
648 default:
649 case RTPROCEXITREASON_NORMAL:
650 LogRel(("Reaper: Pid %d (%x) exited normally: %d (%#x)\n",
651 pid, pid, status.iStatus, status.iStatus));
652 break;
653 case RTPROCEXITREASON_ABEND:
654 LogRel(("Reaper: Pid %d (%x) abended: %d (%#x)\n",
655 pid, pid, status.iStatus, status.iStatus));
656 break;
657 case RTPROCEXITREASON_SIGNAL:
658 LogRel(("Reaper: Pid %d (%x) was signalled: %d (%#x)\n",
659 pid, pid, status.iStatus, status.iStatus));
660 break;
661 }
662 }
663 else
664 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
665 pid, pid, status.iStatus,
666 status.enmReason));
667 it = that->mProcesses.erase(it);
668 }
669 else
670 {
671 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
672 pid, pid, vrc));
673 if (vrc != VERR_PROCESS_RUNNING)
674 {
675 /* remove the process if it is not already running */
676 it = that->mProcesses.erase(it);
677 }
678 else
679 ++it;
680 }
681 }
682 }
683 }
684 }
685 while (true);
686 }
687 while (0);
688
689 /* release sets of machines if any */
690 machines.clear();
691 spawnedMachines.clear();
692
693#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
694
695 bool updateSpawned = false;
696
697 do
698 {
699 AutoCaller autoCaller(that->mVirtualBox);
700 if (!autoCaller.isOk())
701 break;
702
703 do
704 {
705 /* release the caller to let uninit() ever proceed */
706 autoCaller.release();
707
708 /* determine wait timeout adaptively: after updating information
709 * relevant to the client watcher, check a few times more
710 * frequently. This ensures good reaction time when the signalling
711 * has to be done a bit before the actual change for technical
712 * reasons, and saves CPU cycles when no activities are expected. */
713 RTMSINTERVAL cMillies;
714 {
715 uint8_t uOld, uNew;
716 do
717 {
718 uOld = ASMAtomicUoReadU8(&that->mUpdateAdaptCtr);
719 uNew = uOld ? uOld - 1 : uOld;
720 } while (!ASMAtomicCmpXchgU8(&that->mUpdateAdaptCtr, uNew, uOld));
721 Assert(uOld <= RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1);
722 cMillies = s_aUpdateTimeoutSteps[uOld];
723 }
724
725 int rc = RTSemEventWait(that->mUpdateReq, cMillies);
726
727 /*
728 * Restore the caller before using VirtualBox. If it fails, this
729 * means VirtualBox is being uninitialized and we must terminate.
730 */
731 autoCaller.add();
732 if (!autoCaller.isOk())
733 break;
734
735 /** @todo this quite big effort for catching machines in spawning
736 * state which can't be caught by the token mechanism (as the token
737 * can't be in the other process yet) could be eliminated if the
738 * reaping is made smarter, having cross-reference information
739 * from the pid to the corresponding machine object. Both cases do
740 * more or less the same thing anyway. */
741 if (RT_SUCCESS(rc) || updateSpawned)
742 {
743 /* RT_SUCCESS(rc) means an update event is signaled */
744
745 // get reference to the machines list in VirtualBox
746 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->i_getMachinesList();
747
748 // lock the machines list for reading
749 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
750
751 if (RT_SUCCESS(rc) || updateSpawned)
752 {
753 /* obtain a new set of spawned machines */
754 spawnedMachines.clear();
755
756 for (MachinesOList::iterator it = allMachines.begin();
757 it != allMachines.end();
758 ++it)
759 {
760 if ((*it)->i_isSessionSpawning())
761 spawnedMachines.push_back(*it);
762 }
763
764 cntSpawned = spawnedMachines.size();
765 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
766 }
767
768 NOREF(cnt);
769 // machines lock unwinds here
770 }
771
772 updateSpawned = false;
773 for (size_t i = 0; i < cntSpawned; ++i)
774 updateSpawned |= (spawnedMachines[i])->i_checkForSpawnFailure();
775
776 /* reap child processes */
777 {
778 AutoWriteLock alock(that->mLock COMMA_LOCKVAL_SRC_POS);
779 if (that->mProcesses.size())
780 {
781 LogFlowFunc(("UPDATE: child process count = %d\n",
782 that->mProcesses.size()));
783 VirtualBox::ClientWatcher::ProcessList::iterator it = that->mProcesses.begin();
784 while (it != that->mProcesses.end())
785 {
786 RTPROCESS pid = *it;
787 RTPROCSTATUS status;
788 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
789 if (vrc == VINF_SUCCESS)
790 {
791 if ( status.enmReason != RTPROCEXITREASON_NORMAL
792 || status.iStatus != RTEXITCODE_SUCCESS)
793 {
794 switch (status.enmReason)
795 {
796 default:
797 case RTPROCEXITREASON_NORMAL:
798 LogRel(("Reaper: Pid %d (%x) exited normally: %d (%#x)\n",
799 pid, pid, status.iStatus, status.iStatus));
800 break;
801 case RTPROCEXITREASON_ABEND:
802 LogRel(("Reaper: Pid %d (%x) abended: %d (%#x)\n",
803 pid, pid, status.iStatus, status.iStatus));
804 break;
805 case RTPROCEXITREASON_SIGNAL:
806 LogRel(("Reaper: Pid %d (%x) was signalled: %d (%#x)\n",
807 pid, pid, status.iStatus, status.iStatus));
808 break;
809 }
810 }
811 else
812 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
813 pid, pid, status.iStatus,
814 status.enmReason));
815 it = that->mProcesses.erase(it);
816 }
817 else
818 {
819 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
820 pid, pid, vrc));
821 if (vrc != VERR_PROCESS_RUNNING)
822 {
823 /* remove the process if it is not already running */
824 it = that->mProcesses.erase(it);
825 }
826 else
827 ++it;
828 }
829 }
830 }
831 }
832 }
833 while (true);
834 }
835 while (0);
836
837 /* release sets of machines if any */
838 machines.clear();
839 spawnedMachines.clear();
840
841#else
842# error "Port me!"
843#endif
844
845 VirtualBoxBase::uninitializeComForThread();
846
847 LogFlowFuncLeave();
848 return 0;
849}
850/* 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