VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/ObjectsTracker.cpp@ 108014

Last change on this file since 108014 was 107869, checked in by vboxsync, 5 weeks ago

scm fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.3 KB
Line 
1/* $Id: ObjectsTracker.cpp 107869 2025-01-21 06:59:08Z vboxsync $ */
2/** @file
3 * VirtualBox Object tracker implementation
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @page pg_main_objtracker
29 * Overview:
30 *
31 * Sometimes user wants to check or retrieve data or information about objects
32 * that may not exist at the time the user requests such objects. For example,
33 * some action was completed some time ago and all data related to this action
34 * was deleted, but the user missed this moment and later still wants to know
35 * how the action was completed. If it were possible to store such objects for
36 * some time, it would help the user.
37 *
38 * Example with Progress object
39 * 1. When Progress object is created it’s added into TrackedObjectCollector (call setTracked() in Progress::FinalConstruct()).
40 *
41 * 2. User keeps the Progress Id. VirtualBox API returns Progress Id to user everywhere where its’ needed.
42 * When user wants to know the status of action he makes a request to VirtualBox calling the function
43 * VirtualBox::getTrackedObject(). This function looks through TrackedObjectCollector and retrieves the information
44 * about a requested object and if one exists there returns a pointer to IUnknown interface.
45 * This pointer should be converted to the appropriate interface type (IProgress in this case). Next the data are
46 * extracted using the interface functions.
47 *
48 * 2.1. Approach 1.
49 * Getting information about a tracked object using VirtualBox API (VirtualBox frontend or any external client)
50 * - Call VirtualBox::getTrackedObjectIds() with a requested interface name and get back the list of tracked objects.
51 * - Go through the list, call VirtualBox::getTrackedObject() for each Id from the list.
52 * - Convert IUnknown interface to the requested interface.
53 * - Retrieve information about an object.
54 *
55 * See the full example 1 below.
56 *
57 * 2.2. Approach 2
58 * Getting information about a tracked object on server side (VBoxSVC) is:
59 * - Call TrackedObjectsCollector::getObjIdsByClassIID() with the interface name IID (our case is IID_IProgress)
60 * and get back the list of tracked objects.
61 * - Go through the list, call TrackedObjectsCollectorState::getObj() for each Id from the list.
62 * - Convert IUnknown interface to the requested interface.
63 * - Retrieve information about an object.
64 *
65 * See the full example 2 below.
66 *
67 * 3. Class TrackedObjectData keeps some additional data about the tracked object as creation time, last access time,
68 * life time and etc.
69 *
70 * 4. The last access time is updated for the requested object if it's needed (in the class TrackedObjectData).
71 *
72 * 5. For managing the items stored in the TrackedObjectCollector, a new thread TrackedObjectsThread is launched
73 * in VirtualBox::init() function.
74 *
75 * 6. Periodically (1 sec at present), the thread TrackedObjectsThread goes through the list of tracked objects,
76 * measures the difference between the current time and the creation time, if this value is greater than the life
77 * time the object is marked as "lifetime expired" and next the idletime is started for this object. When the idle
78 * time is expired the object is removed from the TrackedObjectsCollector.
79 *
80 * 7. There is a special case for an object with lifetime = 0. This means that the object has an infinite lifetime.
81 * How to handle this case? The idea is that the reference count of an unused object is 1, since the object is only
82 * represented inside the TrackedObjectCollector. When the count is 1, we mark the lifetime as expired and update
83 * the object with new data. After this action, the logic becomes standard (see point 6).
84 *
85 * Addon.
86 * Example 1. Getting information about a Progress tracked object on server side (VBoxSVC)
87 *
88 * std::vector<com::Utf8Str> lObjIdMap;
89 * gTrackedObjectsCollector.getObjIdsByClassIID(IID_IProgress, lObjIdMap);
90 * for (const com::Utf8Str &item : lObjIdMap)
91 * {
92 * if (gTrackedObjectsCollector.checkObj(item.c_str()))
93 * {
94 * TrackedObjectData temp;
95 * gTrackedObjectsCollector.getObj(item.c_str(), temp);
96 * Log2(("Tracked Progress Object with objectId %s was found\n", temp.objectIdStr().c_str()));
97 *
98 * ComPtr<IProgress> pProgress;
99 * temp.getInterface()->QueryInterface(IID_IProgress, (void **)pProgress.asOutParam());
100 * if (pProgress.isNotNull())
101 * {
102 * com::Bstr reqId(aId.toString());
103 * Bstr foundId;
104 * pProgress->COMGETTER(Id)(foundId.asOutParam());
105 * if (reqId == foundId)
106 * {
107 * BOOL aCompleted;
108 * pProgress->COMGETTER(Completed)(&aCompleted);
109 *
110 * BOOL aCanceled;
111 * pProgress->COMGETTER(Canceled)(&aCanceled);
112 *
113 * aProgressObject = pProgress;
114 * return S_OK;
115 * }
116 * }
117 * }
118 * }
119 *
120 * Example 2. Getting information about a Progress tracked object using VirtualBox API
121 *
122 * Utf8Str strObjUuid; // passed from the client
123 * Utf8Str strIfaceName;// passed from the client
124 * ComPtr<IVirtualBox> pVirtualBox;// set before. Usually each client has own ComPtr<IVirtualBox>.
125 *
126 * com::SafeArray<BSTR> ObjIDsList;
127 * hrc = pVirtualBox->GetTrackedObjectIds(Bstr(strIfaceName).raw(), ComSafeArrayAsOutParam(ObjIDsList));
128 * if (SUCCEEDED(hrc))
129 * {
130 * map < Bstr, TrackedObjInfo_T > lObjInfoMap;
131 * for (size_t i = 0; i < ObjIDsList.size(); ++i)
132 * {
133 * Bstr bstrObjId = ObjIDsList[i];
134 * if (bstrObjId.equals(strObjUuid.c_str()))
135 * {
136 * TrackedObjInfo_T objInfo;
137 * hrc = pVirtualBox->GetTrackedObject(bstrObjId.raw(),
138 * objInfo.pIUnknown.asOutParam(),
139 * &objInfo.enmState,
140 * &objInfo.creationTime,
141 * &objInfo.deletionTime);
142 *
143 * // print tracked object information as state, creation time, deletion time
144 * // objInfo.enmState, objInfo.creationTime, objInfo.deletionTime
145 *
146 * if (objInfo.enmState != TrackedObjectState_Invalid)
147 * {
148 * // Conversion IUnknown -> IProgress
149 * ComPtr<IProgress> pObj;
150 * objInfo.pIUnknown->QueryInterface(IID_IProgress, (void **)pObj.asOutParam());
151 * if (pObj.isNotNull())
152 * //print Progress Object data as description, completion, cancellation etc.
153 * }
154 * break;
155 * }
156 * }
157 * }
158 */
159
160#define LOG_GROUP LOG_GROUP_MAIN
161
162#include "LoggingNew.h"
163#include "VirtualBoxBase.h"
164#include "ObjectsTracker.h"
165#include <iprt/log.h>
166#include <iprt/stream.h>
167#include <iprt/time.h>
168#include <iprt/asm.h>
169#include <stdexcept>
170
171typedef std::map<com::Utf8Str, TrackedObjectData>::iterator IterTrObjData_T;
172typedef std::map<com::Utf8Str, TrackedObjectData>::const_iterator ConstIterTrObjData_T;
173
174static Utf8Str trackedObjectStateToStr(TrackedObjectState_T aState)
175{
176 Utf8Str strState("None");
177 switch (aState)
178 {
179 case TrackedObjectState_Alive:
180 strState = "Alive";
181 break;
182 case TrackedObjectState_Deleted:
183 strState = "Deleted";
184 break;
185 case TrackedObjectState_Invalid:
186 strState = "Invalid";
187 break;
188 case TrackedObjectState_None:
189 default:
190 strState = "None";
191 break;
192 }
193
194 return strState;
195}
196
197/////////////////////////////////////////////////////////////////////////////
198// TrackedObjectData
199/////////////////////////////////////////////////////////////////////////////
200TrackedObjectData::TrackedObjectData():
201 m_componentName("noname"),
202 m_lifeTime(0),
203 m_idleTime(1),
204 m_state(TrackedObjectState_None),
205 m_pIface(NULL)
206 {
207}
208
209TrackedObjectData::TrackedObjectData(const com::Guid &aObjId,
210 const com::Guid &aClassIID,
211 uint64_t aLifeTime,
212 uint64_t aIdleTime,
213 IUnknown* aPtr):
214
215 m_objId(aObjId),
216 m_classIID(aClassIID),
217 m_componentName("noname"),
218 m_lifeTime(aLifeTime),
219 m_idleTime(aIdleTime),
220 m_fIdleTimeStart(false),
221 m_fLifeTimeExpired(false),
222 m_pIface(aPtr)
223{
224 RTTimeNow(unconst(&m_creationTime));
225 m_lastAccessTime = m_creationTime;
226 m_state = TrackedObjectState_Alive;
227}
228
229TrackedObjectData::TrackedObjectData(const TrackedObjectData & that)
230{
231 LogFlowFuncEnter();
232 if (this != &that)
233 {
234 m_objId = that.m_objId;
235 m_classIID = that.m_classIID;
236 m_componentName = that.m_componentName;
237 m_lifeTime = that.m_lifeTime;
238 m_idleTime = that.m_idleTime;
239 m_pIface = that.m_pIface;
240 m_creationTime = that.m_creationTime;
241 m_deletionTime = that.m_deletionTime;
242 m_lastAccessTime = that.m_lastAccessTime;
243 m_idleTimeStart = that.m_idleTimeStart;
244 m_fIdleTimeStart = that.m_fIdleTimeStart;
245 m_fLifeTimeExpired = that.m_fLifeTimeExpired;
246 m_state = that.m_state;
247 }
248}
249
250TrackedObjectData::~TrackedObjectData()
251{
252 Log2(("%s destructor\n", __FUNCTION__));
253 Log2(("DELETED Object %s (class IID %s)\n",
254 m_objId.toString().c_str(),
255 m_classIID.toString().c_str()
256 ));
257}
258
259TrackedObjectData &TrackedObjectData::operator=(const TrackedObjectData & that)
260{
261 LogFlowFuncEnter();
262 if (this != &that)
263 {
264 m_objId = that.m_objId;
265 m_classIID = that.m_classIID;
266 m_componentName = that.m_componentName;
267 m_lifeTime = that.m_lifeTime;
268 m_idleTime = that.m_idleTime;
269 m_pIface = that.m_pIface;
270 m_creationTime = that.m_creationTime;
271 m_deletionTime = that.m_deletionTime;
272 m_lastAccessTime = that.m_lastAccessTime;
273 m_idleTimeStart = that.m_idleTimeStart;
274 m_fIdleTimeStart = that.m_fIdleTimeStart;
275 m_fLifeTimeExpired = that.m_fLifeTimeExpired;
276 m_state = that.m_state;
277 }
278
279 return *this;
280}
281
282RTTIMESPEC TrackedObjectData::updateLastAccessTime()
283{
284 RTTimeNow(&m_lastAccessTime);
285 return m_lastAccessTime;
286}
287
288RTTIMESPEC TrackedObjectData::initIdleTime()
289{
290 if (!m_fIdleTimeStart)
291 {
292 RTTimeNow(unconst(&m_deletionTime));
293 updateState(TrackedObjectState_Deleted);//Alive -> Deleted
294 RTTimeNow(unconst(&m_idleTimeStart));
295 m_fIdleTimeStart = true;
296 m_fLifeTimeExpired = true;
297 }
298
299 return m_idleTimeStart;
300}
301
302com::Utf8Str TrackedObjectData::creationTimeStr() const
303{
304 char szCreationTime[RTTIME_STR_LEN];
305 RTTimeSpecToString(&m_creationTime, szCreationTime, sizeof(szCreationTime));
306
307 return com::Utf8Str(szCreationTime);
308}
309
310TrackedObjectState_T TrackedObjectData::deletionTime(PRTTIMESPEC aTime) const
311{
312 if (m_state != TrackedObjectState_Alive)
313 *aTime = m_deletionTime;
314 return m_state;
315}
316
317/* No locking here, be aware */
318unsigned long TrackedObjectData::i_checkRefCount(const Guid& aIID)
319{
320 ULONG cRefs = 0;
321 if (aIID == m_classIID)
322 {
323 m_pIface->AddRef();
324 cRefs = m_pIface->Release();
325
326 Log2(("**** Object %s (class IID %s) (refcount %lu) ****\n",
327 m_objId.toString().c_str(),
328 m_classIID.toString().c_str(),
329 cRefs
330 ));
331 }
332 return cRefs;
333}
334
335TrackedObjectState_T TrackedObjectData::updateState(TrackedObjectState_T aNewState)
336{
337 return m_state < aNewState ? m_state = aNewState : m_state;
338}
339
340/////////////////////////////////////////////////////////////////////////////
341// TrackedObjectsCollector
342/////////////////////////////////////////////////////////////////////////////
343class TrackedObjectsCollector;
344extern TrackedObjectsCollector gTrackedObjectsCollector;
345
346TrackedObjectsCollector::TrackedObjectsCollector(): m_fInitialized(false)
347{
348}
349
350TrackedObjectsCollector::~TrackedObjectsCollector()
351{
352 Log2(("%s destructor \n", __FUNCTION__));
353
354 int vrc = i_checkInitialization();
355
356 /*
357 * Someone forgot to call uninit()
358 */
359 if (RT_SUCCESS(vrc))
360 {
361 if (m_trackedObjectsData.size() != 0)
362 {
363 if (m_trackedObjectsData.size() > 1)
364 LogRel(("%u objects are still presented in the collector\n", m_trackedObjectsData.size()));
365 else
366 LogRel(("%u object is still presented in the collector\n", m_trackedObjectsData.size()));
367
368 m_Released += m_trackedObjectsData.size();
369 m_trackedObjectsData.clear();
370 }
371
372 LogRel(("The Objects Collector history data: added objects %u, released objects %u\n", m_Added, m_Released));
373
374 /* Try to delete m_CritSectData */
375 RTCritSectDelete(&m_CritSectData);
376 }
377}
378
379bool TrackedObjectsCollector::init(){
380 m_fInitialized = false;
381
382 /* Create the critical section protecting the m_trackedObjectsData */
383 int vrc = RTCritSectInit(&m_CritSectData);
384
385 if (RT_SUCCESS(vrc))
386 {
387 /*
388 * TrackedObjectsCollector initialization occurs only when new instance of VirtualBox is created.
389 * At this moment nobody uses the TrackedObjectsCollector and we can call the functions without any locking.
390 */
391 vrc = i_clear();//just in case
392 if (RT_SUCCESS(vrc))
393 {
394 m_fInitialized = true;
395 LogRel(("The collector has been initialized.\n"));
396 }
397 }
398
399 return m_fInitialized;
400}
401
402bool TrackedObjectsCollector::uninit()
403{
404 clear(Uninitialization);
405
406
407 /* Deletion the critical section protecting the m_trackedObjectsData */
408 int vrc = RTCritSectDelete(&m_CritSectData);
409
410 if (RT_SUCCESS(vrc))
411 m_fInitialized = false;
412
413 return m_fInitialized;
414}
415
416int TrackedObjectsCollector::i_checkInitialization() const
417{
418 int vrc = VINF_SUCCESS;
419 if (!m_fInitialized)
420 {
421 Log2(("The collector is in the uninitialized state...\n"));
422 vrc = VERR_INVALID_STATE;
423 }
424
425 return vrc;
426}
427
428HRESULT TrackedObjectsCollector::setObj (const com::Utf8Str &aObjId,
429 const com::Utf8Str &aClassIID,
430 uint64_t lifeTime,
431 uint64_t idleTime,
432 IUnknown* ptrIface)
433{
434 LogFlowFuncEnter();
435
436 HRESULT hrc = S_OK;
437 int vrc = i_checkInitialization();
438 if (RT_FAILURE(vrc))
439 return VBOX_E_INVALID_OBJECT_STATE;
440
441 /* Enter critical section here */
442 RTCritSectEnter(&m_CritSectData);
443
444 com::Guid idObj(aObjId);
445 com::Guid classIID(aClassIID);
446 std::pair < std::set<com::Utf8Str>::iterator, bool > opRes = m_trackedObjectIds.insert(aObjId);
447
448 /*
449 * The case for updating the tracked object data.
450 * The Id is presented in the m_trackedObjectIds. The original object is removed from m_trackedObjectsData.
451 */
452 if (!opRes.second)
453 {
454 Log2(("UPDATING TrackedObjectData: object Id %s, class IID %s, life time %i, idle time %i\n",
455 aObjId.c_str(), aClassIID.c_str(), lifeTime, idleTime));
456
457 m_trackedObjectsData.erase(aObjId.c_str());
458 /* decrease the counter */
459 --m_Added;
460 }
461 else if (LogIs2Enabled())
462 {
463 char szCreationTime[RTTIME_STR_LEN];
464 RTTIMESPEC time;
465 RTTimeSpecToString(RTTimeNow(&time), szCreationTime, sizeof(szCreationTime));
466 Log2(("ADDED TrackedObjectData: creation time %s, object Id %s, class IID %s\n",
467 szCreationTime, aObjId.c_str(), aClassIID.c_str()));
468 }
469
470 /* Data is stored in the m_trackedObjectsData under the passed Id. */
471 m_trackedObjectsData.insert(std::make_pair(aObjId.c_str(),
472 TrackedObjectData(idObj,
473 classIID,
474 lifeTime,
475 idleTime,
476 ptrIface)));
477
478 /* increase the counter */
479 ++m_Added;
480
481 /* Leave critical section here */
482 RTCritSectLeave(&m_CritSectData);
483
484 return hrc;
485}
486
487
488HRESULT TrackedObjectsCollector::updateObj (const TrackedObjectData& aObjData)
489{
490 LogFlowFuncEnter();
491
492 HRESULT hrc = S_OK;
493 int vrc = i_checkInitialization();
494 if (RT_FAILURE(vrc))
495 return VBOX_E_INVALID_OBJECT_STATE;
496
497 /* Enter critical section here */
498 RTCritSectEnter(&m_CritSectData);
499
500 std::pair < std::set<com::Utf8Str>::iterator, bool > opRes = m_trackedObjectIds.insert(aObjData.objectIdStr());
501
502 /*
503 * The case for updating the tracked object data.
504 * The Id is presented in the m_trackedObjectIds. The original object is removed from m_trackedObjectsData.
505 */
506 if (!opRes.second)
507 {
508 Utf8Str strState = trackedObjectStateToStr(aObjData.state());
509 Log2(("UPDATING TrackedObjectData:\n state %s\n object Id %s\n class IID %s\n life time %i\n idle time %i"
510 "\n life time expired - %s\n idle time started - %s\n",
511 strState.c_str(),
512 aObjData.objectIdStr().c_str(),
513 aObjData.classIIDStr().c_str(),
514 aObjData.lifeTime(),
515 aObjData.idleTime(),
516 (aObjData.isLifeTimeExpired() == true ? "True" : "False"),
517 (aObjData.isIdleTimeStarted() == true ? "True" : "False")));
518
519 m_trackedObjectsData.erase(aObjData.objectIdStr().c_str());
520 /* decrease the counter */
521 --m_Added;
522
523 /* Data is stored in the m_trackedObjectsData under the passed Id. */
524 m_trackedObjectsData.insert(std::make_pair(aObjData.objectIdStr(), aObjData));
525
526 /* increase the counter */
527 ++m_Added;
528 }
529 else
530 {
531 Log2(("UPDATING failed because the object Id %s hasn't existed.\n", aObjData.objectIdStr().c_str()));
532 m_trackedObjectIds.erase(aObjData.objectIdStr());
533 }
534
535 /* Leave critical section here */
536 RTCritSectLeave(&m_CritSectData);
537
538 return hrc;
539}
540
541HRESULT TrackedObjectsCollector::getObj(const com::Utf8Str &aObjId,
542 TrackedObjectData &aObjData,
543 bool fUpdate)
544{
545 LogFlowFuncEnter();
546
547 int vrc = i_checkInitialization();
548 if (RT_FAILURE(vrc))
549 return VBOX_E_INVALID_OBJECT_STATE;
550
551 /* Enter critical section here */
552 RTCritSectEnter(&m_CritSectData);
553
554 HRESULT hrc = E_FAIL;
555
556 IterTrObjData_T pIter = m_trackedObjectsData.find(aObjId);
557 if (pIter != m_trackedObjectsData.end())
558 {
559 if (fUpdate == true)
560 {
561 /* Update some fields in the found object if needed. in instance, the last access time */
562 RTTIMESPEC lat = pIter->second.updateLastAccessTime(); /* Update the access time */
563 char szTime[RTTIME_STR_LEN];
564 RTTimeSpecToString(&lat, szTime, sizeof(szTime));
565 Log2(("The updated last access time is %s\n", szTime));
566 }
567 hrc = S_OK;
568 }
569 else
570 hrc = VBOX_E_OBJECT_NOT_FOUND;
571
572 if (SUCCEEDED(hrc))
573 aObjData = i_getObj(aObjId);
574
575 /* Leave critical section here */
576 RTCritSectLeave(&m_CritSectData);
577
578 return hrc;
579}
580
581const TrackedObjectData &TrackedObjectsCollector::i_getObj(const com::Utf8Str &aObjId) const
582{
583 /* No check for existence of aObjId */
584#if 0 /* the solaris VM's stl_map.h code doesn't have any at() function. */
585 return m_trackedObjectsData.at(aObjId);
586#else
587 ConstIterTrObjData_T const Iter = m_trackedObjectsData.find(aObjId);
588 if (Iter == m_trackedObjectsData.end())
589 throw std::out_of_range(aObjId.c_str());
590 return (*Iter).second;
591#endif
592}
593
594HRESULT TrackedObjectsCollector::initObjIdleTime(const com::Utf8Str &aObjId)
595{
596 LogFlowFuncEnter();
597
598 HRESULT hrc = S_OK;
599 int vrc = i_checkInitialization();
600 if (RT_FAILURE(vrc))
601 return VBOX_E_INVALID_OBJECT_STATE;
602
603 vrc = VERR_NOT_FOUND;
604 /* Enter critical section here */
605 RTCritSectEnter(&m_CritSectData);
606
607 IterTrObjData_T pIter = m_trackedObjectsData.find(aObjId);
608 if (pIter != m_trackedObjectsData.end())
609 {
610 /* Init idle time only once, next time returns the initialization time */
611 RTTIMESPEC idleTime = pIter->second.initIdleTime();
612 char szTime[RTTIME_STR_LEN];
613 RTTimeSpecToString(&idleTime, szTime, sizeof(szTime));
614 Log2(("The idle time start is %s\n", szTime));
615 vrc = VINF_SUCCESS;
616 }
617
618 if (RT_FAILURE(vrc))
619 hrc = VBOX_E_OBJECT_NOT_FOUND;
620
621 /* Leave critical section here */
622 RTCritSectLeave(&m_CritSectData);
623
624 return hrc;
625}
626
627HRESULT TrackedObjectsCollector::removeObj(const com::Utf8Str &aObjId)
628{
629 Log2(("%s: object Id %s\n", __FUNCTION__, aObjId.c_str()));
630
631 HRESULT hrc = S_OK;
632 int vrc = i_checkInitialization();
633 if (RT_FAILURE(vrc))
634 return VBOX_E_INVALID_OBJECT_STATE;
635
636 vrc = VERR_NOT_FOUND;
637
638 /* Enter critical section here */
639 RTCritSectEnter(&m_CritSectData);
640
641 IterTrObjData_T Iter = m_trackedObjectsData.find(aObjId);
642 if (Iter != m_trackedObjectsData.end())
643 {
644 Log2(("RELEASED TrackedObjectData: creation time %s, object Id %s, class IID %s\n",
645 Iter->second.creationTimeStr().c_str(), Iter->second.objectIdStr().c_str(), Iter->second.classIIDStr().c_str()));
646
647 m_trackedObjectsData.erase(Iter);
648 m_trackedObjectIds.erase(aObjId);
649 m_trackedInvalidObjectIds.erase(aObjId);
650
651 /* increase the counter */
652 ++m_Released;
653
654 vrc = VINF_SUCCESS;
655 }
656
657 if (RT_FAILURE(vrc))
658 hrc = VBOX_E_OBJECT_NOT_FOUND;
659
660 /* Leave critical section here */
661 RTCritSectLeave(&m_CritSectData);
662
663 return hrc;
664}
665
666HRESULT TrackedObjectsCollector::getAllObjIds (std::vector<com::Utf8Str>& aObjIdMap)
667{
668 HRESULT hrc = S_OK;
669 int vrc = i_checkInitialization();
670 if (RT_FAILURE(vrc))
671 return VBOX_E_INVALID_OBJECT_STATE;
672
673 /* Enter critical section here */
674 RTCritSectEnter(&m_CritSectData);
675 vrc = i_getAllObjIds(aObjIdMap);
676 if (RT_FAILURE(vrc))
677 hrc = VBOX_E_OBJECT_NOT_FOUND;
678 /* Leave critical section here */
679 RTCritSectLeave(&m_CritSectData);
680
681 return hrc;
682}
683
684int TrackedObjectsCollector::i_getAllObjIds(std::vector<com::Utf8Str> &aObjIdMap) const
685{
686 //for (const com::Utf8Str &item : m_trackedObjectIds) - the gcc in the solaris VM doesn't grok this.
687 for (std::set<com::Utf8Str>::const_iterator Iter = m_trackedObjectIds.begin();
688 Iter != m_trackedObjectIds.end();
689 ++Iter)
690 aObjIdMap.push_back(*Iter);
691
692 return aObjIdMap.size() > 0 ? VINF_SUCCESS : VERR_NOT_FOUND;
693}
694
695HRESULT TrackedObjectsCollector::getObjIdsByClassIID (const Guid& iid,
696 std::vector<com::Utf8Str>& aObjIdMap)
697{
698 Log2(("%s: Getting all objects Ids with Class IID %s\n", __FUNCTION__, iid.toString().c_str()));
699
700 HRESULT hrc = S_OK;
701 int vrc = i_checkInitialization();
702 if (RT_FAILURE(vrc))
703 return VBOX_E_INVALID_OBJECT_STATE;
704
705 /* Enter critical section here */
706 RTCritSectEnter(&m_CritSectData);
707
708 vrc = i_getObjIdsByClassIID (iid, aObjIdMap);
709 if (RT_FAILURE(vrc))
710 hrc = VBOX_E_OBJECT_NOT_FOUND;
711 /* Leave critical section here */
712 RTCritSectLeave(&m_CritSectData);
713
714 return hrc;
715}
716
717int TrackedObjectsCollector::i_getObjIdsByClassIID(const Guid &aIId, std::vector<com::Utf8Str> &aObjIdMap) const
718{
719 //for (const std::pair<const com::Utf8Str, TrackedObjectData> &item : m_trackedObjectsData) - the gcc in the solaris VM doesn't grok this.
720 for (ConstIterTrObjData_T Iter = m_trackedObjectsData.begin();
721 Iter != m_trackedObjectsData.end();
722 ++Iter)
723 {
724 if (Iter->second.classIID() == aIId)
725 aObjIdMap.push_back(Iter->first);
726 }
727
728 return aObjIdMap.size() > 0 ? VINF_SUCCESS : VERR_NOT_FOUND;
729}
730
731bool TrackedObjectsCollector::checkObj(const com::Utf8Str &aObjId)
732{
733 Log2(("%s: Checking object with Id %s\n", __FUNCTION__, aObjId.c_str()));
734
735 int vrc = i_checkInitialization();
736 if (RT_FAILURE(vrc))
737 return false;
738
739 RTCritSectEnter(&m_CritSectData);
740 bool res = i_checkObj(aObjId);
741 RTCritSectLeave(&m_CritSectData);
742 return res;
743}
744
745bool TrackedObjectsCollector::i_checkObj(const com::Utf8Str& aObjId) const
746{
747 return m_trackedObjectIds.count(aObjId.c_str()) > 0 ? true : false;
748}
749
750HRESULT TrackedObjectsCollector::clear(TrackedObjectsCollectorState aState)
751{
752 LogFlowFuncEnter();
753
754 HRESULT hrc = S_OK;
755 int vrc = i_checkInitialization();
756 if (RT_FAILURE(vrc))
757 return VBOX_E_INVALID_OBJECT_STATE;
758
759 if (aState != Uninitialization)
760 {
761 /* Enter critical section here */
762 vrc = RTCritSectEnter(&m_CritSectData);
763 if (RT_FAILURE(vrc))
764 {
765 Log2(("%s: Coudn't enter into the critical section (%Rrc)\n", __FUNCTION__, vrc));
766 return E_ABORT;
767 }
768 }
769
770 if (m_trackedObjectsData.size() != 0)
771 {
772 if (m_trackedObjectsData.size() > 1)
773 Log2(("%u objects are still presented in the Objects Collector, clearing...\n", m_trackedObjectsData.size()));
774 else
775 Log2(("%u object is still presented in the Objects Collector, clearing...\n", m_trackedObjectsData.size()));
776
777 vrc = i_clear();
778 /* Ignore setting hrc */
779 if (RT_FAILURE(vrc))
780 LogRel(("Something wrong with clearing the Objects Collector\n"));
781 }
782
783 Log2(("The Objects Collector history data: added objects %u, released objects %u\n", m_Added, m_Released));
784
785 if (aState != Uninitialization)
786 {
787 /* Leave critical section here */
788 RTCritSectLeave(&m_CritSectData);
789 }
790
791 return hrc;
792}
793
794int TrackedObjectsCollector::i_clear()
795{
796 int vrc = VINF_SUCCESS;
797 try
798 {
799 m_Released += m_trackedObjectsData.size();
800 m_trackedObjectsData.clear();
801 m_trackedObjectIds.clear();
802 m_trackedInvalidObjectIds.clear();
803 }
804 catch (...)
805 {
806 vrc = VERR_GENERAL_FAILURE;
807 }
808 return vrc;
809}
810
811HRESULT TrackedObjectsCollector::tryToRemoveObj(const com::Utf8Str& aObjId)
812{
813 LogFlowFuncEnter();
814
815 HRESULT hrc = S_OK;
816 int vrc = i_checkInitialization();
817 if (RT_FAILURE(vrc))
818 return VBOX_E_INVALID_OBJECT_STATE;
819
820 /* Enter critical section here */
821 RTCritSectEnter(&m_CritSectData);
822
823 IterTrObjData_T pIter = m_trackedObjectsData.find(aObjId.c_str());
824 if (pIter != m_trackedObjectsData.end())
825 {
826 pIter->second.getInterface()->AddRef();
827 ULONG cRefs = pIter->second.getInterface()->Release();
828
829 if (cRefs > 1)
830 {
831 Log2(("Object %s with class IID %s can't be released if refcount is more than 1 (now %lu)\n",
832 pIter->second.objectIdStr().c_str(),
833 pIter->second.classIIDStr().c_str(),
834 cRefs
835 ));
836 hrc = E_FAIL;
837 }
838 else
839 {
840 Log2(("Object %s with class IID %s is released (refcount %lu)\n",
841 pIter->second.objectIdStr().c_str(),
842 pIter->second.classIIDStr().c_str(),
843 cRefs
844 ));
845 m_trackedObjectsData.erase(pIter);
846 m_trackedObjectIds.erase(aObjId);
847 m_trackedInvalidObjectIds.erase(aObjId);
848
849 /* increase the counter */
850 ++m_Released;
851 }
852 }
853 else
854 hrc = VBOX_E_OBJECT_NOT_FOUND;
855
856 /* Leave critical section here */
857 RTCritSectLeave(&m_CritSectData);
858
859 return hrc;
860}
861
862/**
863 * Invalidate the tracked object.
864 * Works ONLY in conjunction with setTracked()!
865 */
866HRESULT TrackedObjectsCollector::invalidateObj(const com::Utf8Str &aObjId)
867{
868 LogFlowFuncEnter();
869
870 int vrc = i_checkInitialization();
871 if (RT_FAILURE(vrc))
872 return VBOX_E_INVALID_OBJECT_STATE;
873
874 /* Enter critical section here */
875 RTCritSectEnter(&m_CritSectData);
876
877 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
878 IterTrObjData_T pIter = m_trackedObjectsData.find(aObjId);
879 if (pIter != m_trackedObjectsData.end())
880 {
881 pIter->second.updateState(TrackedObjectState_Invalid);//Deleted -> Invalid
882 m_trackedInvalidObjectIds.insert(aObjId);
883 hrc = S_OK;
884 }
885
886 /* Leave critical section here */
887 RTCritSectLeave(&m_CritSectData);
888
889 return hrc;
890}
891
892/////////////////////////////////////////////////////////////////////////////
893// ObjectTracker
894/////////////////////////////////////////////////////////////////////////////
895ObjectTracker::~ObjectTracker()
896{
897 LogFlowFuncEnter();
898 LogRel(("Start waiting the ObjectTracker thread termination\n"));
899 RTThreadWait(m_Thread, 30000, NULL);
900 LogRel(("Finished waiting the ObjectTracker thread termination\n"));
901 m_Thread = NIL_RTTHREAD;
902}
903
904bool ObjectTracker::init()
905{
906 LogFlowFuncEnter();
907 return true;
908}
909
910bool ObjectTracker::finish()
911{
912 LogFlowFuncEnter();
913 ASMAtomicWriteBool(&fFinish, true);
914 return true;
915}
916
917bool ObjectTracker::isFinished()
918{
919 LogFlowFuncEnter();
920 return ASMAtomicReadBool(&fFinish);
921}
922
923/*static*/
924DECLCALLBACK(int) ObjectTracker::objectTrackerTask(RTTHREAD ThreadSelf, void *pvUser)
925{
926 HRESULT hrc = S_OK;
927
928 NOREF(ThreadSelf);
929 ObjectTracker* const master = (ObjectTracker*)pvUser;
930
931 LogRel(("Starting the ObjectTracker thread %s\n", master->getTaskName().c_str()));
932
933 while (master != NULL && master->isFinished() != true)
934 {
935 std::vector<com::Utf8Str> lObjIdMap;
936 hrc = gTrackedObjectsCollector.getAllObjIds(lObjIdMap);
937
938 //for (const com::Utf8Str& item : lObjIdMap) - the gcc in the solaris VM doesn't grok this.
939 for (std::vector<com::Utf8Str>::const_iterator Iter = lObjIdMap.begin(); Iter != lObjIdMap.end(); ++Iter)
940 {
941 TrackedObjectData temp;
942 if(gTrackedObjectsCollector.checkObj(*Iter))
943 {
944 hrc = gTrackedObjectsCollector.getObj(*Iter, temp);
945 if (SUCCEEDED(hrc))
946 {
947 Log2(("Tracked Object with ID %s was found:\n", temp.m_objId.toString().c_str()));
948
949 RTTIMESPEC now;
950 int64_t currTime = RTTimeSpecGetMilli(RTTimeNow(&now));
951 int64_t creationTime = RTTimeSpecGetMilli(&temp.m_creationTime);
952 int64_t lifeTime = (int64_t)temp.m_lifeTime*1000; //convert to milliseconds
953
954 int64_t remainingLifeTime = lifeTime == 0 ? 0 : ((creationTime + lifeTime) - currTime)/1000;
955
956 bool fLifeTimeExpired = temp.m_fLifeTimeExpired;
957 if (!fLifeTimeExpired)
958 fLifeTimeExpired = (remainingLifeTime == 0 && lifeTime != 0) ? true : false;
959
960 /* lock? */
961 temp.m_pIface->AddRef();
962 unsigned long cRefs = temp.m_pIface->Release();
963
964 /* cRefs > 2 because we created the temporarily object temp */
965 Log2(("Object %s (class IID %s): refcount %lu, remaining life time is %ld sec\n",
966 temp.m_objId.toString().c_str(),
967 temp.m_classIID.toString().c_str(),
968 cRefs - 1,
969 remainingLifeTime
970 ));
971
972 if (fLifeTimeExpired)
973 {
974 {
975 if (temp.m_fIdleTimeStart == false)
976 {
977 gTrackedObjectsCollector.initObjIdleTime(*Iter);
978 Log2(("Idle time for the object with Id %s has been started\n", Iter->c_str()));
979 Log2((" class IID %s, refcount %lu\n", temp.m_classIID.toString().c_str(), cRefs - 1));
980 }
981 else
982 {
983 int64_t idleTime = (int64_t)temp.m_idleTime*1000; //convert to milliseconds
984 int64_t idleTimeStart = RTTimeSpecGetMilli(&temp.m_idleTimeStart);
985 bool fIdleTimeExpired = (currTime - idleTimeStart) > idleTime ? true : false;
986 if (fIdleTimeExpired)
987 {
988 Log2(("Object with Id %s removed from Object Collector "
989 "(recount is %u, idle time exceeded %u sec)\n", Iter->c_str(), cRefs - 1, temp.m_idleTime));
990 gTrackedObjectsCollector.removeObj(*Iter);
991 }
992 }
993 }
994 }
995 else
996 {
997 if (cRefs <= 2)
998 {
999 /*
1000 * Special case for objects with the original lifeTime equal to 0 (0 means endless).
1001 * For these objects the idle time starts when user deletes it.
1002 */
1003 if (lifeTime == 0)
1004 {
1005 temp.m_fLifeTimeExpired = true;
1006
1007 /* Updating the object data */
1008 gTrackedObjectsCollector.updateObj(temp);
1009 }
1010 }
1011 }
1012 }
1013 }
1014 else
1015 Log2(("Tracked Object with ID %s was not found\n", Iter->c_str()));
1016 }
1017
1018 //sleep 1 sec
1019 RTThreadSleep(RT_MS_1SEC);//1 sec
1020 }
1021
1022 LogRel(("Finishing the object tracker thread %s\n", master->getTaskName().c_str()));
1023
1024 return 0;
1025}
1026
1027int ObjectTracker::createThread()
1028{
1029 int vrc = RTThreadCreate(&m_Thread, objectTrackerTask, this, 0,
1030 RTTHREADTYPE_INFREQUENT_POLLER,
1031 RTTHREADFLAGS_WAITABLE,
1032 "ObjTracker");
1033
1034 return vrc;
1035}
1036
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