You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

338 lines
10 KiB
C++

/***************************************************************************
* Copyright (C) gempa GmbH *
* All rights reserved. *
* Contact: gempa GmbH (seiscomp-dev@gempa.de) *
* *
* GNU Affero General Public License Usage *
* This file may be used under the terms of the GNU Affero *
* Public License version 3.0 as published by the Free Software Foundation *
* and appearing in the file LICENSE included in the packaging of this *
* file. Please review the following information to ensure the GNU Affero *
* Public License version 3.0 requirements will be met: *
* https://www.gnu.org/licenses/agpl-3.0.html. *
* *
* Other Usage *
* Alternatively, this file may be used in accordance with the terms and *
* conditions contained in a signed written agreement between you and *
* gempa GmbH. *
***************************************************************************/
#ifndef SEISCOMP_DATAMODEL_PUBLICOBJECT_CACHE_H__
#define SEISCOMP_DATAMODEL_PUBLICOBJECT_CACHE_H__
#include <seiscomp/core/timewindow.h>
#include <seiscomp/datamodel/publicobject.h>
#include <queue>
#include <functional>
namespace Seiscomp {
namespace DataModel {
DEFINE_SMARTPOINTER(DatabaseArchive);
struct SC_SYSTEM_CORE_API CachePopCallback {
virtual void handle(PublicObject *obj) {}
virtual ~CachePopCallback() {}
};
/**
* @brief The PublicObjectCache class implements an PublicObject store to
* prevent PublicObjects from de-registration when they go out of scope.
*
* The global registraty of PublicObject and the database access are common
* patterns in user code. To simplify the following code sequence:
*
* @code
* auto po = PublicObject::Find(publicID);
* if ( !po ) {
* po = _archive->getObject(Pick::TypeInfo(), publicID)
* }
* auto pick = Pick::Cast(po);
* if ( pick ) {
* // Do something
* }
* @endcode
*
* The cache encapsulates this code in a single function and can implement
* several strategies for storing objects in memory. Currently a cache size
* based strategy and a time span based strategy are implemented.
*
* The cache receives an optional pointer to the database archive to load
* objects from database if required. That makes the object lookup a breeze:
*
* @code
* auto pick = _cache.get<Pick>(publicID);
* if ( pick ) {
* // Do something
* }
* @endcode
*
* The following two code examples illustrate the mode of operation of the
* cache:
*
* @code
* // A pick is being created and registered in the global object pool.
* PickPtr pick = Pick::Create();
* string publicID = pick->publicID();
*
* // Although the pick has never been added to the cache it will be found
* // through the global object registry. And it will be added to the cache
* // automatically for later retrieval.
* auto cachedPick = _cache.get<Pick>(publicID);
* cachedPick = nullptr; // Just drop the reference
*
* // Even if the pick pointer is set to nullptr which should cause the
* // pick to be disposed (-> SmartPointer) it is not the case. The cache still
* // manages a smart pointer to the pick and prevents it from being destroyed
* // until it will be removed from the cache.
* pick = nullptr;
*
* // The following lookup will succeed because the cache is still holding the
* // pick.
* cachedPick = _cache.get<Pick>(publicID);
* @endcode
*
* Continuing from the above code snippet, a totally different part of the code
* might want to lookup the object through the global registry. Due to the
* cache storage, it will success:
*
* @code
* // The pick is still registered globally because the cache is still holding
* // it.
* Pick *pick = PublicObject::Find(publicID);
* @endcode
*/
class SC_SYSTEM_CORE_API PublicObjectCache : public Core::BaseObject {
private:
struct CacheItem;
typedef std::map<std::string, CacheItem*> CacheLookup;
// Simple double linked list
struct CacheItem {
PublicObjectPtr object;
time_t timestamp;
CacheItem *prev;
CacheItem *next;
CacheLookup::iterator lookup;
};
public:
typedef std::function<void (PublicObject*)> PopCallback;
typedef std::function<void (PublicObject*)> PushCallback;
public:
class const_iterator {
// ------------------------------------------------------------------
// Xstruction
// ------------------------------------------------------------------
public:
//! C'tor
const_iterator();
//! Copy c'tor
const_iterator(const const_iterator &it);
// ------------------------------------------------------------------
// Operators
// ------------------------------------------------------------------
public:
PublicObject* operator*();
const_iterator &operator=(const const_iterator &it);
const_iterator &operator++();
const_iterator operator++(int);
bool operator==(const const_iterator &it);
bool operator!=(const const_iterator &it);
// ------------------------------------------------------------------
// Interface
// ------------------------------------------------------------------
public:
time_t timeStamp() const;
// ------------------------------------------------------------------
// Implementation
// ------------------------------------------------------------------
private:
const_iterator(CacheItem *);
CacheItem *_item;
friend class PublicObjectCache;
};
public:
PublicObjectCache();
PublicObjectCache(DatabaseArchive *ar);
~PublicObjectCache() override;
public:
void setDatabaseArchive(DatabaseArchive *);
void setPopCallback(const PopCallback &);
void setPopCallback(CachePopCallback *);
void removePopCallback();
/**
* Insert a new object into the cache.
* @param po The PublicObject pointer to insert
* @return True or False
*/
virtual bool feed(PublicObject *po) = 0;
/**
* Removes an object from the cache. This function
* can be quite time consuming since its a linear search.
* @param po The PublicObject pointer to be removed
* @return True or False
*/
bool remove(PublicObject *po);
//! Clears the cache.
void clear();
/**
* @brief Retrieves the type information of a public object which
* is either globally registered or stored in the cache.
*
* A database lookup will not be performed!
*
* @param publicID The publicID of the PublicObject
* @return The RTTI pointer if found, nullptr otherwise.
*/
const Core::RTTI *typeInfo(const std::string &publicID) const;
/**
* @brief Retrieves an object by publicID.
*
* If the object is not in the cache and not globally registered it
* will be fetched from the database and inserted into the cache.
* Please note that any object retrieved will be fed again to the
* cache to raise its priority.
*
* @param classType The required class type of the searched objects.
* If the object is not the same type or derived from
* it then nullptr will be returned.
* @param publicID The publicID of the PublicObject to be
* retrieved
* @return The PublicObject pointer or nullptr
*/
PublicObject *find(const Core::RTTI &classType,
const std::string &publicID);
/**
* Returns the cached state the the last object returned by find.
* This function was introduced in API 1.1.
* @return Whether an already cached object has been returned or not
*/
bool cached() const { return _cached; }
//! Time window currently in buffer
Core::TimeWindow timeWindow() const;
template <typename T>
typename Core::SmartPointer<T>::Impl
get(const std::string &publicID) {
return static_cast<T*>(find(T::TypeInfo(), publicID));
}
//! Returns the time of the oldest entry
Core::Time oldest() const;
//! Returns whether the cache is empty or not
bool empty() const { return _front == nullptr; }
//! Returns the number of cached elements
size_t size() const { return _size; }
const_iterator begin() const;
const_iterator end() const;
/**
* @brief Checks if the passed object pointer is part of the cache.
* @param object The object to be checked
* @return true if part of this cache, false otherwise
*/
bool contains(PublicObject *object) const;
/**
* @brief Checks if an instance with the passed publicID is part of
* the cache.
* @param publicID The publicID of the object that is part of the cache.
* @return true if part of this cache, false otherwise
*/
bool contains(const std::string &publicID) const;
protected:
void pop();
void push(PublicObject *obj);
void setCached(bool cached) { _cached = cached; }
DatabaseArchive *databaseArchive() { return _archive.get(); }
private:
DatabaseArchivePtr _archive;
size_t _size;
CacheItem *_front;
CacheItem *_back;
CacheLookup _lookup;
bool _cached;
PushCallback _pushCallback;
PopCallback _popCallback;
};
class SC_SYSTEM_CORE_API PublicObjectRingBuffer : public PublicObjectCache {
public:
PublicObjectRingBuffer();
PublicObjectRingBuffer(DatabaseArchive *ar,
size_t bufferSize);
public:
bool setBufferSize(size_t bufferSize);
bool feed(PublicObject *po) override;
private:
size_t _bufferSize;
};
class SC_SYSTEM_CORE_API PublicObjectTimeSpanBuffer : public PublicObjectCache {
public:
PublicObjectTimeSpanBuffer();
PublicObjectTimeSpanBuffer(DatabaseArchive *ar,
const Core::TimeSpan &length);
public:
bool setTimeSpan(const Core::TimeSpan &);
bool feed(PublicObject *po) override;
private:
Core::TimeSpan _timeSpan;
};
}
}
#endif