| /* |
| * MVKObjectPool.h |
| * |
| * Copyright (c) 2014-2018 The Brenwill Workshop Ltd. (http://www.brenwill.com) |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| #pragma once |
| |
| #include "MVKBaseObject.h" |
| #include <mutex> |
| |
| |
| #pragma mark - |
| #pragma mark MVKObjectPool |
| |
| /** |
| * Manages a pool of instances of a particular object type. |
| * |
| * The objects managed by this pool must have public member variable named "_next", |
| * of the same object type, which is used by this pool to create a linked list of objects. |
| * |
| * When this pool is destroyed, any objects contained in the pool are also destroyed. |
| * |
| * This pool includes member functions for managing resources in either a thread-safe, |
| * or somewhat faster, but not-thread-safe manner. |
| * |
| * An instance of this pool can be configured to either manage a pool of objects, |
| * or simply allocate a new object instance on each request and destroy the object |
| * when it is released back to the pool. |
| */ |
| template <class T> |
| class MVKObjectPool : public MVKBaseObject { |
| |
| public: |
| |
| /** |
| * Acquires and returns the next available object from the pool, creating it if necessary. |
| * |
| * If this instance was configured to use pooling, the object is removed from the pool |
| * until it is returned back to the pool. If this instance was configured NOT to use |
| * pooling, the object is created anew on each request, and will be deleted when |
| * returned back to the pool. |
| * |
| * This method is not thread-safe. For a particular pool instance, all calls to |
| * aquireObject() and returnObject() must be made from the same thread. |
| */ |
| T* acquireObject() { |
| T* obj = VK_NULL_HANDLE; |
| if (_isPooling) { obj = nextObject(); } |
| if ( !obj ) { obj = newObject(); } |
| |
| return obj; |
| } |
| |
| /** |
| * Returns the specified object back to the pool. |
| * |
| * If this instance was configured to use pooling, the returned object is added back |
| * into the pool. If this instance was configured NOT to use pooling, the returned |
| * object is simply deleted. |
| * |
| * This method is not thread-safe. For a particular pool instance, all calls to |
| * aquireObject() and returnObject() must be made from the same thread. |
| */ |
| void returnObject(T* obj) { |
| if (_isPooling) { |
| if (_tail) { _tail->_next = obj; } |
| obj->_next = VK_NULL_HANDLE; |
| _tail = obj; |
| if ( !_head ) { _head = obj; } |
| } else { |
| obj->destroy(); |
| } |
| } |
| |
| /** A thread-safe version of the acquireObject() function. */ |
| T* acquireObjectSafely() { |
| std::lock_guard<std::mutex> lock(_lock); |
| return acquireObject(); |
| } |
| |
| /** A thread-safe version of the returnObject() function. */ |
| void returnObjectSafely(T* obj) { |
| std::lock_guard<std::mutex> lock(_lock); |
| returnObject(obj); |
| } |
| |
| /** Clears all the objects from this pool, deleting each one. This method is thread-safe. */ |
| void clear() { |
| std::lock_guard<std::mutex> lock(_lock); |
| while ( T* obj = nextObject() ) { obj->destroy(); } |
| } |
| |
| /** |
| * Configures this instance to either use pooling, or not, depending on the |
| * value of isPooling, which defaults to true if not indicated explicitly. |
| */ |
| MVKObjectPool(bool isPooling = true) : _isPooling(isPooling) {} |
| |
| ~MVKObjectPool() override { clear(); } |
| |
| protected: |
| |
| /** |
| * Removes and returns the first object in this pool, or returns null if this pool |
| * contains no objects. This differs from the acquireObject() function, which creates |
| * and return a new instance if this pool is empty. This method is not thread-safe. |
| */ |
| T* nextObject() { |
| T* obj = _head; |
| if (obj) { |
| _head = (T*)obj->_next; // Will be null for last object in pool |
| if ( !_head ) { _tail = VK_NULL_HANDLE; } // If last, also clear tail |
| obj->_next = VK_NULL_HANDLE; // Objects in the wild should never think they are still part of this pool |
| } |
| return obj; |
| } |
| |
| /** Returns a new instance of the type of object managed by this pool. */ |
| virtual T* newObject() = 0; |
| |
| std::mutex _lock; |
| T* _head = nullptr; |
| T* _tail = nullptr; |
| bool _isPooling; |
| }; |
| |