2015-09-29 19:21:02 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
2016-05-28 14:21:16 +00:00
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <memory>
|
2015-09-29 19:21:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Позволяет хранить некоторый объект, использовать его read-only в разных потоках,
|
|
|
|
|
* и заменять его на другой в других потоках.
|
|
|
|
|
* Замена производится атомарно, при этом, читающие потоки могут работать с разными версиями объекта.
|
|
|
|
|
*
|
|
|
|
|
* Использование:
|
|
|
|
|
* MultiVersion<T> x;
|
|
|
|
|
* - при обновлении данных:
|
|
|
|
|
* x.set(new value);
|
|
|
|
|
* - при использовании данных для чтения в разных потоках:
|
|
|
|
|
* {
|
|
|
|
|
* MultiVersion<T>::Version current_version = x.get();
|
|
|
|
|
* // используем для чего-нибудь *current_version
|
|
|
|
|
* } // здесь перестаём владеть версией; если версия устарела, и её никто больше не использует - она будет уничтожена
|
|
|
|
|
*
|
|
|
|
|
* Все методы thread-safe.
|
|
|
|
|
*/
|
2016-05-28 14:21:16 +00:00
|
|
|
|
template <typename T, typename Ptr = std::shared_ptr<T>>
|
2015-09-29 19:21:02 +00:00
|
|
|
|
class MultiVersion
|
|
|
|
|
{
|
|
|
|
|
public:
|
2016-05-28 17:31:50 +00:00
|
|
|
|
/// Конкретная версия объекта для использования. shared_ptr определяет время жизни версии.
|
2016-05-28 10:35:44 +00:00
|
|
|
|
using Version = Ptr;
|
2015-09-29 19:21:02 +00:00
|
|
|
|
|
|
|
|
|
/// Инициализация по-умолчанию (NULL-ом).
|
|
|
|
|
MultiVersion() = default;
|
|
|
|
|
|
|
|
|
|
/// Инициализация первой версией.
|
|
|
|
|
MultiVersion(const Version & value)
|
|
|
|
|
{
|
|
|
|
|
set(value);
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-28 14:26:29 +00:00
|
|
|
|
/// Захватить владение первой версией.
|
|
|
|
|
MultiVersion(T * value)
|
|
|
|
|
{
|
|
|
|
|
set(value);
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-29 19:21:02 +00:00
|
|
|
|
MultiVersion(Version && value)
|
|
|
|
|
{
|
|
|
|
|
set(std::move(value));
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-09 12:50:26 +00:00
|
|
|
|
MultiVersion(std::unique_ptr<T> && value)
|
|
|
|
|
{
|
|
|
|
|
set(std::move(value));
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-28 17:31:50 +00:00
|
|
|
|
/// Получить текущую версию для использования. Возвращает shared_ptr, который определяет время жизни версии.
|
2015-09-29 19:21:02 +00:00
|
|
|
|
const Version get() const
|
|
|
|
|
{
|
2016-05-28 17:31:50 +00:00
|
|
|
|
/// TODO: можно ли заменять shared_ptr lock-free? (Можно, если сделать свою реализацию с использованием cmpxchg16b.)
|
2016-05-28 14:21:16 +00:00
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
2015-09-29 19:21:02 +00:00
|
|
|
|
return current_version;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Обновить объект новой версией.
|
|
|
|
|
void set(Version value)
|
|
|
|
|
{
|
2016-05-28 14:21:16 +00:00
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
2015-09-29 19:21:02 +00:00
|
|
|
|
current_version = value;
|
|
|
|
|
}
|
2016-05-28 14:21:16 +00:00
|
|
|
|
|
2016-05-28 14:26:29 +00:00
|
|
|
|
/// Обновить объект новой версией и захватить владение.
|
2016-05-28 14:24:27 +00:00
|
|
|
|
void set(T * value)
|
|
|
|
|
{
|
|
|
|
|
set(Version(value));
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-09 12:50:26 +00:00
|
|
|
|
void set(std::unique_ptr<T> && value)
|
|
|
|
|
{
|
|
|
|
|
set(Version(value.release()));
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-29 19:21:02 +00:00
|
|
|
|
private:
|
|
|
|
|
Version current_version;
|
2016-05-28 14:21:16 +00:00
|
|
|
|
mutable std::mutex mutex;
|
2015-09-29 19:21:02 +00:00
|
|
|
|
};
|