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
70  node = nullptr;
71  }
72 
75  node = ref.node;
76  count_up();
77  }
78 
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 
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 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
246  // Safe access to cache_
247  casadi::conditional_lock_guard<std::mutex> lock(mtx_, needs_lock);
248 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
249  // Add to cache
250  cache_.insert(std::make_pair(key, f));
251  // Remove a lost reference, if any, to prevent uncontrolled growth
252  for (auto it = cache_.begin(); it!=cache_.end(); ++it) {
253  if (!it->second.alive()) {
254  cache_.erase(it);
255  break; // just one dead reference is enough
256  }
257  }
258  }
259  /* \brief Thread-safe unique caching
260  * While an incache/tocache pair in multi-threaded context is safe
261  * it may lead to fresh cache entries being overwritten.
262  *
263  * A mutex lock_guard on the scope of an incache/tocache pair
264  * may lead to deadlocks.
265  *
266  */
267  void tocache_if_missing(const K& key, T& f) {
268 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
269  // Safe access to cache_
270  std::lock_guard<std::mutex> lock(mtx_);
271 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
272  if (!incache(key, f, false)) {
273  tocache(key, f, false);
274  }
275  }
276  bool incache(const K& key, T& f, bool needs_lock=true) const {
277 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
278  // Safe access to cache_
279  casadi::conditional_lock_guard<std::mutex> lock(mtx_, needs_lock);
280 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
281  auto it = cache_.find(key);
282  typename T::base_type temp;
283  if (it!=cache_.end() && it->second.shared_if_alive(temp)) {
284  f = shared_cast<T>(temp);
285  return true;
286  } else {
287  return false;
288  }
289  }
290  void cache(std::vector<K>& keys, std::vector<T>& entries) const {
291 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
292  // Safe access to cache_
293  std::lock_guard<std::mutex> lock(mtx_);
294 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
295  keys.clear();
296  entries.clear();
297  // Add all entries that haven't been deleted
298  for (auto&& cf : cache_) {
299  typename T::base_type temp;
300  if (cf.second.shared_if_alive(temp)) {
301  keys.push_back(cf.first);
302  entries.push_back(shared_cast<T>(temp));
303  }
304  }
305  }
306  private:
307  std::unordered_map<K,
309  > cache_;
310 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
311  mutable std::mutex mtx_;
312 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
313 };
314 
315 // gcc-12 is overzealous about use-after-free warnings
316 // <12 or >12 works fine
317 #pragma GCC diagnostic push
318 #if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ == 12)
319 #pragma GCC diagnostic ignored "-Wuse-after-free"
320 #endif
321 
326 template<typename K, typename T>
327 class CASADI_EXPORT RevWeakCache {
328  public:
329  void tocache(const K& key, const T& f, bool needs_lock=true) {
330 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
331  // Safe access to cache_
332  casadi::conditional_lock_guard<std::mutex> lock(mtx_, needs_lock);
333 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
334  // Add to cache
335  const void* k = key.get();
336  pre_cache_.insert(std::make_pair(k, key));
337  cache_.insert(std::make_pair(k, f));
338  // Remove a lost reference, if any, to prevent uncontrolled growth
339  for (auto it = pre_cache_.begin(); it!=pre_cache_.end(); ++it) {
340  if (!it->second.alive()) {
341  pre_cache_.erase(it);
342  cache_.erase(it->first);
343  break; // just one dead reference is enough
344  }
345  }
346  }
347  /* \brief Thread-safe unique caching
348  * While an incache/tocache pair in multi-threaded context is safe
349  * it may lead to fresh cache entries being overwritten.
350  *
351  * A mutex lock_guard on the scope of an incache/tocache pair
352  * may lead to deadlocks.
353  *
354  */
355  void tocache_if_missing(const K& key, T& f) {
356 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
357  // Safe access to cache_
358  std::lock_guard<std::mutex> lock(mtx_);
359 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
360  if (!incache(key, f, false)) {
361  tocache(key, f, false);
362  }
363  }
364  bool incache(const K& key, T& f, bool needs_lock=true) const {
365 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
366  // Safe access to cache_
367  casadi::conditional_lock_guard<std::mutex> lock(mtx_, needs_lock);
368 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
369  const void* k = key.get();
370  auto it = pre_cache_.find(k);
371  K temp;
372  if (it!=pre_cache_.end() && it->second.shared_if_alive(temp)) {
373  auto it2 = cache_.find(k);
374  f = it2->second;
375  return true;
376  } else {
377  return false;
378  }
379  }
380  private:
381  std::unordered_map<const void*,
383  > pre_cache_;
384  std::unordered_map<const void*, T> cache_;
385 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
386  mutable std::mutex mtx_;
387 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
388 };
389 
390 #pragma GCC diagnostic pop
391 
392 } // namespace casadi
393 
394 
395 #endif // CASADI_GENERIC_SHARED_HPP
friend const B shared_cast(const S &A)
Typecast a shared object to a base class to a shared object to a derived class,.
GenericShared(const GenericShared &ref)
Copy constructor (shallow copy)
GenericShared()
Default constructor.
~GenericShared()
Destructor.
friend B shared_cast(S &A)
Typecast a shared object to a base class to a shared object to a derived class,.
void tocache_if_missing(const K &key, T &f)
void tocache(const K &key, const T &f, bool needs_lock=true)
bool incache(const K &key, T &f, bool needs_lock=true) const
void tocache(const K &key, const T &f, bool needs_lock=true)
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)
The casadi namespace.
Definition: archiver.cpp:28
B shared_cast(S &A)
Typecast a shared object to a base class to a shared object to a derived class,.