Actual source code: objpool.hpp
1: #ifndef PETSCOBJECTPOOL_HPP
2: #define PETSCOBJECTPOOL_HPP
4: #include <petscsys.h>
5: #if !PetscDefined(HAVE_CXX_DIALECT_CXX11)
6: #error "ObjectPool requires c++11"
7: #endif
9: #if defined(__cplusplus)
11: #include <stack>
12: #include <type_traits>
14: #define PETSC_STATIC_ASSERT_BASE_CLASS(base_,derived_,mess_) \
15: static_assert(std::is_base_of<base_,derived_>::value,mess_)
17: namespace Petsc {
19: // Allocator ABC for interoperability with C ctors and dtors.
20: template <typename T>
21: class Allocator {
22: public:
23: typedef T value_type;
25: PETSC_NODISCARD PetscErrorCode create(value_type*) noexcept;
26: PETSC_NODISCARD PetscErrorCode destroy(value_type&) noexcept;
27: PETSC_NODISCARD PetscErrorCode reset(value_type&) noexcept;
28: PETSC_NODISCARD PetscErrorCode finalize(void) noexcept;
30: protected:
31: // make the constructor protected, this forces this class to be derived from to ever be
32: // instantiated
33: Allocator() {}
34: ~Allocator() {}
35: };
37: // Default allocator that performs the bare minimum of petsc object creation and
38: // desctruction
39: template <typename T>
40: class CAllocator : Allocator<T>
41: {
42: public:
43: typedef Allocator<T> allocator_t;
44: typedef typename allocator_t::value_type value_type;
46: PETSC_NODISCARD PetscErrorCode create(value_type *obj) const noexcept
47: {
51: PetscNew(obj);
52: return(0);
53: }
55: PETSC_NODISCARD PetscErrorCode destroy(value_type &obj) const noexcept
56: {
60: (*obj->ops->destroy)(obj);
61: PetscHeaderDestroy(&obj);
62: return(0);
63: }
65: PETSC_NODISCARD PetscErrorCode reset(value_type &obj) const noexcept
66: {
70: this->destroy(obj);
71: this->create(&obj);
72: return(0);
73: }
75: PETSC_NODISCARD PetscErrorCode finalize(void) const noexcept { return 0;}
76: };
78: // Base class to object pool, defines helpful typedefs and stores the allocator instance
79: template <typename T, class _Allocator>
80: class ObjectPoolBase {
81: public:
82: typedef _Allocator allocator_t;
83: typedef typename allocator_t::value_type value_type;
85: protected:
86: allocator_t _alloc;
88: PETSC_NODISCARD allocator_t& __getAllocator() noexcept { return this->_alloc;}
90: PETSC_NODISCARD const allocator_t& __getAllocator() const noexcept { return this->_alloc;}
92: // default constructor
93: constexpr ObjectPoolBase() noexcept(std::is_nothrow_default_constructible<allocator_t>::value) : _alloc() {}
95: // const copy constructor
96: explicit ObjectPoolBase(const allocator_t &alloc) : _alloc{alloc} {}
98: // move constructor
99: explicit ObjectPoolBase(allocator_t &&alloc) noexcept(std::is_nothrow_move_assignable<allocator_t>::value) : _alloc{std::move(alloc)} {}
101: ~ObjectPoolBase()
102: {
103: PETSC_STATIC_ASSERT_BASE_CLASS(Allocator<value_type>,_Allocator,"Allocator type must be subclass of Petsc::Allocator");
104: }
105: };
107: // default implementation, use the petsc c allocator
108: template <typename T, class _Allocator = CAllocator<T>> class ObjectPool;
110: // multi-purpose basic object-pool, useful for recirculating old "destroyed" objects. Uses
111: // a stack to take advantage of LIFO for memory locallity. Registers all objects to be
112: // cleaned up on PetscFinalize()
113: template <typename T, class _Allocator>
114: class ObjectPool : ObjectPoolBase<T,_Allocator> {
115: protected:
116: typedef ObjectPoolBase<T,_Allocator> base_t;
118: public:
119: typedef typename base_t::allocator_t allocator_t;
120: typedef typename base_t::value_type value_type;
121: typedef std::stack<value_type> stack_type;
123: protected:
124: stack_type _stack;
125: PetscBool _registered{PETSC_FALSE};
127: private:
128: PETSC_NODISCARD static PetscErrorCode __staticFinalizer(void*) noexcept;
129: PETSC_NODISCARD PetscErrorCode __finalizer(void) noexcept;
130: PETSC_NODISCARD PetscErrorCode __registerFinalize(void) noexcept;
132: public:
133: // default constructor
134: constexpr ObjectPool() noexcept(std::is_nothrow_default_constructible<allocator_t>::value) {}
136: // copy constructor
137: ObjectPool(ObjectPool &other) noexcept(std::is_nothrow_copy_constructible<stack_type>::value) : _stack{other._stack},_registered{other._registered} {}
139: // const copy constructor
140: ObjectPool(const ObjectPool &other) noexcept(std::is_nothrow_copy_constructible<stack_type>::value) : _stack{other._stack},_registered{other._registered} {}
142: // move constructor
143: ObjectPool(ObjectPool &&other) noexcept(std::is_nothrow_move_constructible<stack_type>::value) : _stack{std::move(other._stack)},_registered{std::move(other._registered)} {}
145: // copy constructor with allocator
146: explicit ObjectPool(const allocator_t &alloc) : base_t{alloc},_registered{PETSC_FALSE} {}
148: // move constructor with allocator
149: explicit ObjectPool(allocator_t &&alloc) noexcept(std::is_nothrow_move_constructible<allocator_t>::value) : base_t{std::move(alloc)},_registered{PETSC_FALSE} {}
151: // Retrieve an object from the pool, if the pool is empty a new object is created instead
152: PETSC_NODISCARD PetscErrorCode get(value_type&) noexcept;
153: // Return an object to the pool, the object need not necessarily have been created by
154: // the pool
155: PETSC_NODISCARD PetscErrorCode reclaim(value_type&) noexcept;
156: PETSC_NODISCARD PetscErrorCode reclaim(value_type&&) noexcept;
158: // operators
159: template <typename T_, class A_>
160: PetscBool friend operator==(const ObjectPool<T_,A_>&,const ObjectPool<T_,A_>&);
162: template <typename T_, class A_>
163: PetscBool friend operator< (const ObjectPool<T_,A_>&,const ObjectPool<T_,A_>&);
164: };
166: template <typename T, class _Allocator>
167: inline PetscBool operator==(const ObjectPool<T,_Allocator> &l,const ObjectPool<T,_Allocator> &r)
168: {
169: return static_cast<PetscBool>(l._stack == r._stack);
170: }
172: template <typename T, class _Allocator>
173: inline PetscBool operator< (const ObjectPool<T,_Allocator> &l, const ObjectPool<T,_Allocator> &r)
174: {
175: return static_cast<PetscBool>(l._stack < r._stack);
176: }
178: template <typename T, class _Allocator>
179: inline PetscBool operator!=(const ObjectPool<T,_Allocator> &l, const ObjectPool<T,_Allocator> &r)
180: {
181: return !(l._stack == r._stack);
182: }
184: template <typename T, class _Allocator>
185: inline PetscBool operator> (const ObjectPool<T,_Allocator> &l, const ObjectPool<T,_Allocator> &r)
186: {
187: return r._stack < l._stack;
188: }
190: template <typename T, class _Allocator>
191: inline PetscBool operator>=(const ObjectPool<T,_Allocator> &l, const ObjectPool<T,_Allocator> &r)
192: {
193: return !(l._stack < r._stack);
194: }
196: template <typename T, class _Allocator>
197: inline PetscBool operator<=(const ObjectPool<T,_Allocator> &l, const ObjectPool<T,_Allocator> &r)
198: {
199: return !(r._stack < l._stack);
200: }
202: template <typename T, class _Allocator>
203: PetscErrorCode ObjectPool<T,_Allocator>::__staticFinalizer(void *obj) noexcept
204: {
208: static_cast<ObjectPool<T,_Allocator>*>(obj)->__finalizer();
209: return(0);
210: }
212: template <typename T, class _Allocator>
213: inline PetscErrorCode ObjectPool<T,_Allocator>::__finalizer(void) noexcept
214: {
218: while (!this->_stack.empty()) {
219: // we do CHKERRQ __after__ the CHKERCXX on the off chance that someone uses the CXX
220: // error handler, we don't want to catch our own exception!
221: CHKERRCXX(this->__getAllocator().destroy(this->_stack.top()));
222: CHKERRCXX(this->_stack.pop());
223: }
224: this->__getAllocator().finalize();
225: this->_registered = PETSC_FALSE;
226: return(0);
227: }
229: template <typename T, class _Allocator>
230: inline PetscErrorCode ObjectPool<T,_Allocator>::__registerFinalize(void) noexcept
231: {
233: if (PetscUnlikely(!this->_registered)) {
235: PetscContainer contain;
237: /* use a PetscContainer as a form of thunk, it holds not only a pointer to this but
238: also the pointer to the static member function, which just converts the thunk back
239: to this. none of this would be needed if PetscRegisterFinalize() just took a void*
240: itself though... */
241: PetscContainerCreate(PETSC_COMM_SELF,&contain);
242: PetscContainerSetPointer(contain,this);
243: PetscContainerSetUserDestroy(contain,this->__staticFinalizer);
244: PetscObjectRegisterDestroy((PetscObject)contain);
245: this->_registered = PETSC_TRUE;
246: }
247: return(0);
248: }
250: template <typename T, class _Allocator>
251: inline PetscErrorCode ObjectPool<T,_Allocator>::get(value_type &obj) noexcept
252: {
256: this->__registerFinalize();
257: if (this->_stack.empty()) {
258: this->__getAllocator().create(&obj);
259: } else {
260: CHKERRCXX(obj = std::move(this->_stack.top()));
261: CHKERRCXX(this->_stack.pop());
262: }
263: return(0);
264: }
266: template <typename T, class _Allocator>
267: inline PetscErrorCode ObjectPool<T,_Allocator>::reclaim(value_type &obj) noexcept
268: {
272: if (PetscUnlikely(!this->_registered)) {
273: /* this is necessary if an object is "reclaimed" within another PetscFinalize()
274: registered cleanup after this object pool has returned from it's finalizer. In
275: this case, instead of pushing onto the stack we just destroy the object directly */
276: this->__getAllocator().destroy(obj);
277: } else {
278: this->__getAllocator().reset(obj);
279: CHKERRCXX(this->_stack.push(std::move(obj)));
280: obj = nullptr;
281: }
282: return(0);
283: }
285: template <typename T, class _Allocator>
286: inline PetscErrorCode ObjectPool<T,_Allocator>::reclaim(value_type &&obj) noexcept
287: {
291: if (PetscUnlikely(!this->_registered)) {
292: /* this is necessary if an object is "reclaimed" within another PetscFinalize()
293: registered cleanup after this object pool has returned from it's finalizer. In
294: this case, instead of pushing onto the stack we just destroy the object directly */
295: this->__getAllocator().destroy(obj);
296: } else {
297: // allows const allocator_t& to be used if allocator defines a const reset
298: this->__getAllocator().reset(obj);
299: CHKERRCXX(this->_stack.push(std::move(obj)));
300: obj = nullptr;
301: }
302: return(0);
303: }
305: } // namespace Petsc
307: #endif /* __cplusplus */
309: #endif /* PETSCOBJECTPOOL_HPP */