1 | #pragma once |
2 | |
3 | #include <mutex> |
4 | #include <memory> |
5 | |
6 | |
7 | /** Allow to store and read-only usage of an object in several threads, |
8 | * and to atomically replace an object in another thread. |
9 | * The replacement is atomic and reading threads can work with different versions of an object. |
10 | * |
11 | * Usage: |
12 | * MultiVersion<T> x; |
13 | * - on data update: |
14 | * x.set(new value); |
15 | * - on read-only usage: |
16 | * { |
17 | * MultiVersion<T>::Version current_version = x.get(); |
18 | * // use *current_version |
19 | * } // now we finish own current version; if the version is outdated and no one else is using it - it will be destroyed. |
20 | * |
21 | * All methods are thread-safe. |
22 | */ |
23 | template <typename T> |
24 | class MultiVersion |
25 | { |
26 | public: |
27 | /// Version of object for usage. shared_ptr manage lifetime of version. |
28 | using Version = std::shared_ptr<const T>; |
29 | |
30 | /// Default initialization - by nullptr. |
31 | MultiVersion() = default; |
32 | |
33 | MultiVersion(std::unique_ptr<const T> && value) |
34 | { |
35 | set(std::move(value)); |
36 | } |
37 | |
38 | /// Obtain current version for read-only usage. Returns shared_ptr, that manages lifetime of version. |
39 | Version get() const |
40 | { |
41 | /// NOTE: is it possible to lock-free replace of shared_ptr? |
42 | std::lock_guard lock(mutex); |
43 | return current_version; |
44 | } |
45 | |
46 | /// Update an object with new version. |
47 | void set(std::unique_ptr<const T> && value) |
48 | { |
49 | std::lock_guard lock(mutex); |
50 | current_version = std::move(value); |
51 | } |
52 | |
53 | private: |
54 | Version current_version; |
55 | mutable std::mutex mutex; |
56 | }; |
57 | |