VirtualBox

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

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

VirtualBoxClientListImpl.cpp: scm fix

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

© 2023 Oracle
ContactPrivacy policyTerms of Use