VirtualBox

source: vbox/trunk/src/VBox/Main/src-global/win/VirtualBoxClientListImpl.cpp@ 76066

Last change on this file since 76066 was 76066, checked in by vboxsync, 6 years ago

VBoxSDS: updates

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