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 #ifdef CASADI_WITH_THREAD
34 #ifdef CASADI_WITH_THREAD_MINGW
35 #include <mingw.mutex.h>
36 #else // CASADI_WITH_THREAD_MINGW
37 #include <mutex>
38 #endif // CASADI_WITH_THREAD_MINGW
39 #endif //CASADI_WITH_THREAD
40 
41 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
42 #include <memory>
43 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
44 
45 namespace casadi {
46 
47  // Forward declaration of weak reference class
48  template<typename Shared, typename Internal>
49  class GenericWeakRef;
50 
52  // Forward declaration of internal classes
53  template<typename Shared, typename Internal>
54  class GenericSharedInternal;
55 
56  template<typename Shared, typename Internal>
57  class GenericWeakRefInternal;
59 
60  template<typename Shared, typename Internal>
61  class CASADI_EXPORT GenericShared {
62 #ifndef SWIG
63  template<class B, class S> friend B shared_cast(S& A);
64  template<class B, class S> friend const B shared_cast(const S& A);
65 #endif // SWIG
66 
67  public:
68 #ifndef SWIG
71  node = nullptr;
72  }
73 
76  node = ref.node;
77  count_up();
78  }
79 
82  count_down();
83  }
84 
86  GenericShared& operator=(const GenericShared& ref);
87 
90  void own(Internal* node);
91 
97  void assign(Internal* node);
98 
100  Internal* get() const;
101 
103  casadi_int getCount() const;
104 
106  void swap(GenericShared& other);
107 
109  Internal* operator->() const;
111 #endif // SWIG
112 
113  std::string debug_repr() const;
114 
115 
117  bool is_null() const;
118 
124  casadi_int __hash__() const;
125 
127 #ifndef SWIG
132  protected:
133  void count_up(); // increase counter of the node
134  void count_down(); // decrease counter of the node
135  private:
136  Internal *node;
137 #endif // SWIG
139  };
140 
141  template<typename Shared, typename Internal>
142  class CASADI_EXPORT GenericWeakRef : public GenericShared<Shared, Internal> {
143  public:
144  friend class GenericSharedInternal<Shared, Internal>;
145 
147 
151  GenericWeakRef(int dummy=0);
152 
156  GenericWeakRef(Shared shared);
157 
161  Shared shared() const;
162 
166  bool alive() const;
167 
171  bool shared_if_alive(Shared& shared) const;
172 
177 
181  const GenericWeakRefInternal<Shared, Internal>* operator->() const;
182 
183 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
184  std::shared_ptr<std::mutex> get_mutex() const;
185 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
186 
187 #ifndef SWIG
188  private:
192  explicit GenericWeakRef(Internal* raw);
193 
197  void kill();
198 #endif // SWIG
199  };
200 
201 #ifndef SWIG
202 
208  template<class B, class S>
209  B shared_cast(S& A) {
210 
212  typename S::internal_base_type* ptr = A.get();
213 
215  B ret;
216 
218  if (!B::test_cast(ptr)) return ret;
219 
221  ret.own(ptr);
222  return ret;
223  }
224 
230  template<class B, class S>
231  const B shared_cast(const S& A) {
232  S A_copy = A;
233  return shared_cast<B, S>(A_copy);
234  }
235 
236 #endif // SWIG
237 
238 
239 template<typename K, typename T>
240 class CASADI_EXPORT WeakCache {
241  public:
242  void tocache(const K& key, const T& f, bool needs_lock=true) {
243 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
244  // Safe access to cache_
245  casadi::conditional_lock_guard<std::mutex> lock(mtx_, needs_lock);
246 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
247  // Add to cache
248  cache_.insert(std::make_pair(key, f));
249  // Remove a lost reference, if any, to prevent uncontrolled growth
250  for (auto it = cache_.begin(); it!=cache_.end(); ++it) {
251  if (!it->second.alive()) {
252  cache_.erase(it);
253  break; // just one dead reference is enough
254  }
255  }
256  }
257  /* \brief Thread-safe unique caching
258  * While an incache/tocache pair in multi-threaded context is safe
259  * it may lead to fresh cache entries being overwritten.
260  *
261  * A mutex lock_guard on the scope of an incache/tocache pair
262  * may lead to deadlocks.
263  *
264  */
265  void tocache_if_missing(const K& key, T& f) {
266 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
267  // Safe access to cache_
268  std::lock_guard<std::mutex> lock(mtx_);
269 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
270  if (!incache(key, f, false)) {
271  tocache(key, f, false);
272  }
273  }
274  bool incache(const K& key, T& f, bool needs_lock=true) const {
275 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
276  // Safe access to cache_
277  casadi::conditional_lock_guard<std::mutex> lock(mtx_, needs_lock);
278 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
279  auto it = cache_.find(key);
280  typename T::base_type temp;
281  if (it!=cache_.end() && it->second.shared_if_alive(temp)) {
282  f = shared_cast<T>(temp);
283  return true;
284  } else {
285  return false;
286  }
287  }
288  void cache(std::vector<K>& keys, std::vector<T>& entries) const {
289 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
290  // Safe access to cache_
291  std::lock_guard<std::mutex> lock(mtx_);
292 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
293  keys.clear();
294  entries.clear();
295  // Add all entries that haven't been deleted
296  for (auto&& cf : cache_) {
297  typename T::base_type temp;
298  if (cf.second.shared_if_alive(temp)) {
299  keys.push_back(cf.first);
300  entries.push_back(shared_cast<T>(temp));
301  }
302  }
303  }
304  private:
305  std::unordered_map<K,
307  > cache_;
308 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
309  mutable std::mutex mtx_;
310 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
311 };
312 
313 
314 
315 } // namespace casadi
316 
317 
318 #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(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,.