VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/VirtualBoxClientListImpl.cpp@ 71607

Last change on this file since 71607 was 71183, checked in by vboxsync, 7 years ago

VirtualBoxClientListImpl.cpp: Fixed invalid LogRel format string (%\n); did some cleanups, left a bunch of r=bird comments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.7 KB
Line 
1/* $Id: VirtualBoxClientListImpl.cpp 71183 2018-03-03 00:53:44Z vboxsync $ */
2/** @file
3 * VBox Global COM Class implementation.
4 *
5 * @todo r=bird: Why is this file in src-all? Shouldn't it be in src-global/win/?!?
6 */
7
8/*
9 * Copyright (C) 2017-2018 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21#include "Logging.h"
22#include "VirtualBoxClientListImpl.h"
23
24#include <iprt/process.h>
25#include <iprt/asm.h>
26
27
28////////////////// CClientListWatcher implementation /////////////////
29
30/**
31 * Helper class that tracks existance of registered client processes.
32 *
33 * It removes the client process from client list if it shutdowned unexpectedly,
34 * without calling DeRegisterClient().
35 *
36 * It also notifies VBoxSVC and VBoxSDS that this client process finished.
37 */
38class CClientListWatcher
39{
40public:
41 CClientListWatcher(TClientSet& list, RTCRITSECTRW& m_clientListCritSect);
42 virtual ~CClientListWatcher();
43
44protected:
45 static DECLCALLBACK(int) WatcherWorker(RTTHREAD ThreadSelf, void *pvUser);
46 void NotifySDSAllClientsFinished();
47 // m_WatcherThread is static to check that single watcher thread used only
48 static volatile RTTHREAD m_WatcherThread;
49 volatile bool m_fWatcherRunning;
50 TClientSet& m_clientList;
51 RTCRITSECTRW& m_clientListCritSect;
52 RTSEMEVENT m_wakeUpWatcherEvent;
53};
54
55volatile RTTHREAD CClientListWatcher::m_WatcherThread = NULL; /** @todo r=bird: Wrong prefix for a static variable (s_)*/
56
57CClientListWatcher::CClientListWatcher(TClientSet& list, RTCRITSECTRW& clientListCritSect)
58 : m_clientList(list), m_clientListCritSect(clientListCritSect)
59{
60 Assert(ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread) == NULL);
61
62 if (ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread) != NULL)
63 {
64 LogRelFunc(("Error: Watcher thread already created!\n"));
65 return;
66 }
67
68 int rc = RTSemEventCreate(&m_wakeUpWatcherEvent);
69 if (RT_FAILURE(rc))
70 {
71 LogRelFunc(("Error: Failed to create wake up event for watcher thread: %Rrs\n", rc));
72 return;
73 }
74
75 /** @todo r=bird: Hanging indent on '(', please. */
76 RTTHREAD watcherThread;
77 rc = RTThreadCreate(&watcherThread,
78 (PFNRTTHREAD)CClientListWatcher::WatcherWorker,
79 this, // pVUser
80 0, // cbStack
81 RTTHREADTYPE_DEFAULT,
82 RTTHREADFLAGS_WAITABLE,
83 "CLWatcher");
84 if (RT_SUCCESS(rc))
85 {
86 ASMAtomicWritePtr((void* volatile*)&CClientListWatcher::m_WatcherThread, watcherThread);
87 LogRelFunc(("Created client list watcher thread.\n"));
88 }
89 else
90 LogRelFunc(("Failed to create client list watcher thread: %Rrs\n", rc));
91}
92
93
94CClientListWatcher::~CClientListWatcher()
95{
96 Assert(ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread));
97
98 // mark watcher thread to finish
99 ASMAtomicWriteBool(&m_fWatcherRunning, false);
100
101 // Wake up watcher thread to finish it faster
102 int rc = RTSemEventSignal(m_wakeUpWatcherEvent);
103 Assert(RT_SUCCESS(rc));
104
105 rc = RTThreadWait(CClientListWatcher::m_WatcherThread, RT_INDEFINITE_WAIT, NULL);
106 if (RT_FAILURE(rc))
107 LogRelFunc(("Error: watcher thread didn't finished. Possible thread leak. %Rrs\n", rc));
108 else
109 LogRelFunc(("Watcher thread finished.\n"));
110
111 ASMAtomicWriteNullPtr((void* volatile*)&CClientListWatcher::m_WatcherThread);
112
113 RTSemEventDestroy(m_wakeUpWatcherEvent);
114}
115
116/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
117/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
118/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
119
120/*
121* Notifies the VBoxSDS that API client list is empty.
122* Initiates the chain of closing VBoxSDS/VBoxSVC.
123* VBoxSDS, in it's turn notifies VBoxSvc that relates on it,
124* VBoxSvc instances releases itself and releases their references to VBoxSDS.
125* in result VBoxSDS and VBoxSvc finishes itself after delay
126*/
127void CClientListWatcher::NotifySDSAllClientsFinished()
128{
129 ComPtr<IVirtualBoxSDS> ptrVirtualBoxSDS;
130 /*
131 * Connect to VBoxSDS.
132 */
133 HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS,
134 (void **)ptrVirtualBoxSDS.asOutParam());
135 if (SUCCEEDED(hrc))
136 {
137 LogRelFunc(("Notifying SDS that all API clients finished...\n"));
138 ptrVirtualBoxSDS->NotifyClientsFinished();
139 }
140}
141
142
143/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
144/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
145/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
146/*
147* Deregister all staled VBoxSVC through VBoxSDS and forcebly close VBoxSVC process
148* Parameters:
149* ThreadSelf - current thread id
150* pvUser - pointer to CClientListWatcher that created this thread. */
151DECLCALLBACK(int) CClientListWatcher::WatcherWorker(RTTHREAD ThreadSelf, void *pvUser)
152{
153 NOREF(ThreadSelf);
154 Assert(ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread));
155 LogRelFunc(("Enter watcher thread\n"));
156
157 CClientListWatcher *pThis = (CClientListWatcher *)pvUser;
158 Assert(pThis);
159
160 ASMAtomicWriteBool(&pThis->m_fWatcherRunning, true);
161
162 while (ASMAtomicReadBool(&pThis->m_fWatcherRunning))
163 {
164 /* remove finished API clients from list */
165 int rc = RTCritSectRwEnterShared(&pThis->m_clientListCritSect);
166 Assert(RT_SUCCESS(rc));
167 NOREF(rc);
168
169 TClientSet::iterator it = pThis->m_clientList.begin();
170 TClientSet::iterator end = pThis->m_clientList.end();
171 for (; it != end; ++it)
172 {
173/** @todo r=bird: this is a bit inefficient because RTProcWait will try open
174 * all the process each time. Would be better have registerClient open the
175 * process and just to a WaitForSingleObject here (if you want to be really
176 * performant, you could keep wait arrays of 64 objects each and use
177 * WaitForMultipleObjects on each of them).
178 *
179 * And please, don't give me the portability argument here, because this
180 * RTProcWait call only works on windows. We're not the parent of any of these
181 * clients and can't wait on them.
182 */
183 // check status of client process by his pid
184 int rc = RTProcWait(*it, RTPROCWAIT_FLAGS_NOBLOCK, NULL);
185 if (rc == VERR_PROCESS_NOT_FOUND)
186 {
187 LogRelFunc(("Finished process detected: %d\n", *it));
188 /* delete finished process from client list */
189 it = pThis->m_clientList.erase(it);
190
191 if (pThis->m_clientList.empty())
192 {
193 /*
194 Starts here chain of events between SDS and VBoxSVC
195 to shutdown them in case when all clients finished and
196 some of them crashed
197 */
198 pThis->NotifySDSAllClientsFinished();
199 }
200 }
201 }
202
203 rc = RTCritSectRwLeaveShared(&pThis->m_clientListCritSect);
204 Assert(RT_SUCCESS(rc));
205
206 /*
207 * Wait for two second before next iteration.
208 * Destructor will wake up it immidietely.
209 */
210/** @todo r=bird: This is where wait for multiple objects would be really nice, as
211 * you could wait on the first 63 client processes here in addition to the event.
212 * That would speed up the response time. */
213 RTSemEventWait(pThis->m_wakeUpWatcherEvent, 2000);
214 }
215 LogRelFunc(("Finish watcher thread. Client list size: %d\n", pThis->m_clientList.size()));
216 return 0;
217}
218
219///////////////////////// VirtualBoxClientList implementation //////////////////////////
220
221HRESULT VirtualBoxClientList::FinalConstruct()
222{
223 int rc = RTCritSectRwInit(&m_MapCritSect);
224 AssertLogRelRCReturn(rc, E_FAIL);
225
226 try
227 {
228 m_pWatcher = new CClientListWatcher(m_ClientSet, m_MapCritSect);
229 }
230 catch (std::bad_alloc)
231 {
232 AssertLogRelFailedReturn(E_OUTOFMEMORY);
233 }
234 Assert(m_pWatcher);
235
236 AutoInitSpan autoInitSpan(this);
237 autoInitSpan.setSucceeded();
238 BaseFinalConstruct();
239
240 LogRelFunc(("VirtualBoxClientList initialized.\n"));
241 return S_OK;
242}
243
244void VirtualBoxClientList::FinalRelease()
245{
246 Assert(m_pWatcher);
247 if (m_pWatcher)
248 {
249 delete m_pWatcher;
250 m_pWatcher = NULL;
251 }
252
253 int rc = RTCritSectRwDelete(&m_MapCritSect);
254 AssertRC(rc);
255
256 BaseFinalRelease();
257 LogRelFunc(("VirtualBoxClientList released.\n"));
258}
259
260/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
261/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
262/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
263/*
264* Deregister API client to add it to API client list
265* API client process calls this function at start to include this process to client list
266*
267*/
268HRESULT VirtualBoxClientList::registerClient(LONG aPid)
269{
270 int rc = RTCritSectRwEnterExcl(&m_MapCritSect);
271 AssertRCReturn(rc, E_FAIL);
272 Assert(m_pWatcher);
273
274 try
275 {
276 m_ClientSet.insert(aPid);
277 }
278 catch (std::bad_alloc)
279 {
280 RTCritSectRwLeaveExcl(&m_MapCritSect);
281 AssertLogRelFailedReturn(E_OUTOFMEMORY);
282 }
283
284 rc = RTCritSectRwLeaveExcl(&m_MapCritSect);
285 AssertRC(rc);
286 LogRelFunc(("VirtualBoxClientList client registered. pid: %d\n", aPid, rc));
287 return S_OK;
288}
289
290
291/**
292 * Returns PIDs of the API client processes.
293 *
294 * @returns COM status code.
295 * @param aEnvironment Reference to vector that is to receive the PID list.
296 *
297 * @todo r=bird: There is no obvious reason why this is called 'aEnvironment', a
298 * more natural name would be 'aPids'.
299 */
300HRESULT VirtualBoxClientList::getClients(std::vector<LONG> &aEnvironment)
301{
302 int rc = RTCritSectRwEnterShared(&m_MapCritSect);
303 AssertLogRelRCReturn(rc, E_FAIL);
304 if (!m_ClientSet.empty())
305 {
306 Assert(aEnvironment.empty());
307 size_t const cClients = m_ClientSet.size();
308 try
309 {
310 aEnvironment.reserve(cClients);
311 aEnvironment.assign(m_ClientSet.begin(), m_ClientSet.end());
312 }
313 catch (std::bad_alloc)
314 {
315 RTCritSectRwLeaveShared(&m_MapCritSect);
316 AssertLogRelMsgFailedReturn(("cClients=%zu\n", cClients), E_OUTOFMEMORY);
317 }
318 Assert(aEnvironment.size() == cClients);
319 }
320 else
321 {
322 LogFunc(("Client list is empty\n"));
323 }
324
325 rc = RTCritSectRwLeaveShared(&m_MapCritSect);
326 AssertRC(rc);
327
328 LogRelFunc(("VirtualBoxClientList client list requested.\n"));
329 return S_OK;
330}
331
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