#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_GRAPHICS_HELPERS_CONCURRENTOBSERVERSET_H #define NUCLEX_GRAPHICS_HELPERS_CONCURRENTOBSERVERSET_H #include #include namespace Nuclex { namespace Graphics { namespace Helpers { // ------------------------------------------------------------------------------------------- // /// Vector that can safely be accessed from multiple threads /// /// /// Destruction is not thread-safe. Use reference counting or another method to ensure /// the vector is no longer accessed during destruction. /// /// /// Currently uses mutexes and locks. Replace this with a lock-free implementation. /// It needs a Remove() method, however, for which I haven't figured out a safe /// algorithm yet that would work in multiple reader, multiple writer situations. /// /// template class ConcurrentObserverSet { #pragma region struct AttachedObserver /// /// Stores informations about an observer attached to the graphics resource /// private: struct AttachedObserver { /// Creates an uninitialized observer public: AttachedObserver() {} /// Initializes a new attached observer container /// Rasterizer of related class owning the observer /// Observer that will watch the graphics resource public: AttachedObserver(const void *owner, TObserver *observer) : Owner(owner), Observer(observer) {} /// Rasterizer or related class that owns the resource /// /// This only acts as a key (since C++ does not do heap compaction, using the /// address of the class that manages graphics resources in the rasterizer ensrures /// that no other rasterizer active in the same process can have the same key) /// public: const void *Owner; /// Observer that is attached to the resource public: TObserver *Observer; }; #pragma endregion // struct AttachedObserver /// Initializes a new thread-safe vector public: ConcurrentObserverSet() {} /// Frees all memory owned by the thread-safe vector public: ~ConcurrentObserverSet() {} /// Attaches an observer /// Owner the observer will belong to /// Observer that will be attached public: void Attach(const void *owner, TObserver *observer) { std::lock_guard guard(this->mutex); this->observers.push_back(AttachedObserver(owner, observer)); } /// Attaches an observer, ensuring it only appears once /// Owner the observer will belong to /// Observer that will be attached /// True if the observer was attached, false if it already existed public: bool AttachUnique(const void *owner, TObserver *observer) { std::lock_guard guard(this->mutex); for(std::size_t index = 0; index < this->observers.size(); ++index) { if(this->observers[index].Owner == owner) { return false; } } this->observers.push_back(AttachedObserver(owner, observer)); return true; } /// Removes an observer from the vector /// Owner whose observer will be removed /// True if the observer was found and removed, false otherwise public: bool Detach(const void *owner) { std::lock_guard guard(this->mutex); std::size_t observerCount = this->observers.size(); for(std::size_t index = 0; index < observerCount; ++index) { if(this->observers[index].Owner == owner) { if(observerCount == 1) { this->observers.clear(); } else { --observerCount; if(index != observerCount) { std::swap(this->observers[index], this->observers[observerCount]); } this->observers.resize(observerCount); } return true; } } return false; } /// Looks up the observer belonging to the specified owner /// Owner whose observer will be looked up /// The observer attached by the specified owner public: TObserver *Get(const void *owner) const { std::lock_guard guard(this->mutex); for(std::size_t index = 0; index < this->observers.size(); ++index) { if(this->observers[index].Owner == owner) { return this->observers[index].Observer; } } return nullptr; } /// Creates a thread-unsafe copy of the attached observer list /// A thread-unsafe copy of the attached observers public: std::vector Copy() const { std::lock_guard guard(this->mutex); std::size_t observerCount = this->observers.size(); std::vector copy(observerCount); for(std::size_t index = 0; index < observerCount; ++index) { copy[index] = this->observers[index].Observer; } return copy; } private: ConcurrentObserverSet(const ConcurrentObserverSet &); private: ConcurrentObserverSet &operator =(const ConcurrentObserverSet &); /// Mutex used to synchronize accesses to the vector private: mutable std::mutex mutex; /// Holds the observers attached to the graphics resource private: std::vector observers; }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Graphics::Helpers #endif // NUCLEX_GRAPHICS_HELPERS_CONCURRENTOBSERVERSET_H