VirtualBox

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

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

VirtualBoxClientListImpl.cpp -> src-global/win/

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette