diff options
Diffstat (limited to 'raul/List.hpp')
-rw-r--r-- | raul/List.hpp | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/raul/List.hpp b/raul/List.hpp new file mode 100644 index 0000000..d3f4707 --- /dev/null +++ b/raul/List.hpp @@ -0,0 +1,544 @@ +/* This file is part of Raul. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Raul is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Raul 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 details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RAUL_LIST_HPP +#define RAUL_LIST_HPP + +#include <cstddef> +#include <cassert> +#include <raul/Deletable.hpp> +#include <raul/AtomicPtr.hpp> +#include <raul/AtomicInt.hpp> + +namespace Raul { + + +/** A node in a List. + * + * This is exposed so the user can allocate ListNodes in different thread + * than the list reader, and insert (e.g. via an Event) it later in the + * reader thread. + */ +template <typename T> +class ListNode : public Raul::Deletable +{ +public: + ListNode(T elem) : _elem(elem) {} + virtual ~ListNode() {} + + ListNode* next() const { return _next.get(); } + void next(ListNode* ln) { _next = ln; } + ListNode* prev() const { return _prev.get(); } + void prev(ListNode* ln) { _prev = ln; } + T& elem() { return _elem;} + const T& elem() const { return _elem; } + +private: + T _elem; + AtomicPtr<ListNode> _next; + AtomicPtr<ListNode> _prev; +}; + + + +/** A realtime safe, (partially) thread safe doubly-linked list. + * + * Elements can be added safely while another thread is reading the list. + * Like a typical ringbuffer, this is single-reader single-writer threadsafe + * only. See documentation for specific functions for specifics. + */ +template <typename T> +class List : public Raul::Deletable +{ +public: + List() : _size(0), _end_iter(this), _const_end_iter(this) + { + _end_iter._listnode = NULL; + _const_end_iter._listnode = NULL; + } + ~List(); + + void push_back(ListNode<T>* elem); // realtime safe + void push_back(T& elem); // NOT realtime safe + + void append(List<T>& list); + + void clear(); + + /// Valid only in the write thread + unsigned size() const { return (unsigned)_size.get(); } + + /// Valid for any thread + bool empty() { return (_head.get() == NULL); } + + class iterator; + + /** Realtime safe const iterator for a List. */ + class const_iterator + { + public: + const_iterator(const List<T>* const list); + const_iterator(const iterator& i) + : _list(i._list), _listnode(i._listnode) {} + + inline const T& operator*(); + inline const_iterator& operator++(); + inline bool operator!=(const const_iterator& iter) const; + inline bool operator!=(const iterator& iter) const; + inline bool operator==(const const_iterator& iter) const; + inline bool operator==(const iterator& iter) const; + + friend class List<T>; + + private: + const List<T>* const _list; + const ListNode<T>* _listnode; + }; + + + /** Realtime safe iterator for a List. */ + class iterator + { + public: + iterator(List<T>* const list); + + inline T& operator*(); + inline iterator& operator++(); + inline bool operator!=(const iterator& iter) const; + inline bool operator!=(const const_iterator& iter) const; + inline bool operator==(const iterator& iter) const; + inline bool operator==(const const_iterator& iter) const; + + friend class List<T>; + friend class List<T>::const_iterator; + + private: + const List<T>* _list; + ListNode<T>* _listnode; + }; + + + ListNode<T>* erase(const iterator iter); + + iterator find(const T& val); + + iterator begin(); + const iterator end() const; + + const_iterator begin() const; + //const_iterator end() const; + +private: + AtomicPtr<ListNode<T> > _head; + AtomicPtr<ListNode<T> > _tail; ///< writer only + AtomicInt _size; + iterator _end_iter; + const_iterator _const_end_iter; +}; + + + + +template <typename T> +List<T>::~List<T>() +{ + clear(); +} + + +/** Clear the list, deleting all ListNodes contained (but NOT their contents!) + * + * Not realtime safe. + */ +template <typename T> +void +List<T>::clear() +{ + ListNode<T>* node = _head.get(); + ListNode<T>* next = NULL; + + while (node) { + next = node->next(); + delete node; + node = next; + } + + _head = 0; + _tail = 0; + _size = 0; +} + + +/** Add an element to the list. + * + * Thread safe (may be called while another thread is reading the list). + * Realtime safe. + */ +template <typename T> +void +List<T>::push_back(ListNode<T>* const ln) +{ + assert(ln); + + ln->next(NULL); + + if ( ! _head.get()) { // empty + ln->prev(NULL); + _tail = ln; + _head = ln; + } else { + ln->prev(_tail.get()); + _tail.get()->next(ln); + _tail = ln; + } + ++_size; +} + + +/** Add an element to the list. + * + * Thread safe (may be called while another thread is reading the list). + * NOT realtime safe (a ListNode is allocated). + */ +template <typename T> +void +List<T>::push_back(T& elem) +{ + ListNode<T>* const ln = new ListNode<T>(elem); + + assert(ln); + + ln->next(NULL); + + if ( ! _head.get()) { // empty + ln->prev(NULL); + _tail = ln; + _head = ln; + } else { + ln->prev(_tail.get()); + _tail.get()->next(ln); + _tail = ln; + } + ++_size; +} + + +/** Append a list to this list. + * + * This operation is fast ( O(1) ). + * The appended list is not safe to use concurrently with this call. + * + * The appended list will be empty after this call. + * + * Thread safe (may be called while another thread is reading the list). + * Realtime safe. + */ +template <typename T> +void +List<T>::append(List<T>& list) +{ + ListNode<T>* const my_head = _head.get(); + ListNode<T>* const my_tail = _tail.get(); + ListNode<T>* const other_head = list._head.get(); + ListNode<T>* const other_tail = list._tail.get(); + + // Appending to an empty list + if (my_head == NULL && my_tail == NULL) { + _head = other_head; + _tail = other_tail; + _size = list._size; + } else if (other_head != NULL && other_tail != NULL) { + + other_head->prev(my_tail); + + // FIXME: atomicity an issue? _size < true size is probably fine... + // no gurantee an iteration runs exactly size times though. verify/document this. + // assuming comment above that says tail is writer only, this is fine + my_tail->next(other_head); + _tail = other_tail; + _size += list.size(); + } + + list._head = NULL; + list._tail = NULL; + list._size = 0; +} + + +/** Remove all elements equal to @val from the list. + * + * This function is realtime safe - it is the caller's responsibility to + * delete the returned ListNode, or there will be a leak. + */ +#if 0 +template <typename T> +ListNode<T>* +List<T>::remove(const T val) +{ + // FIXME: atomicity? + ListNode<T>* n = _head; + while (n) { + if (n->elem() == elem) + break; + n = n->next(); + } + if (n) { + if (n == _head) _head = _head->next(); + if (n == _tail) _tail = _tail->prev(); + if (n->prev()) + n->prev()->next(n->next()); + if (n->next()) + n->next()->prev(n->prev()); + --_size; + + if (_size == 0) + _head = _tail = NULL; // FIXME: Shouldn't be necessary + + return n; + } + return NULL; +} +#endif + + +/** Find an element in the list. + * + * This will only return the first element found. If there are duplicated, + * another call to find() will return the next, etc. + */ +template <typename T> +typename List<T>::iterator +List<T>::find(const T& val) +{ + for (iterator i = begin(); i != end(); ++i) + if (*i == val) + return i; + + return end(); +} + + +/** Remove an element from the list using an iterator. + * + * This function is realtime safe - it is the caller's responsibility to + * delete the returned ListNode, or there will be a leak. + * Thread safe (safe to call while another thread reads the list). + * @a iter is invalid immediately following this call. + */ +template <typename T> +ListNode<T>* +List<T>::erase(const iterator iter) +{ + ListNode<T>* const n = iter._listnode; + + if (n) { + + ListNode<T>* const prev = n->prev(); + ListNode<T>* const next = n->next(); + + // Removing the head (or the only element) + if (n == _head.get()) + _head = next; + + // Removing the tail (or the only element) + if (n == _tail.get()) + _tail = _tail.get()->prev(); + + if (prev) + n->prev()->next(next); + + if (next) + n->next()->prev(prev); + + --_size; + } + + return n; +} + + +//// Iterator stuff //// + +template <typename T> +List<T>::iterator::iterator(List<T>* list) +: _list(list), + _listnode(NULL) +{ +} + + +template <typename T> +T& +List<T>::iterator::operator*() +{ + assert(_listnode); + return _listnode->elem(); +} + + +template <typename T> +inline typename List<T>::iterator& +List<T>::iterator::operator++() +{ + assert(_listnode); + _listnode = _listnode->next(); + + return *this; +} + + +template <typename T> +inline bool +List<T>::iterator::operator!=(const iterator& iter) const +{ + return (_listnode != iter._listnode); +} + + +template <typename T> +inline bool +List<T>::iterator::operator!=(const const_iterator& iter) const +{ + return (_listnode != iter._listnode); +} + + +template <typename T> +inline bool +List<T>::iterator::operator==(const iterator& iter) const +{ + return (_listnode == iter._listnode); +} + + +template <typename T> +inline bool +List<T>::iterator::operator==(const const_iterator& iter) const +{ + return (_listnode == iter._listnode); +} + + +template <typename T> +inline typename List<T>::iterator +List<T>::begin() +{ + typename List<T>::iterator iter(this); + + iter._listnode = _head.get(); + + return iter; +} + + +template <typename T> +inline const typename List<T>::iterator +List<T>::end() const +{ + return _end_iter; +} + + + +/// const_iterator stuff /// + + +template <typename T> +List<T>::const_iterator::const_iterator(const List<T>* const list) +: _list(list), + _listnode(NULL) +{ +} + + +template <typename T> +const T& +List<T>::const_iterator::operator*() +{ + assert(_listnode); + return _listnode->elem(); +} + + +template <typename T> +inline typename List<T>::const_iterator& +List<T>::const_iterator::operator++() +{ + assert(_listnode); + _listnode = _listnode->next(); + + return *this; +} + + +template <typename T> +inline bool +List<T>::const_iterator::operator!=(const const_iterator& iter) const +{ + return (_listnode != iter._listnode); +} + + +template <typename T> +inline bool +List<T>::const_iterator::operator!=(const iterator& iter) const +{ + return (_listnode != iter._listnode); +} + + +template <typename T> +inline bool +List<T>::const_iterator::operator==(const const_iterator& iter) const +{ + return (_listnode == iter._listnode); +} + + +template <typename T> +inline bool +List<T>::const_iterator::operator==(const iterator& iter) const +{ + return (_listnode == iter._listnode); +} + +template <typename T> +inline typename List<T>::const_iterator +List<T>::begin() const +{ + typename List<T>::const_iterator iter(this); + iter._listnode = _head.get(); + return iter; +} + +#if 0 +template <typename T> +inline typename List<T>::const_iterator +List<T>::end() const +{ + /*typename List<T>::const_iterator iter(this); + iter._listnode = NULL; + iter._next = NULL; + return iter;*/ + return _const_end_iter; +} +#endif + + +} // namespace Raul + +#endif // RAUL_LIST_HPP |