Files
librubberband/src/common/Scavenger.h

251 lines
5.8 KiB
C
Raw Normal View History

2007-11-06 21:41:16 +00:00
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
2012-09-09 16:57:42 +01:00
Rubber Band Library
2007-11-06 21:41:16 +00:00
An audio time-stretching and pitch-shifting library.
2022-01-04 17:50:15 +00:00
Copyright 2007-2022 Particular Programs Ltd.
2012-09-09 16:57:42 +01:00
2007-11-06 21:41:16 +00:00
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
2012-09-09 16:57:42 +01:00
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
2007-11-06 21:41:16 +00:00
*/
2021-02-10 11:07:33 +00:00
#ifndef RUBBERBAND_SCAVENGER_H
#define RUBBERBAND_SCAVENGER_H
2007-11-06 21:41:16 +00:00
#include <vector>
#include <list>
#include <utility>
2007-11-06 21:41:16 +00:00
#include <iostream>
2021-03-05 10:31:00 +00:00
#ifndef _MSC_VER
#include <sys/time.h>
#endif
#include "Thread.h"
#include "sysutils.h"
#include "Allocators.h"
//#define DEBUG_SCAVENGER 1
2007-11-28 11:50:47 +00:00
2007-11-06 21:41:16 +00:00
namespace RubberBand {
/**
* A very simple class that facilitates running things like plugins
* without locking, by collecting unwanted objects and deleting them
* after a delay so as to be sure nobody's in the middle of using
* them. Requires scavenge() to be called regularly from a non-RT
* thread.
*
* This is currently not at all suitable for large numbers of objects
* -- it's just a quick hack for use with things like plugins.
*/
//!!! should review this, it's not really thread safe owing to lack of
//!!! atomic updates
2007-11-06 21:41:16 +00:00
template <typename T>
class Scavenger
{
public:
Scavenger(int sec = 2, int defaultObjectListSize = 200);
~Scavenger();
/**
* Call from an RT thread etc., to pass ownership of t to us.
* Only one thread should be calling this on any given scavenger.
*/
void claim(T *t);
/**
* Call from a non-RT thread.
* Only one thread should be calling this on any given scavenger.
*/
void scavenge(bool clearNow = false);
protected:
typedef std::pair<T *, int> ObjectTimePair;
typedef std::vector<ObjectTimePair> ObjectTimeList;
ObjectTimeList m_objects;
int m_sec;
typedef std::list<T *> ObjectList;
ObjectList m_excess;
int m_lastExcess;
2007-11-28 11:50:47 +00:00
Mutex m_excessMutex;
2007-11-06 21:41:16 +00:00
void pushExcess(T *);
void clearExcess(int);
unsigned int m_claimed;
unsigned int m_scavenged;
unsigned int m_asExcess;
2007-11-06 21:41:16 +00:00
};
2007-11-06 21:41:16 +00:00
/**
* A wrapper to permit arrays allocated with new[] to be scavenged.
2007-11-06 21:41:16 +00:00
*/
template <typename T>
class ScavengerArrayWrapper
{
public:
ScavengerArrayWrapper(T *array) : m_array(array) { }
~ScavengerArrayWrapper() { delete[] m_array; }
private:
T *m_array;
};
/**
* A wrapper to permit arrays allocated with the Allocators functions
* to be scavenged.
*/
template <typename T>
class ScavengerAllocArrayWrapper
{
public:
ScavengerAllocArrayWrapper(T *array) : m_array(array) { }
~ScavengerAllocArrayWrapper() { deallocate<T>(m_array); }
private:
T *m_array;
};
2007-11-06 21:41:16 +00:00
template <typename T>
Scavenger<T>::Scavenger(int sec, int defaultObjectListSize) :
m_objects(ObjectTimeList(defaultObjectListSize)),
m_sec(sec),
m_lastExcess(0),
2007-11-06 21:41:16 +00:00
m_claimed(0),
m_scavenged(0),
m_asExcess(0)
2007-11-06 21:41:16 +00:00
{
}
template <typename T>
Scavenger<T>::~Scavenger()
{
if (m_scavenged < m_claimed) {
for (size_t i = 0; i < m_objects.size(); ++i) {
ObjectTimePair &pair = m_objects[i];
if (pair.first != 0) {
T *ot = pair.first;
pair.first = 0;
delete ot;
++m_scavenged;
}
}
}
clearExcess(0);
}
template <typename T>
void
Scavenger<T>::claim(T *t)
{
// std::cerr << "Scavenger::claim(" << t << ")" << std::endl;
struct timeval tv;
(void)gettimeofday(&tv, 0);
int sec = tv.tv_sec;
for (size_t i = 0; i < m_objects.size(); ++i) {
ObjectTimePair &pair = m_objects[i];
if (pair.first == 0) {
pair.second = sec;
pair.first = t;
++m_claimed;
return;
}
}
#ifdef DEBUG_SCAVENGER
std::cerr << "WARNING: Scavenger::claim(" << t << "): run out of slots (at "
<< m_objects.size() << "), using non-RT-safe method" << std::endl;
#endif
2007-11-06 21:41:16 +00:00
pushExcess(t);
}
template <typename T>
void
Scavenger<T>::scavenge(bool clearNow)
{
#ifdef DEBUG_SCAVENGER
std::cerr << "Scavenger::scavenge: claimed " << m_claimed << ", scavenged " << m_scavenged << ", cleared as excess " << m_asExcess << std::endl;
#endif
2007-11-06 21:41:16 +00:00
if (m_scavenged >= m_claimed) return;
struct timeval tv;
(void)gettimeofday(&tv, 0);
int sec = tv.tv_sec;
bool anything = false;
2007-11-06 21:41:16 +00:00
for (size_t i = 0; i < m_objects.size(); ++i) {
ObjectTimePair &pair = m_objects[i];
if (!pair.first) continue;
if (clearNow || pair.second + m_sec < sec) {
2007-11-06 21:41:16 +00:00
T *ot = pair.first;
pair.first = 0;
delete ot;
++m_scavenged;
anything = true;
2007-11-06 21:41:16 +00:00
}
}
if (clearNow || anything || (sec > m_lastExcess + m_sec)) {
2007-11-06 21:41:16 +00:00
clearExcess(sec);
}
}
template <typename T>
void
Scavenger<T>::pushExcess(T *t)
{
2007-11-28 11:50:47 +00:00
m_excessMutex.lock();
2007-11-06 21:41:16 +00:00
m_excess.push_back(t);
struct timeval tv;
(void)gettimeofday(&tv, 0);
m_lastExcess = tv.tv_sec;
2007-11-28 11:50:47 +00:00
m_excessMutex.unlock();
2007-11-06 21:41:16 +00:00
}
template <typename T>
void
Scavenger<T>::clearExcess(int sec)
{
#ifdef DEBUG_SCAVENGER
std::cerr << "Scavenger::clearExcess: Excess now " << m_excess.size() << std::endl;
#endif
2007-11-28 11:50:47 +00:00
m_excessMutex.lock();
2007-11-06 21:41:16 +00:00
for (typename ObjectList::iterator i = m_excess.begin();
i != m_excess.end(); ++i) {
delete *i;
++m_asExcess;
2007-11-06 21:41:16 +00:00
}
m_excess.clear();
m_lastExcess = sec;
2007-11-28 11:50:47 +00:00
m_excessMutex.unlock();
2007-11-06 21:41:16 +00:00
}
}
#endif