diff --git a/public/ReentrantList.h b/public/ReentrantList.h new file mode 100644 index 00000000..f9b05e67 --- /dev/null +++ b/public/ReentrantList.h @@ -0,0 +1,162 @@ +// vim: set ts=4 sw=4 tw=99 noet : +// ============================================================================= +// SourceMod +// Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. +// ============================================================================= +// +// This program is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License, version 3.0, as published by the +// Free Software Foundation. +// +// This program 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 GNU General Public License for more +// details. +// +// You should have received a copy of the GNU General Public License along with +// this program. If not, see . +// +// As a special exception, AlliedModders LLC gives you permission to link the +// code of this program (as well as its derivative works) to "Half-Life 2," the +// "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software +// by the Valve Corporation. You must obey the GNU General Public License in +// all respects for all other code used. Additionally, AlliedModders LLC grants +// this exception to all derivative works. AlliedModders LLC defines further +// exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), +// or . +#ifndef _include_sourcemod_reentrant_iterator_h_ +#define _include_sourcemod_reentrant_iterator_h_ + +#include +#include + +namespace SourceMod { + +// ReentrantList is a wrapper around a LinkedList, with special attention twoard +// reentrancy. The list may be mutated during iteration as long as its iterator +// protocol is obeyed: iterators may only be used on the stack in a LIFO manner, +// and it is illegal to request access to an iterator's item after the list has +// been mutated. +// +// We guard against this using assertions. If an item in a list is removed, and +// this affects any active iterators, those iterators cannot be used until their +// next() function is called. +template +class ReentrantList : public ke::LinkedList +{ + typedef typename ke::LinkedList BaseType; + typedef typename ke::LinkedList::iterator BaseTypeIter; + +public: + ReentrantList(AllocPolicy ap = AllocPolicy()) + : ke::LinkedList(ap), + top_(nullptr) + { + } + + // Begin and end are disallowed here. + void begin() = delete; + void end() = delete; + + class iterator + { + public: + iterator(ReentrantList& list) + : iterator(&list) + {} + iterator(ReentrantList* list) + : list_(list), + prev_(list_->top_), + impl_(list->impl_begin()), + mutated_(false) + { + list_->top_ = this; + } + ~iterator() { + assert(list_->top_ == this); + list_->top_ = prev_; + } + + // Returns true if the underlying iterator mutated during iteration. + bool mutated() const { + return mutated_; + } + bool more() { + return impl_ != list_->impl_end(); + } + bool done() { + return impl_ == list_->impl_end(); + } + + // Accessors. It is illegal to access the current item if the list has + // mutated. + const T& operator *() const { + assert(!mutated()); + return *impl_; + } + T& operator *() { + assert(!mutated()); + return *impl_; + } + T* operator ->() { + assert(!mutated()); + return &*impl_; + } + const T* operator ->() const { + assert(!mutated()); + return &*impl_; + } + + void next() { + if (mutated_) { + mutated_ = false; + return; + } + impl_++; + } + + void remove() { + BaseTypeIter old_impl = impl_; + + impl_ = list_->erase(impl_); + mutated_ = true; + for (iterator* p = prev_; p; p = p->prev_) { + if (p->impl_ == old_impl) { + p->impl_ = impl_; + p->mutated_ = true; + } + } + } + + private: + ReentrantList* list_; + iterator* prev_; + BaseTypeIter impl_; + bool mutated_; + }; + + // Same protocol as LinkedList::remove, but we use our own iterator. + void remove(const T& obj) { + for (iterator iter(this); !iter.done(); iter.next()) { + if (*iter == obj) { + iter.remove(); + break; + } + } + } + +private: + BaseTypeIter impl_begin() { + return BaseType::begin(); + } + BaseTypeIter impl_end() { + return BaseType::end(); + } + +private: + iterator* top_; +}; + +} // namespace SourceMod + +#endif // _include_sourcemod_reentrant_iterator_h_