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
|
|
|
|
|
|
|
|
2017-12-11 18:05:16 +00:00
|
|
|
/** Allow to store and read-only usage of an object in several threads,
|
|
|
|
* and to atomically replace an object in another thread.
|
|
|
|
* The replacement is atomic and reading threads can work with different versions of an object.
|
2015-09-29 19:21:02 +00:00
|
|
|
*
|
2017-12-11 18:05:16 +00:00
|
|
|
* Usage:
|
|
|
|
* MultiVersion<T> x;
|
|
|
|
* - on data update:
|
|
|
|
* x.set(new value);
|
|
|
|
* - on read-only usage:
|
|
|
|
* {
|
|
|
|
* MultiVersion<T>::Version current_version = x.get();
|
|
|
|
* // use *current_version
|
|
|
|
* } // now we finish own current version; if the version is outdated and no one else is using it - it will be destroyed.
|
2015-09-29 19:21:02 +00:00
|
|
|
*
|
2017-12-11 18:05:16 +00:00
|
|
|
* All methods are thread-safe.
|
2015-09-29 19:21:02 +00:00
|
|
|
*/
|
2017-12-11 18:05:16 +00:00
|
|
|
template <typename T>
|
2015-09-29 19:21:02 +00:00
|
|
|
class MultiVersion
|
|
|
|
{
|
|
|
|
public:
|
2017-12-11 18:05:16 +00:00
|
|
|
/// Version of object for usage. shared_ptr manage lifetime of version.
|
|
|
|
using Version = std::shared_ptr<const T>;
|
2015-09-29 19:21:02 +00:00
|
|
|
|
2017-12-11 18:05:16 +00:00
|
|
|
/// Default initialization - by nullptr.
|
2017-04-01 07:20:54 +00:00
|
|
|
MultiVersion() = default;
|
2015-09-29 19:21:02 +00:00
|
|
|
|
2017-12-11 18:05:16 +00:00
|
|
|
/// Initialization with first version.
|
2017-04-01 07:20:54 +00:00
|
|
|
MultiVersion(const Version & value)
|
|
|
|
{
|
|
|
|
set(value);
|
|
|
|
}
|
2015-09-29 19:21:02 +00:00
|
|
|
|
2017-12-11 18:05:16 +00:00
|
|
|
/// Take an ownership of first version.
|
2017-12-11 18:07:08 +00:00
|
|
|
MultiVersion(const T * value)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
set(value);
|
|
|
|
}
|
2016-05-28 14:26:29 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
MultiVersion(Version && value)
|
|
|
|
{
|
|
|
|
set(std::move(value));
|
|
|
|
}
|
2015-09-29 19:21:02 +00:00
|
|
|
|
2017-12-11 18:07:08 +00:00
|
|
|
MultiVersion(std::unique_ptr<const T> && value)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
set(std::move(value));
|
|
|
|
}
|
2016-06-09 12:50:26 +00:00
|
|
|
|
2017-12-11 18:05:16 +00:00
|
|
|
/// Obtain current version for read-only usage. Returns shared_ptr, that manages lifetime of version.
|
2017-04-01 07:20:54 +00:00
|
|
|
const Version get() const
|
|
|
|
{
|
2017-12-11 18:05:16 +00:00
|
|
|
/// NOTE: is it possible to lock-free replace of shared_ptr?
|
|
|
|
std::lock_guard lock(mutex);
|
2017-04-01 07:20:54 +00:00
|
|
|
return current_version;
|
|
|
|
}
|
2015-09-29 19:21:02 +00:00
|
|
|
|
2017-12-11 18:05:16 +00:00
|
|
|
/// Update an object with new version.
|
2017-12-11 18:07:08 +00:00
|
|
|
void set(const Version & value)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-12-11 18:05:16 +00:00
|
|
|
std::lock_guard lock(mutex);
|
2017-04-01 07:20:54 +00:00
|
|
|
current_version = value;
|
|
|
|
}
|
2016-05-28 14:21:16 +00:00
|
|
|
|
2017-12-11 18:05:16 +00:00
|
|
|
/// Update an object with new version and take an ownership of it.
|
2017-12-11 18:07:08 +00:00
|
|
|
void set(const T * value)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
set(Version(value));
|
|
|
|
}
|
2016-05-28 14:24:27 +00:00
|
|
|
|
2017-12-11 18:07:08 +00:00
|
|
|
void set(std::unique_ptr<const T> && value)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
set(Version(value.release()));
|
|
|
|
}
|
2016-06-09 12:50:26 +00:00
|
|
|
|
2015-09-29 19:21:02 +00:00
|
|
|
private:
|
2017-04-01 07:20:54 +00:00
|
|
|
Version current_version;
|
|
|
|
mutable std::mutex mutex;
|
2015-09-29 19:21:02 +00:00
|
|
|
};
|