VirtualBox

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

Last change on this file since 107867 was 107867, checked in by vboxsync, 4 months ago

bugref:10806. Revised the functions updateLastAccessTime() and initIdleTime().

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