VirtualBox

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

Last change on this file since 55200 was 51498, checked in by vboxsync, 11 years ago

6813 - MachineImpl use of server side wrappers + misc mods on other classes

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