Chris@49: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@0: 
Chris@0: /*
Chris@52:     Sonic Visualiser
Chris@52:     An audio file viewer and annotation editor.
Chris@52:     Centre for Digital Music, Queen Mary, University of London.
Chris@52:     This file copyright 2006 Chris Cannam.
Chris@0:     
Chris@52:     This program is free software; you can redistribute it and/or
Chris@52:     modify it under the terms of the GNU General Public License as
Chris@52:     published by the Free Software Foundation; either version 2 of the
Chris@52:     License, or (at your option) any later version.  See the file
Chris@52:     COPYING included with this distribution for more information.
Chris@0: */
Chris@0: 
Chris@0: /*
Chris@0:    This is a modified version of a source file from the 
Chris@0:    Rosegarden MIDI and audio sequencer and notation editor.
Chris@17:    This file copyright 2000-2006 Chris Cannam.
Chris@0: */
Chris@0: 
Chris@0: #ifndef _SCAVENGER_H_
Chris@0: #define _SCAVENGER_H_
Chris@0: 
Chris@0: #include "System.h"
Chris@0: 
Chris@0: #include <vector>
Chris@0: #include <sys/time.h>
Chris@0: 
Chris@0: /**
Chris@0:  * A very simple class that facilitates running things like plugins
Chris@0:  * without locking, by collecting unwanted objects and deleting them
Chris@0:  * after a delay so as to be sure nobody's in the middle of using
Chris@0:  * them.  Requires scavenge() to be called regularly from a non-RT
Chris@0:  * thread.
Chris@0:  *
Chris@0:  * This is currently not at all suitable for large numbers of objects
Chris@0:  * -- it's just a quick hack for use with things like plugins.
Chris@0:  */
Chris@0: 
Chris@0: template <typename T>
Chris@0: class Scavenger
Chris@0: {
Chris@0: public:
Chris@0:     Scavenger(int sec = 2, int defaultObjectListSize = 200);
Chris@0: 
Chris@0:     /**
Chris@0:      * Call from an RT thread etc., to pass ownership of t to us.
Chris@0:      * Only one thread should be calling this on any given scavenger.
Chris@0:      */
Chris@0:     void claim(T *t);
Chris@0: 
Chris@0:     /**
Chris@0:      * Call from a non-RT thread.
Chris@0:      * Only one thread should be calling this on any given scavenger.
Chris@0:      */
Chris@14:     void scavenge(bool clearNow = false);
Chris@0: 
Chris@0: protected:
Chris@0:     typedef std::pair<T *, int> ObjectTimePair;
Chris@0:     typedef std::vector<ObjectTimePair> ObjectTimeList;
Chris@0:     ObjectTimeList m_objects;
Chris@0:     int m_sec;
Chris@0: 
Chris@0:     unsigned int m_claimed;
Chris@0:     unsigned int m_scavenged;
Chris@0: };
Chris@0: 
Chris@0: /**
Chris@0:  * A wrapper to permit arrays to be scavenged.
Chris@0:  */
Chris@0: 
Chris@0: template <typename T>
Chris@0: class ScavengerArrayWrapper
Chris@0: {
Chris@0: public:
Chris@0:     ScavengerArrayWrapper(T *array) : m_array(array) { }
Chris@0:     ~ScavengerArrayWrapper() { delete[] m_array; }
Chris@0: 
Chris@0: private:
Chris@0:     T *m_array;
Chris@0: };
Chris@0: 
Chris@0: 
Chris@0: template <typename T>
Chris@0: Scavenger<T>::Scavenger(int sec, int defaultObjectListSize) :
Chris@0:     m_objects(ObjectTimeList(defaultObjectListSize)),
Chris@0:     m_sec(sec),
Chris@0:     m_claimed(0),
Chris@0:     m_scavenged(0)
Chris@0: {
Chris@0: }
Chris@0: 
Chris@0: template <typename T>
Chris@0: void
Chris@0: Scavenger<T>::claim(T *t)
Chris@0: {
Chris@0: //    std::cerr << "Scavenger::claim(" << t << ")" << std::endl;
Chris@0: 
Chris@0:     struct timeval tv;
Chris@0:     (void)gettimeofday(&tv, 0);
Chris@0:     int sec = tv.tv_sec;
Chris@0: 
Chris@0:     for (size_t i = 0; i < m_objects.size(); ++i) {
Chris@0: 	ObjectTimePair &pair = m_objects[i];
Chris@0: 	if (pair.first == 0) {
Chris@0: 	    pair.second = sec;
Chris@0: 	    pair.first = t;
Chris@0: 	    ++m_claimed;
Chris@0: 	    return;
Chris@0: 	}
Chris@0:     }
Chris@0: 
Chris@0:     // Oh no -- run out of slots!  Warn and discard something at
Chris@0:     // random (without deleting it -- it's probably safer to leak).
Chris@0: 
Chris@0:     for (size_t i = 0; i < m_objects.size(); ++i) {
Chris@0: 	ObjectTimePair &pair = m_objects[i];
Chris@0: 	if (pair.first != 0) {
Chris@0: 	    pair.second = sec;
Chris@0: 	    pair.first = t;
Chris@0: 	    ++m_claimed;
Chris@0: 	    ++m_scavenged;
Chris@0: 	}
Chris@0:     }
Chris@0: }
Chris@0: 
Chris@0: template <typename T>
Chris@0: void
Chris@14: Scavenger<T>::scavenge(bool clearNow)
Chris@0: {
Chris@0: //    std::cerr << "Scavenger::scavenge: scavenged " << m_scavenged << ", claimed " << m_claimed << std::endl;
Chris@0: 
Chris@0:     if (m_scavenged >= m_claimed) return;
Chris@0:     
Chris@0:     struct timeval tv;
Chris@0:     (void)gettimeofday(&tv, 0);
Chris@0:     int sec = tv.tv_sec;
Chris@0: 
Chris@0:     for (size_t i = 0; i < m_objects.size(); ++i) {
Chris@0: 	ObjectTimePair &pair = m_objects[i];
Chris@14: 	if (clearNow ||
Chris@14: 	    (pair.first != 0 && pair.second + m_sec < sec)) {
Chris@0: 	    T *ot = pair.first;
Chris@0: 	    pair.first = 0;
Chris@0: 	    delete ot;
Chris@0: 	    ++m_scavenged;
Chris@0: 	}
Chris@0:     }
Chris@0: }
Chris@0: 
Chris@0: #endif