AMDiS  0.3
The Adaptive Multi-Dimensional Simulation Toolbox
ConcurrentCache.hpp
1 #pragma once
2 
3 #include <mutex>
4 #include <shared_mutex>
5 #include <thread>
6 #include <tuple>
7 #include <type_traits>
8 #include <unordered_map>
9 
10 #include <dune/common/hash.hh>
11 
12 namespace AMDiS
13 {
15  template <class Container>
17 
19  template <class Container>
21 
23  template <class Container>
25 
26 
29 
49  template <class Key,
50  class Data,
51  template <class> class Policy = ThreadLocalPolicy,
52  class Container = std::unordered_map<Key, Data>>
54 
55 
56 #ifdef DOXYGEN
57 
66  template <class Container>
67  class ConcurrentCachePolicy;
68 #endif
69 
70  // implementation of the consecutive policy. Data is stored in instance variable.
71  template <class Container>
72  struct ConsecutivePolicy
73  {
74  using key_type = typename Container::key_type;
75  using data_type = typename Container::mapped_type;
76  using container_type = Container;
77 
78  template <class F, class... Args>
79  data_type const& get_or_init(key_type const& key, F&& f, Args&&... args) const
80  {
81  return impl(std::is_default_constructible<data_type>{}, key, FWD(f), FWD(args)...);
82  }
83 
84  private:
85  // data_type is default_constructible
86  template <class F, class... Args>
87  data_type const& impl(std::true_type, key_type const& key, F&& f, Args&&... args) const
88  {
89  data_type empty;
90  auto it = cachedData_.emplace(key, std::move(empty));
91  if (it.second) {
92  data_type data = f(key, FWD(args)...);
93  it.first->second = std::move(data);
94  }
95  return it.first->second;
96  }
97 
98  // data_type is not default_constructible
99  template <class F, class... Args>
100  data_type const& impl(std::false_type, key_type const& key, F&& f, Args&&... args) const
101  {
102  auto it = cachedData_.find(key);
103  if (it != cachedData_.end())
104  return it->second;
105  else {
106  data_type data = f(key, FWD(args)...);
107  auto it = cachedData_.emplace(key, std::move(data));
108  return it.first->second;
109  }
110  }
111 
112  mutable container_type cachedData_;
113  };
114 
115 
116  // implementation of the ThreadLocal policy. Data is stored in thread_local variable.
117  template <class Container>
118  struct ThreadLocalPolicy
119  {
120  using key_type = typename Container::key_type;
121  using data_type = typename Container::mapped_type;
122  using container_type = Container;
123 
124  template <class F, class... Args>
125  static data_type const& get_or_init(key_type const& key, F&& f, Args&&... args)
126  {
127  return impl(std::is_default_constructible<data_type>{}, key, FWD(f), FWD(args)...);
128  }
129 
130  private:
131  // data_type is default_constructible
132  template <class F, class... Args>
133  static data_type const& impl(std::true_type, key_type const& key, F&& f, Args&&... args)
134  {
135  // Container to store the cached values
136  thread_local container_type cached_data;
137 
138  data_type empty;
139  auto it = cached_data.emplace(key, std::move(empty));
140  if (it.second) {
141  data_type data = f(key, FWD(args)...);
142  it.first->second = std::move(data);
143  }
144  return it.first->second;
145  }
146 
147  // data_type is not default_constructible
148  template <class F, class... Args>
149  static data_type const& impl(std::false_type, key_type const& key, F&& f, Args&&... args)
150  {
151  // Container to store the cached values
152  thread_local container_type cached_data;
153 
154  auto it = cached_data.find(key);
155  if (it != cached_data.end())
156  return it->second;
157  else {
158  data_type data = f(key, FWD(args)...);
159  auto it = cached_data.emplace(key, std::move(data));
160  return it.first->second;
161  }
162  }
163  };
164 
165 
166  // implementation of the Shared policy. Data is stored in static variable.
167  template <class Container>
168  struct StaticLockedPolicy
169  {
170  using key_type = typename Container::key_type;
171  using data_type = typename Container::mapped_type;
172  using container_type = Container;
173 
174  template <class F, class... Args>
175  static data_type const& get_or_init(key_type const& key, F&& f, Args&&... args)
176  {
177  // Container to store the cached values
178  static container_type cached_data;
179 
180  // mutex used to access the data in the container, necessary since
181  // access emplace is read-write.
182  using mutex_type = std::shared_timed_mutex;
183  static mutex_type access_mutex;
184 
185  // first try to lock for read-only, if an element for key is found, return it,
186  // if not, obtain a unique_lock to insert a new element and initialize it.
187  std::shared_lock<mutex_type> read_lock(access_mutex);
188  auto it = cached_data.find(key);
189  if (it != cached_data.end())
190  return it->second;
191  else {
192  read_lock.unlock();
193  data_type data = f(key, FWD(args)...);
194  std::unique_lock<mutex_type> write_lock(access_mutex);
195  auto new_it = cached_data.emplace(key, std::move(data));
196  return new_it.first->second;
197  }
198  }
199  };
200 
201 
202  template <class Key, class Data, template <class> class Policy, class Container>
203  class ConcurrentCache
204  : protected Policy<Container>
205  {
206  using key_type = Key;
207  using data_type = Data;
208 
209  public:
210 
212 
219  template <class F, class... Args>
220  data_type const& get(key_type const& key, F&& f, Args&&... args) const
221  {
222  static_assert(std::is_invocable_r_v<data_type, F, key_type, Args...>,
223  "Functor F must have the signature data_type(key_type, Args...)");
224 
225  return ConcurrentCache::get_or_init(key, FWD(f), FWD(args)...);
226  }
227  };
228 
229 } // end namespace AMDiS
Store cache thread local, requires no locking.
Definition: ConcurrentCache.hpp:20
The class template ConcurrentCache describes an associative static container that allows the concurre...
Definition: ConcurrentCache.hpp:53
Stores cache global static, requires locking on write access.
Definition: ConcurrentCache.hpp:24
Contains all classes needed for solving linear and non linear equation systems.
Definition: AdaptBase.hpp:6
Store cache in instance.
Definition: ConcurrentCache.hpp:16