VirtualBox

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

Last change on this file since 72057 was 71714, checked in by vboxsync, 7 years ago

VirtualBoxClientListImpl.cpp: More todos.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.2 KB
Line 
1/* $Id: VirtualBoxClientListImpl.cpp 71714 2018-04-06 17:30:32Z 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 RTTHREAD watcherThread;
76 rc = RTThreadCreate(&watcherThread,
77 (PFNRTTHREAD)CClientListWatcher::WatcherWorker,
78 this, // pVUser
79 0, // cbStack
80 RTTHREADTYPE_DEFAULT,
81 RTTHREADFLAGS_WAITABLE,
82 "CLWatcher");
83 if (RT_SUCCESS(rc))
84 {
85 ASMAtomicWritePtr((void* volatile*)&CClientListWatcher::m_WatcherThread, watcherThread);
86 LogRelFunc(("Created client list watcher thread.\n"));
87 }
88 else
89 LogRelFunc(("Failed to create client list watcher thread: %Rrs\n", rc));
90}
91
92
93CClientListWatcher::~CClientListWatcher()
94{
95 /** @todo r=bird: This assertion is patently wrong in the unlikely
96 * case that RTThreadCreate fails... This is why we either let
97 * constructor throw stuff or we have separate init() methods
98 * for doing things that may fail */
99 Assert(ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread));
100
101 // mark watcher thread to finish
102 ASMAtomicWriteBool(&m_fWatcherRunning, false);
103
104 // Wake up watcher thread to finish it faster
105 int rc = RTSemEventSignal(m_wakeUpWatcherEvent);
106 Assert(RT_SUCCESS(rc));
107
108 rc = RTThreadWait(CClientListWatcher::m_WatcherThread, RT_INDEFINITE_WAIT, NULL);
109 if (RT_FAILURE(rc))
110 LogRelFunc(("Error: watcher thread didn't finished. Possible thread leak. %Rrs\n", rc));
111 else
112 LogRelFunc(("Watcher thread finished.\n"));
113
114 ASMAtomicWriteNullPtr((void* volatile*)&CClientListWatcher::m_WatcherThread);
115
116 RTSemEventDestroy(m_wakeUpWatcherEvent);
117}
118
119/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
120/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
121/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
122
123/*
124* Notifies the VBoxSDS that API client list is empty.
125* Initiates the chain of closing VBoxSDS/VBoxSVC.
126* VBoxSDS, in it's turn notifies VBoxSvc that relates on it,
127* VBoxSvc instances releases itself and releases their references to VBoxSDS.
128* in result VBoxSDS and VBoxSvc finishes itself after delay
129*/
130void CClientListWatcher::NotifySDSAllClientsFinished()
131{
132 ComPtr<IVirtualBoxSDS> ptrVirtualBoxSDS;
133 /*
134 * Connect to VBoxSDS.
135 */
136 HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS,
137 (void **)ptrVirtualBoxSDS.asOutParam());
138 if (SUCCEEDED(hrc))
139 {
140 LogRelFunc(("Notifying SDS that all API clients finished...\n"));
141 ptrVirtualBoxSDS->NotifyClientsFinished();
142 }
143}
144
145
146/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
147/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
148/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
149/*
150* Deregister all staled VBoxSVC through VBoxSDS and forcebly close VBoxSVC process
151* Parameters:
152* ThreadSelf - current thread id
153* pvUser - pointer to CClientListWatcher that created this thread. */
154DECLCALLBACK(int) CClientListWatcher::WatcherWorker(RTTHREAD ThreadSelf, void *pvUser)
155{
156 NOREF(ThreadSelf);
157 /** @todo r=bird: This will fail once in a while because you don't know
158 * for sure how the scheduling is going to be. So, RTThreadCreate
159 * may return and set g_hWatcherThread after the thread started
160 * executing and got here! */
161 Assert(ASMAtomicReadPtr((void* volatile*)&CClientListWatcher::m_WatcherThread));
162 LogRelFunc(("Enter watcher thread\n"));
163
164 CClientListWatcher *pThis = (CClientListWatcher *)pvUser;
165 Assert(pThis);
166
167 ASMAtomicWriteBool(&pThis->m_fWatcherRunning, true);
168
169 while (ASMAtomicReadBool(&pThis->m_fWatcherRunning))
170 {
171 /* remove finished API clients from list */
172 int rc = RTCritSectRwEnterShared(&pThis->m_clientListCritSect);
173 Assert(RT_SUCCESS(rc));
174 NOREF(rc);
175
176 TClientSet::iterator it = pThis->m_clientList.begin();
177 TClientSet::iterator end = pThis->m_clientList.end();
178 for (; it != end; ++it)
179 {
180/** @todo r=bird: this is a bit inefficient because RTProcWait will try open
181 * all the process each time. Would be better have registerClient open the
182 * process and just to a WaitForSingleObject here (if you want to be really
183 * performant, you could keep wait arrays of 64 objects each and use
184 * WaitForMultipleObjects on each of them).
185 *
186 * And please, don't give me the portability argument here, because this
187 * RTProcWait call only works on windows. We're not the parent of any of these
188 * clients and can't wait on them.
189 */
190 // check status of client process by his pid
191 int rc = RTProcWait(*it, RTPROCWAIT_FLAGS_NOBLOCK, NULL);
192 if (rc == VERR_PROCESS_NOT_FOUND)
193 {
194 LogRelFunc(("Finished process detected: %d\n", *it));
195 /* delete finished process from client list */
196 it = pThis->m_clientList.erase(it);
197
198 if (pThis->m_clientList.empty())
199 {
200 /*
201 Starts here chain of events between SDS and VBoxSVC
202 to shutdown them in case when all clients finished and
203 some of them crashed
204 */
205 pThis->NotifySDSAllClientsFinished();
206 }
207 }
208 }
209
210 rc = RTCritSectRwLeaveShared(&pThis->m_clientListCritSect);
211 Assert(RT_SUCCESS(rc));
212
213 /*
214 * Wait for two second before next iteration.
215 * Destructor will wake up it immidietely.
216 */
217/** @todo r=bird: This is where wait for multiple objects would be really nice, as
218 * you could wait on the first 63 client processes here in addition to the event.
219 * That would speed up the response time. */
220 RTSemEventWait(pThis->m_wakeUpWatcherEvent, 2000);
221 }
222 LogRelFunc(("Finish watcher thread. Client list size: %d\n", pThis->m_clientList.size()));
223 return 0;
224}
225
226///////////////////////// VirtualBoxClientList implementation //////////////////////////
227
228HRESULT VirtualBoxClientList::FinalConstruct()
229{
230 int rc = RTCritSectRwInit(&m_MapCritSect);
231 AssertLogRelRCReturn(rc, E_FAIL);
232
233 try
234 {
235 m_pWatcher = new CClientListWatcher(m_ClientSet, m_MapCritSect);
236 }
237 catch (std::bad_alloc)
238 {
239 AssertLogRelFailedReturn(E_OUTOFMEMORY);
240 }
241 Assert(m_pWatcher);
242
243 AutoInitSpan autoInitSpan(this);
244 autoInitSpan.setSucceeded();
245 BaseFinalConstruct();
246
247 LogRelFunc(("VirtualBoxClientList initialized.\n"));
248 return S_OK;
249}
250
251void VirtualBoxClientList::FinalRelease()
252{
253 Assert(m_pWatcher);
254 if (m_pWatcher)
255 {
256 delete m_pWatcher;
257 m_pWatcher = NULL;
258 }
259
260 int rc = RTCritSectRwDelete(&m_MapCritSect);
261 AssertRC(rc);
262
263 BaseFinalRelease();
264 LogRelFunc(("VirtualBoxClientList released.\n"));
265}
266
267/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
268/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
269/** @todo r=bird: DOXYGEN COMMENTS! DON'T FORGET IT!! */
270/*
271* Deregister API client to add it to API client list
272* API client process calls this function at start to include this process to client list
273*
274*/
275HRESULT VirtualBoxClientList::registerClient(LONG aPid)
276{
277 int rc = RTCritSectRwEnterExcl(&m_MapCritSect);
278 AssertRCReturn(rc, E_FAIL);
279 Assert(m_pWatcher);
280
281 try
282 {
283 m_ClientSet.insert(aPid);
284 }
285 catch (std::bad_alloc)
286 {
287 RTCritSectRwLeaveExcl(&m_MapCritSect);
288 AssertLogRelFailedReturn(E_OUTOFMEMORY);
289 }
290
291 rc = RTCritSectRwLeaveExcl(&m_MapCritSect);
292 AssertRC(rc);
293 LogRelFunc(("VirtualBoxClientList client registered. pid: %d\n", aPid, rc));
294 return S_OK;
295}
296
297
298/**
299 * Returns PIDs of the API client processes.
300 *
301 * @returns COM status code.
302 * @param aEnvironment Reference to vector that is to receive the PID list.
303 *
304 * @todo r=bird: There is no obvious reason why this is called 'aEnvironment', a
305 * more natural name would be 'aPids'.
306 */
307HRESULT VirtualBoxClientList::getClients(std::vector<LONG> &aEnvironment)
308{
309 int rc = RTCritSectRwEnterShared(&m_MapCritSect);
310 AssertLogRelRCReturn(rc, E_FAIL);
311 if (!m_ClientSet.empty())
312 {
313 Assert(aEnvironment.empty());
314 size_t const cClients = m_ClientSet.size();
315 try
316 {
317 aEnvironment.reserve(cClients);
318 aEnvironment.assign(m_ClientSet.begin(), m_ClientSet.end());
319 }
320 catch (std::bad_alloc)
321 {
322 RTCritSectRwLeaveShared(&m_MapCritSect);
323 AssertLogRelMsgFailedReturn(("cClients=%zu\n", cClients), E_OUTOFMEMORY);
324 }
325 Assert(aEnvironment.size() == cClients);
326 }
327 else
328 {
329 LogFunc(("Client list is empty\n"));
330 }
331
332 rc = RTCritSectRwLeaveShared(&m_MapCritSect);
333 AssertRC(rc);
334
335 LogRelFunc(("VirtualBoxClientList client list requested.\n"));
336 return S_OK;
337}
338
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