generic_shared.hpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #ifndef CASADI_GENERIC_SHARED_HPP
27 #define CASADI_GENERIC_SHARED_HPP
28 
29 #include "casadi_common.hpp"
30 #include "exception.hpp"
31 #include <unordered_map>
32 #include <vector>
33 #include <cstdint>
34 #ifdef CASADI_WITH_THREAD
35 #ifdef CASADI_WITH_THREAD_MINGW
36 #include <mingw.mutex.h>
37 #else // CASADI_WITH_THREAD_MINGW
38 #include <mutex>
39 #endif // CASADI_WITH_THREAD_MINGW
40 #endif //CASADI_WITH_THREAD
41 
42 #include <memory>
43 
44 namespace casadi {
45 
46  // Forward declaration of weak reference class
47  template<typename Shared, typename Internal>
48  class GenericWeakRef;
49 
51  // Forward declaration of internal classes
52  template<typename Shared, typename Internal>
53  class GenericSharedInternal;
54 
55  template<typename Shared, typename Internal>
56  class GenericWeakRefInternal;
58 
59  template<typename Shared, typename Internal>
60  class CASADI_EXPORT GenericShared {
61 #ifndef SWIG
62  template<class B, class S> friend B shared_cast(S& A);
63  template<class B, class S> friend const B shared_cast(const S& A);
64 #endif // SWIG
65 
66  public:
67 #ifndef SWIG
69  GenericShared() {
70  node = nullptr;
71  }
72 
74  GenericShared(const GenericShared& ref) {
75  node = ref.node;
76  count_up();
77  }
78 
80  ~GenericShared() {
81  count_down();
82  }
83 
85  GenericShared& operator=(const GenericShared& ref);
86 
89  void own(Internal* node);
90 
96  void assign(Internal* node);
97 
99  Internal* get() const;
100 
102  casadi_int getCount() const;
103 
105  void swap(GenericShared& other);
106 
108  Internal* operator->() const;
110 #endif // SWIG
111 
112  std::string debug_repr() const;
113 
114 
116  bool is_null() const;
117 
123  casadi_int __hash__() const;
124 
126 #ifndef SWIG
131  protected:
132  void count_up(); // increase counter of the node
133  void count_down(); // decrease counter of the node
134  private:
135  Internal *node;
136 #endif // SWIG
138  };
139 
140  template<typename Shared, typename Internal>
141  class CASADI_EXPORT GenericWeakRef : public GenericShared<Shared, Internal> {
142  public:
143  friend class GenericSharedInternal<Shared, Internal>;
144 
146 
150  GenericWeakRef(int dummy=0);
151 
155  GenericWeakRef(Shared shared);
156 
160  Shared shared() const;
161 
165  bool alive() const;
166 
170  bool shared_if_alive(Shared& shared) const;
171 
175  GenericWeakRefInternal<Shared, Internal>* operator->();
176 
180  const GenericWeakRefInternal<Shared, Internal>* operator->() const;
181 
182 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
183  std::shared_ptr<std::mutex> get_mutex() const;
184 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
185 
186 #ifndef SWIG
187  private:
191  explicit GenericWeakRef(Internal* raw);
192 
196  void kill();
197 #endif // SWIG
198  };
199 
200 #ifndef SWIG
201 
207  template<class B, class S>
208  B shared_cast(S& A) {
209 
211  typename S::internal_base_type* ptr = A.get();
212 
214  B ret;
215 
217  if (!B::test_cast(ptr)) return ret;
218 
220  ret.own(ptr);
221  return ret;
222  }
223 
229  template<class B, class S>
230  const B shared_cast(const S& A) {
231  S A_copy = A;
232  return shared_cast<B, S>(A_copy);
233  }
234 
235 #endif // SWIG
236 
241 template<typename K, typename T>
242 class CASADI_EXPORT WeakCache {
243  public:
244  void tocache(const K& key, const T& f, bool needs_lock=true,
245  bool prune=true) {
246 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
247  // Safe access to cache_
248  casadi::conditional_lock_guard<std::mutex> lock(mtx_, needs_lock);
249 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
250  // Add to cache
251  auto it = cache_.find(key);
252 
253  if (it == cache_.end()) {
254  cache_.emplace(key, f);
255  } else if (!it->second.alive()) {
256  it->second = f;
257  }
258 
259  // Remove a lost reference, if any, to prevent uncontrolled growth
260  if (!prune) return;
261  for (auto it = cache_.begin(); it!=cache_.end(); ++it) {
262  if (!it->second.alive()) {
263  cache_.erase(it);
264  break; // just one dead reference is enough
265  }
266  }
267  }
268  /* \brief Thread-safe unique caching
269  * While an incache/tocache pair in multi-threaded context is safe
270  * it may lead to fresh cache entries being overwritten.
271  *
272  * A mutex lock_guard on the scope of an incache/tocache pair
273  * may lead to deadlocks.
274  *
275  */
276  void tocache_if_missing(const K& key, T& f, bool prune=true) {
277 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
278  // Safe access to cache_
279  std::lock_guard<std::mutex> lock(mtx_);
280 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
281  if (!incache(key, f, false)) {
282  tocache(key, f, false, prune);
283  }
284  }
285  bool incache(const K& key, T& f, bool needs_lock=true) const {
286 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
287  // Safe access to cache_
288  casadi::conditional_lock_guard<std::mutex> lock(mtx_, needs_lock);
289 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
290  auto it = cache_.find(key);
291  typename T::base_type temp;
292  if (it!=cache_.end() && it->second.shared_if_alive(temp)) {
293  f = shared_cast<T>(temp);
294  return true;
295  } else {
296  return false;
297  }
298  }
299  // Remove all expired entries in one pass.
300  void prune() {
301 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
302  // Safe access to cache_
303  std::lock_guard<std::mutex> lock(mtx_);
304 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
305  for (auto it = cache_.begin(); it!=cache_.end();) {
306  if (!it->second.alive()) {
307  it = cache_.erase(it);
308  } else {
309  ++it;
310  }
311  }
312  }
313  void cache(std::vector<K>& keys, std::vector<T>& entries) const {
314 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
315  // Safe access to cache_
316  std::lock_guard<std::mutex> lock(mtx_);
317 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
318  keys.clear();
319  entries.clear();
320  // Add all entries that haven't been deleted
321  for (auto&& cf : cache_) {
322  typename T::base_type temp;
323  if (cf.second.shared_if_alive(temp)) {
324  keys.push_back(cf.first);
325  entries.push_back(shared_cast<T>(temp));
326  }
327  }
328  }
329  private:
330  std::unordered_map<K,
332  > cache_;
333 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
334  mutable std::mutex mtx_;
335 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
336 };
337 
342 template<typename K, typename T>
343 class CASADI_EXPORT RevWeakCache {
344  public:
345  void tocache(const K& key, const T& f, bool needs_lock=true,
346  bool prune=true) {
347 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
348  // Safe access to cache_
349  casadi::conditional_lock_guard<std::mutex> lock(mtx_, needs_lock);
350 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
351  // Add to cache
352  const void* k = key.get();
353  auto it = pre_cache_.find(k);
354 
355  if (it == pre_cache_.end()) {
356  pre_cache_.emplace(k, key);
357  cache_.emplace(k, f);
358  } else if (it->second.get() != typename K::base_type(key).weak()->get()) {
359  // The address was recycled. Compare weak-reference identities without
360  // reading liveness, and replace both entries in place.
361  it->second = key;
362  cache_.find(k)->second = f;
363  }
364 
365  // Remove a lost reference, if any, to prevent uncontrolled growth
366  if (!prune) return;
367  for (auto it = pre_cache_.begin(); it!=pre_cache_.end(); ++it) {
368  if (!it->second.alive()) {
369  const void* dead = it->first;
370  pre_cache_.erase(it);
371  cache_.erase(dead);
372  break; // just one dead reference is enough
373  }
374  }
375  }
376  /* \brief Thread-safe unique caching
377  * While an incache/tocache pair in multi-threaded context is safe
378  * it may lead to fresh cache entries being overwritten.
379  *
380  * A mutex lock_guard on the scope of an incache/tocache pair
381  * may lead to deadlocks.
382  *
383  */
384  void tocache_if_missing(const K& key, T& f, bool prune=true) {
385 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
386  // Safe access to cache_
387  std::lock_guard<std::mutex> lock(mtx_);
388 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
389  if (!incache(key, f, false)) {
390  tocache(key, f, false, prune);
391  }
392  }
393  bool incache(const K& key, T& f, bool needs_lock=true) const {
394 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
395  // Safe access to cache_
396  casadi::conditional_lock_guard<std::mutex> lock(mtx_, needs_lock);
397 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
398  const void* k = key.get();
399  auto it = pre_cache_.find(k);
400  K temp;
401  if (it!=pre_cache_.end() && it->second.shared_if_alive(temp)) {
402  auto it2 = cache_.find(k);
403  f = it2->second;
404  return true;
405  } else {
406  return false;
407  }
408  }
409  private:
410  std::unordered_map<const void*,
412  > pre_cache_;
413  std::unordered_map<const void*, T> cache_;
414 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
415  mutable std::mutex mtx_;
416 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
417 };
418 
423 template<typename K, typename T>
424 class CASADI_EXPORT SimpleCache {
425  public:
426  void tocache(const K& key, const T& value) {
427  cache_[key] = value;
428  }
429 
430  bool incache(const K& key, T& value) const {
431  auto it = cache_.find(key);
432  if (it != cache_.end()) {
433  value = it->second;
434  return true;
435  }
436  return false;
437  }
438 
439  const std::unordered_map<K, T>& cache_map() const {
440  return cache_;
441  }
442 
443  private:
444  std::unordered_map<K, T> cache_;
445 };
446 
447 } // namespace casadi
448 
449 
450 #endif // CASADI_GENERIC_SHARED_HPP
void tocache_if_missing(const K &key, T &f, bool prune=true)
void tocache(const K &key, const T &f, bool needs_lock=true, bool prune=true)
bool incache(const K &key, T &f, bool needs_lock=true) const
void tocache(const K &key, const T &value)
const std::unordered_map< K, T > & cache_map() const
bool incache(const K &key, T &value) const
bool incache(const K &key, T &f, bool needs_lock=true) const
void cache(std::vector< K > &keys, std::vector< T > &entries) const
void tocache_if_missing(const K &key, T &f, bool prune=true)
void tocache(const K &key, const T &f, bool needs_lock=true, bool prune=true)
The casadi namespace.
Definition: archiver.hpp:32