#include #include #include #include "../shared/MiscSupport.h" #include "../shared/ThreadSafeRefCount.h" class Row { // All read only public: typedef std::basic_string< unsigned char > Body; private: const Body _body; public: Row(Body body) : _body(body) { } Row(Row const &other) : _body(other._body) { } // Export row data to give to MySQL. unsigned char const *copyData() const { return _body.c_str(); } }; typedef TSRefCount RowRef; class LinkedListNode : NoCopy, NoAssign { public: typedef TSRefCount< LinkedListNode > Ref; typedef NCTSRefCount< LinkedListNode > MRef; enum Position { First, Last, Data }; private: mutable pthread_mutex_t _mutex; // In order to modify these items you must hold the mutex for this object and // the mutex for the index. In order to read these you must hold the mutex // for this object or the mutex for the index. If someone calls a non-const // function on this object, assume they are already holding the mutex for the // index. The constructor and destructor for the index don't actually need // to hold the mutex. Assume no one else is accessing the data at that time. MRef _previous; MRef _next; RowRef _row; const Position _position; LinkedListNode(Position position) : _position(position) { pthread_mutex_init(&_mutex, NULL); } public: bool isFirst() const { return _position == First; } bool isLast() const { return _position == Last; } bool isData() const { return _position == Data; } Ref getPrevious() const { // If you read off the end you will receive a special node marked First. pthread_mutex_lock(&_mutex); Ref result = _previous; pthread_mutex_unlock(&_mutex); return result; } Ref getNext() const { // If you read off the end you will receive a special node marked Last. pthread_mutex_lock(&_mutex); Ref result = _next; pthread_mutex_unlock(&_mutex); return result; } RowRef getRow() const { // If this data has been deleted you will receive NULL. pthread_mutex_lock(&_mutex); RowRef result = _row; pthread_mutex_unlock(&_mutex); return result; } void remove() { pthread_mutex_lock(&_mutex); if (isData()) { _row = NULL; _previous->_next = _next; _next->_previous = _previous; } else { _previous = NULL; _next = NULL; } pthread_mutex_unlock(&_mutex); } void setRow(RowRef const &row) { assert(isData()); pthread_mutex_lock(&_mutex); _row = row; pthread_mutex_unlock(&_mutex); } static void createBeginEnd(Ref &begin, Ref &end) { const MRef b = new LinkedListNode(First); const MRef e = new LinkedListNode(Last); b->_previous = b; b->_next = e; e->_previous = b; e->_next = e; begin = b; end = e; } static MRef create(RowRef const &row, MRef const &insertBefore) { MRef newItem = new LinkedListNode(Data); newItem->_previous = insertBefore->_previous; newItem->_next = insertBefore; newItem->_row = row; // memory barrier insertBefore->_previous->_next = newItem; insertBefore->_previous = newItem; return newItem; } ~LinkedListNode() { pthread_mutex_destroy(&_mutex); } }; template < class T > class UniqueIndex { private: mutable pthread_mutex_t _mutex; typedef std::map< T, LinkedListNode::MRef > Map; Map _map; LinkedListNode::Ref _begin; LinkedListNode::Ref _end; // Caller should hold mutex. // The result will point to the item, if the item already exists. // Otherwise, the result will point to the item that should come right after // the specified item. typename Map::const_iterator getAtOrAfter(T const &key) const { return _map.lower_bound(key); } public: LinkedListNode::Ref get(T const &key) const { // Returns NULL if not found. pthread_mutex_lock(&_mutex); LinkedListNode::Ref result = getProperty(_map, key); pthread_mutex_unlock(&_mutex); return result; } LinkedListNode::Ref getFirst() const { // Returns _last if the list is empty. pthread_mutex_lock(&_mutex); LinkedListNode::Ref result = _begin->getNext(); pthread_mutex_unlock(&_mutex); return result; } LinkedListNode::Ref getLast() const { // Returns _first if the list is empty. pthread_mutex_lock(&_mutex); LinkedListNode::Ref result = _end->getPrevious(); pthread_mutex_unlock(&_mutex); return result; } void remove(T const &key) { // Does nothing if not found pthread_mutex_lock(&_mutex); typename Map::iterator it = _map.find(key); if (it != _map.end()) { (*it)->remove(); _map.remove(it); } pthread_mutex_unlock(&_mutex); } void replace(T key, RowRef row) { // If the key does not exist, create it. Otherwise, update it. LinkedListNode::MRef current, after; pthread_mutex_lock(&_mutex); typename Map::const_iterator it = getAtOrAfter(key); if (it == _map.end()) { // New last item. after = _end; } else if (it-> first == key) { // Found item. current = it->second; } else { // Not found, not last. after = it->second; } if (current) current->setRow(row); else { current = LinkedListNode::create(row, after); _map[key] = current; } pthread_mutex_unlock(&_mutex); } UniqueIndex() { pthread_mutex_init(&_mutex, NULL); LinkedListNode::createBeginEnd(_begin, _end); } ~UniqueIndex() { pthread_mutex_destroy(&_mutex); for (typename Map::iterator it = _map.begin(); it != _map.end(); it++) it->second->remove(); _begin->remove(); _end->remove(); } };