VirtualBox

Changeset 36839 in vbox


Ignore:
Timestamp:
Apr 25, 2011 5:29:21 PM (14 years ago)
Author:
vboxsync
Message:

Main/Metrics: Locking revised to prevent lockups on VM shutdown (#5637)

Location:
trunk/src/VBox/Main
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/include/Performance.h

    r36527 r36839  
    145145        ~CollectorGuest();
    146146
     147        bool isUnregistered()   { return mUnregistered; };
    147148        bool isEnabled()        { return mEnabled; };
    148149        bool isValid()          { return mValid; };
    149150        void invalidateStats()  { mValid = false; };
     151        void unregister()       { mUnregistered = true; };
    150152        int updateStats();
    151153        int enable();
     
    168170
    169171    private:
     172        bool                 mUnregistered;
    170173        bool                 mEnabled;
    171174        bool                 mValid;
     
    194197    public:
    195198        CollectorGuestManager() : mVMMStatsProvider(NULL) {};
     199        ~CollectorGuestManager() { Assert(mGuests.size() == 0); };
    196200        void registerGuest(CollectorGuest* pGuest);
    197201        void unregisterGuest(CollectorGuest* pGuest);
    198202        CollectorGuest *getVMMStatsProvider() { return mVMMStatsProvider; };
    199203        void preCollect(CollectorHints& hints, uint64_t iTick);
     204        void destroyUnregistered();
    200205    private:
    201206        CollectorGuestList mGuests;
     
    234239    public:
    235240        BaseMetric(CollectorHAL *hal, const char *name, ComPtr<IUnknown> object)
    236             : mPeriod(0), mLength(0), mHAL(hal), mName(name), mObject(object), mLastSampleTaken(0), mEnabled(false) {};
     241            : mPeriod(0), mLength(0), mHAL(hal), mName(name), mObject(object),
     242              mLastSampleTaken(0), mEnabled(false), mUnregistered(false) {};
    237243        virtual ~BaseMetric() {};
    238244
     
    249255        void enable()  { mEnabled = true; };
    250256        void disable() { mEnabled = false; };
    251 
     257        void unregister() { mUnregistered = true; };
     258
     259        bool isUnregistered() { return mUnregistered; };
    252260        bool isEnabled() { return mEnabled; };
    253261        ULONG getPeriod() { return mPeriod; };
     
    265273        uint64_t         mLastSampleTaken;
    266274        bool             mEnabled;
     275        bool             mUnregistered;
    267276    };
    268277
  • trunk/src/VBox/Main/src-server/MachineImpl.cpp

    r36727 r36839  
    1056210562    {
    1056310563        mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
    10564         delete mCollectorGuest;
     10564        // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
    1056510565        mCollectorGuest = NULL;
    1056610566    }
     10567#if 0
    1056710568    // Trigger async cleanup tasks, avoid doing things here which are not
    1056810569    // vital to be done immediately and maybe need more locks. This calls
    1056910570    // Machine::unregisterMetrics().
    1057010571    mParent->onMachineUninit(mPeer);
     10572#else
     10573    /*
     10574     * It is safe to call Machine::unregisterMetrics() here because
     10575     * PerformanceCollector::samplerCallback no longer accesses guest methods
     10576     * holding the lock.
     10577     */
     10578    unregisterMetrics(mParent->performanceCollector(), mPeer);
     10579#endif
    1057110580
    1057210581    if (aReason == Uninit::Abnormal)
  • trunk/src/VBox/Main/src-server/Performance.cpp

    r36527 r36839  
    110110
    111111CollectorGuest::CollectorGuest(Machine *machine, RTPROCESS process) :
    112     mEnabled(false), mValid(false), mMachine(machine), mProcess(process),
     112    mUnregistered(false), mEnabled(false), mValid(false), mMachine(machine), mProcess(process),
    113113    mCpuUser(0), mCpuKernel(0), mCpuIdle(0),
    114114    mMemTotal(0), mMemFree(0), mMemBalloon(0), mMemShared(0), mMemCache(0), mPageTotal(0),
     
    202202void CollectorGuestManager::preCollect(CollectorHints& hints, uint64_t /* iTick */)
    203203{
     204    /*
     205     * Since we are running without a lock the value of mVMMStatsProvider
     206     * can change at any moment. In the worst case we won't collect any data.
     207     */
    204208    CollectorGuestList::iterator it;
    205209
     
    211215                    this, __PRETTY_FUNCTION__, *it, (*it)->getProcess(),
    212216                    hints.isGuestStatsCollected((*it)->getProcess())?"y":"n"));
     217        if ((*it)->isUnregistered())
     218            continue;
    213219        if (  (hints.isHostRamVmmCollected() && *it == mVMMStatsProvider)
    214220            || hints.isGuestStatsCollected((*it)->getProcess()))
     
    252258    LogAleksey(("{%p} " LOG_FN_FMT ": About to unregister guest=%p provider=%p\n",
    253259                this, __PRETTY_FUNCTION__, pGuest, mVMMStatsProvider));
    254     mGuests.remove(pGuest);
    255     LogAleksey(("{%p} " LOG_FN_FMT ": Number of guests after remove is %d\n",
    256                 this, __PRETTY_FUNCTION__, mGuests.size()));
     260    //mGuests.remove(pGuest); => destroyUnregistered()
     261    pGuest->unregister();
    257262    if (pGuest == mVMMStatsProvider)
    258263    {
    259264        /* This was our VMM stats provider, it is time to re-elect */
    260         if (mGuests.empty())
    261         {
    262             /* Nobody can provide VMM stats */
    263             mVMMStatsProvider = NULL;
    264         }
    265         else
    266         {
    267             /* First let's look for a guest already collecting stats */
    268             CollectorGuestList::iterator it;
    269 
    270             for (it = mGuests.begin(); it != mGuests.end(); it++)
    271                 if ((*it)->isEnabled())
    272                 {
    273                     /* Found one, elect it */
    274                     mVMMStatsProvider = *it;
    275                     LogAleksey(("{%p} " LOG_FN_FMT ": LEAVE new provider=%p\n",
    276                                 this, __PRETTY_FUNCTION__, mVMMStatsProvider));
    277                     return;
    278                 }
    279 
    280             /* Nobody collects stats, take the first one */
    281             mVMMStatsProvider = mGuests.front();
     265        CollectorGuestList::iterator it;
     266        /* Assume that nobody can provide VMM stats */
     267        mVMMStatsProvider = NULL;
     268
     269        for (it = mGuests.begin(); it != mGuests.end(); it++)
     270        {
     271            /* Skip unregistered as they are about to be destroyed */
     272            if ((*it)->isUnregistered())
     273                continue;
     274
     275            if ((*it)->isEnabled())
     276            {
     277                /* Found the guest already collecting stats, elect it */
     278                mVMMStatsProvider = *it;
     279                break;
     280            }
     281            else if (!mVMMStatsProvider)
     282            {
     283                /* If nobody collects stats, take the first registered */
     284                mVMMStatsProvider = *it;
     285            }
    282286        }
    283287    }
     
    286290}
    287291
     292void CollectorGuestManager::destroyUnregistered()
     293{
     294    CollectorGuestList::iterator it;
     295
     296    for (it = mGuests.begin(); it != mGuests.end();)
     297        if ((*it)->isUnregistered())
     298        {
     299            delete *it;
     300            it = mGuests.erase(it);
     301            LogAleksey(("{%p} " LOG_FN_FMT ": Number of guests after erasing unregistered is %d\n",
     302                        this, __PRETTY_FUNCTION__, mGuests.size()));
     303        }
     304        else
     305            ++it;
     306}
    288307
    289308#endif /* !VBOX_COLLECTOR_TEST_CASE */
  • trunk/src/VBox/Main/src-server/PerformanceImpl.cpp

    r36128 r36839  
    1616 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
    1717 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
     18 */
     19
     20/*
     21 * Rules of engagement:
     22 * 1) All performance objects must be destroyed by PerformanceCollector only!
     23 * 2) All public methods of PerformanceCollector must be protected with
     24 *    read or write lock.
     25 * 3) samplerCallback only uses the write lock during the third phase
     26 *    which pulls data into SubMetric objects. This is where object destruction
     27 *    and all list modifications are done. The pre-collection phases are
     28 *    run without any locks which is only possible because:
     29 * 4) Public methods of PerformanceCollector as well as pre-collection methods
     30      cannot modify lists or destroy objects, and:
     31 * 5) Pre-collection methods cannot modify metric data.
    1832 */
    1933
     
    200214    mMagic = 0;
    201215
     216    /* Destroy unregistered metrics */
     217    BaseMetricList::iterator it;
     218    for (it = m.baseMetrics.begin(); it != m.baseMetrics.end();)
     219        if ((*it)->isUnregistered())
     220        {
     221            delete *it;
     222            it = m.baseMetrics.erase(it);
     223        }
     224        else
     225            ++it;
     226    Assert(m.baseMetrics.size() == 0);
     227    /*
     228     * Now when we have destroyed all base metrics that could
     229     * try to pull data from unregistered CollectorGuest objects
     230     * it is safe to destroy them as well.
     231     */
     232    m.gm->destroyUnregistered();
     233
    202234    /* Destroy resource usage sampler */
    203235    int vrc = RTTimerLRDestroy (m.sampler);
     
    534566
    535567    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    536     LogAleksey(("{%p} " LOG_FN_FMT ": before remove_if: m.baseMetrics.size()=%d\n", this, __PRETTY_FUNCTION__, m.baseMetrics.size()));
     568    int n = 0;
    537569    BaseMetricList::iterator it;
    538     for (it = m.baseMetrics.begin(); it != m.baseMetrics.end();)
     570    for (it = m.baseMetrics.begin(); it != m.baseMetrics.end(); ++it)
    539571        if ((*it)->associatedWith(aObject))
    540572        {
    541             delete *it;
    542             it = m.baseMetrics.erase(it);
     573            (*it)->unregister();
     574            ++n;
    543575        }
    544         else
    545             ++it;
    546     LogAleksey(("{%p} " LOG_FN_FMT ": after remove_if: m.baseMetrics.size()=%d\n", this, __PRETTY_FUNCTION__, m.baseMetrics.size()));
     576    LogAleksey(("{%p} " LOG_FN_FMT ": obj=%p, marked %d metrics\n",
     577                this, __PRETTY_FUNCTION__, (void *)aObject, n));
    547578    //LogFlowThisFuncLeave();
    548579}
     
    640671{
    641672    Log4(("{%p} " LOG_FN_FMT ": ENTER\n", this, __PRETTY_FUNCTION__));
    642     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     673    /* No locking until stage 3!*/
    643674
    644675    pm::CollectorHints hints;
     
    665696    m.gm->preCollect(hints, iTick);
    666697
     698    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     699    /*
     700     * Before we can collect data we need to go through both lists
     701     * again to see if any base metrics are marked as unregistered.
     702     * Those should be destroyed now.
     703     */
     704    LogAleksey(("{%p} " LOG_FN_FMT ": before remove_if: toBeCollected.size()=%d\n", this, __PRETTY_FUNCTION__, toBeCollected.size()));
     705    toBeCollected.remove_if(std::mem_fun(&pm::BaseMetric::isUnregistered));
     706    LogAleksey(("{%p} " LOG_FN_FMT ": after remove_if: toBeCollected.size()=%d\n", this, __PRETTY_FUNCTION__, toBeCollected.size()));
     707    LogAleksey(("{%p} " LOG_FN_FMT ": before remove_if: m.baseMetrics.size()=%d\n", this, __PRETTY_FUNCTION__, m.baseMetrics.size()));
     708    for (it = m.baseMetrics.begin(); it != m.baseMetrics.end();)
     709        if ((*it)->isUnregistered())
     710        {
     711            delete *it;
     712            it = m.baseMetrics.erase(it);
     713        }
     714        else
     715            ++it;
     716    LogAleksey(("{%p} " LOG_FN_FMT ": after remove_if: m.baseMetrics.size()=%d\n", this, __PRETTY_FUNCTION__, m.baseMetrics.size()));
     717    /*
     718     * Now when we have destroyed all base metrics that could
     719     * try to pull data from unregistered CollectorGuest objects
     720     * it is safe to destroy them as well.
     721     */
     722    m.gm->destroyUnregistered();
     723
    667724    /* Finally, collect the data */
    668725    std::for_each (toBeCollected.begin(), toBeCollected.end(),
Note: See TracChangeset for help on using the changeset viewer.

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