Add a ReentrantList class to abstract list mutation during iteration.
This commit is contained in:
		
							parent
							
								
									c1396de2fc
								
							
						
					
					
						commit
						d0843ab997
					
				
							
								
								
									
										162
									
								
								public/ReentrantList.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								public/ReentrantList.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 <http://www.gnu.org/licenses/>.
 | ||||||
|  | //
 | ||||||
|  | // 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 <http://www.sourcemod.net/license.php>.
 | ||||||
|  | #ifndef _include_sourcemod_reentrant_iterator_h_ | ||||||
|  | #define _include_sourcemod_reentrant_iterator_h_ | ||||||
|  | 
 | ||||||
|  | #include <amtl/am-linkedlist.h> | ||||||
|  | #include <amtl/am-function.h> | ||||||
|  | 
 | ||||||
|  | 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 <typename T, class AllocPolicy = ke::SystemAllocatorPolicy> | ||||||
|  | class ReentrantList : public ke::LinkedList<T, AllocPolicy> | ||||||
|  | { | ||||||
|  | 	typedef typename ke::LinkedList<T> BaseType; | ||||||
|  | 	typedef typename ke::LinkedList<T>::iterator BaseTypeIter; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	ReentrantList(AllocPolicy ap = AllocPolicy()) | ||||||
|  | 		: ke::LinkedList<T, AllocPolicy>(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_
 | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user