qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From acon...@apache.org
Subject svn commit: r590907 - in /incubator/qpid/trunk/qpid/cpp/src: Makefile.am qpid/RefCounted.h qpid/sys/RefCountedMap.h tests/Makefile.am tests/RefCountedMap.cpp
Date Thu, 01 Nov 2007 05:58:51 GMT
Author: aconway
Date: Wed Oct 31 22:58:50 2007
New Revision: 590907

URL: http://svn.apache.org/viewvc?rev=590907&view=rev
Log:

Added qpid::sys::RefCountedMap: thread safe refcounted map of refcounted entries.
 - Entries are atomically erased when released.
 - Map is released when all entries are erased.

Added:
    incubator/qpid/trunk/qpid/cpp/src/qpid/sys/RefCountedMap.h   (with props)
    incubator/qpid/trunk/qpid/cpp/src/tests/RefCountedMap.cpp   (with props)
Modified:
    incubator/qpid/trunk/qpid/cpp/src/Makefile.am
    incubator/qpid/trunk/qpid/cpp/src/qpid/RefCounted.h
    incubator/qpid/trunk/qpid/cpp/src/tests/Makefile.am

Modified: incubator/qpid/trunk/qpid/cpp/src/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/Makefile.am?rev=590907&r1=590906&r2=590907&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/Makefile.am (original)
+++ incubator/qpid/trunk/qpid/cpp/src/Makefile.am Wed Oct 31 22:58:50 2007
@@ -127,6 +127,7 @@
   qpid/sys/AsynchIOAcceptor.cpp \
   qpid/sys/Dispatcher.cpp \
   qpid/sys/Runnable.cpp \
+  qpid/sys/RefCountedMap.h \
   qpid/sys/Serializer.cpp \
   qpid/sys/Shlib.cpp \
   qpid/Options.cpp \

Modified: incubator/qpid/trunk/qpid/cpp/src/qpid/RefCounted.h
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/RefCounted.h?rev=590907&r1=590906&r2=590907&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/RefCounted.h (original)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/RefCounted.h Wed Oct 31 22:58:50 2007
@@ -65,8 +65,8 @@
 
 // intrusive_ptr support.
 namespace boost {
-void intrusive_ptr_add_ref(const qpid::AbstractRefCounted* p) { p->addRef(); }
-void intrusive_ptr_release(const qpid::AbstractRefCounted* p) { p->release(); }
+inline void intrusive_ptr_add_ref(const qpid::AbstractRefCounted* p) { p->addRef(); }
+inline void intrusive_ptr_release(const qpid::AbstractRefCounted* p) { p->release(); }
 }
 
 

Added: incubator/qpid/trunk/qpid/cpp/src/qpid/sys/RefCountedMap.h
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/sys/RefCountedMap.h?rev=590907&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/sys/RefCountedMap.h (added)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/sys/RefCountedMap.h Wed Oct 31 22:58:50 2007
@@ -0,0 +1,171 @@
+#ifndef QPID_SYS_REFCOUNTEDMAP_H
+#define QPID_SYS_REFCOUNTEDMAP_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/Mutex.h"
+#include "qpid/RefCounted.h"
+
+#include <boost/call_traits.hpp>
+#include <boost/iterator/iterator_facade.hpp>
+
+#include <map>
+
+namespace qpid {
+namespace sys {
+
+/**
+ * A thread-safe, RefCounted map of RefCounted entries.  Entries are
+ * automatically erased when all iterators to them are destroyed.  The
+ * entire map is released when all its entries are erased.
+ *
+ * The assumption is that some other object/thread holds an iterator
+ * to each entry for as long as it is useful.
+ *
+ * API is a subset of std::map
+ *
+ * WARNING: Modifying iterators locks the map.  To avoid deadlock, you
+ * MUST NOT modify an iterator while holding another lock that could be
+ * locked as a result of erasing the entry and destroying its value.
+ *
+ */
+
+template <class K, class D>
+class RefCountedMap : public RefCounted
+{
+    typedef Mutex::ScopedLock Lock;
+
+  public:
+    typedef K key_type;
+    typedef D data_type;
+    typedef std::pair<key_type,data_type> value_type;
+
+    /** Bidirectional iterator maintains a reference count on the map entry.
+     * Provides operator!() and operator bool() to test for end() iterator.
+     */
+    class iterator : 
+        public boost::iterator_facade<iterator, value_type,
+                                      boost::bidirectional_traversal_tag>
+    {
+      public:
+        iterator() {}
+        bool operator!() const { return !ptr; }
+        operator bool() const { return ptr; }
+        void reset() { ptr=0; }
+
+      private:
+        typedef typename RefCountedMap::Entry Entry;
+
+        iterator(intrusive_ptr<Entry> entry) : ptr(entry) {}
+
+        // boost::iterator_facade functions.
+        value_type& dereference() const { return ptr->value; }
+        bool equal(iterator j) const { return ptr==j.ptr; }
+        void increment() { assert(ptr); *this=ptr->map->next(ptr->self); }
+        void decrement() { assert(ptr); *this=ptr->map->prev(ptr->self); }
+
+        intrusive_ptr<Entry> ptr;
+
+      friend class boost::iterator_core_access;
+      friend class RefCountedMap<K,D>;
+    };
+
+    iterator begin() { Lock l(lock); return makeIterator(map.begin()); }
+
+    iterator end() { Lock l(lock); return makeIterator(map.end()); }
+
+    size_t size() { Lock l(lock); return map.size(); }
+
+    bool empty() { return size() == 0u; }
+    
+    iterator find(const key_type& key) {
+        Lock l(lock); return makeIterator(map.find(key));
+    }
+
+    std::pair<iterator, bool> insert(const value_type& x) {
+        Lock l(lock);
+        std::pair<typename Map::iterator,bool> ib=
+            map.insert(make_pair(x.first, Entry(x, this)));
+        ib.first->second.self = ib.first;
+        return std::make_pair(makeIterator(ib.first), ib.second);
+    }
+
+  private:
+
+    //
+    // INVARIANT:
+    //  - All entries in the map have non-0 refcounts.
+    //  - Each entry holds an intrusive_ptr to the map 
+    //
+
+    struct Entry : public RefCounted {
+        typedef typename RefCountedMap::Map::iterator Iterator;
+
+        Entry(const value_type& v, RefCountedMap* m) : value(v), map(m) {}
+        
+        value_type value;
+        intrusive_ptr<RefCountedMap> map;
+        Iterator self;
+
+        // RefCounts are modified with map locked. 
+        struct MapLock : public Lock {
+            MapLock(RefCountedMap& m) : Lock(m.lock) {}
+        };
+
+        void released() const {
+            intrusive_ptr<RefCountedMap> protect(map);
+            map->map.erase(self);
+        }
+    };
+
+    typedef std::map<K,Entry> Map;
+
+    iterator makeIterator(typename Map::iterator i) {
+        // Call with lock held.
+        return iterator(i==map.end() ? 0 : &i->second);
+    }
+
+     void erase(typename RefCountedMap::Map::iterator i) {
+        // Make sure this is not deleted till lock is released.
+        intrusive_ptr<RefCountedMap> self(this);
+        { Lock l(lock); map.erase(i); }
+    }
+
+    iterator next(typename RefCountedMap::Map::iterator i) {
+        { Lock l(lock); return makeIterator(++i); }
+    }
+
+    iterator prev(typename RefCountedMap::Map::iterator i) {
+        { Lock l(lock); return makeIterator(--i); }
+    }
+
+    Mutex lock;
+    Map map;
+
+  friend struct Entry;
+  friend class iterator;
+};
+
+
+}} // namespace qpid::sys
+
+#endif  /*!QPID_SYS_REFCOUNTEDMAP_H*/

Propchange: incubator/qpid/trunk/qpid/cpp/src/qpid/sys/RefCountedMap.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/trunk/qpid/cpp/src/qpid/sys/RefCountedMap.h
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: incubator/qpid/trunk/qpid/cpp/src/tests/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/tests/Makefile.am?rev=590907&r1=590906&r2=590907&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/tests/Makefile.am (original)
+++ incubator/qpid/trunk/qpid/cpp/src/tests/Makefile.am Wed Oct 31 22:58:50 2007
@@ -21,14 +21,16 @@
 #
 # Unit tests are built as a single program to reduce valgrind overhead
 # when running the tests. If you want to build a subset of the tests do 
-#   rm unit_test; make unit_test unit_test_SOURCES="SelectedTest.cpp"
+#   rm -f unit_test; make unit_test unit_test_OBJECTS="unit_test.o SelectedTest.o"
 # 
 
 TESTS+=unit_test
 check_PROGRAMS+=unit_test
 unit_test_LDADD=-lboost_unit_test_framework -lboost_regex $(lib_common)
 unit_test_SOURCES= unit_test.cpp \
-	RefCounted.cpp SessionState.cpp Blob.cpp logging.cpp Url.cpp Uuid.cpp \
+	RefCounted.cpp RefCountedMap.cpp \
+	SessionState.cpp Blob.cpp logging.cpp \
+	Url.cpp Uuid.cpp \
 	Shlib.cpp FieldValue.cpp FieldTable.cpp
 
 check_LTLIBRARIES += libshlibtest.la
@@ -96,7 +98,7 @@
 
 check_PROGRAMS += $(testprogs) interop_runner
 
-TESTS_ENVIRONMENT = VALGRIND=$(VALGRIND) srcdir=$(srcdir) BOOST_TEST_SHOW_PROGRESS=yes $(srcdir)/run_test
+TESTS_ENVIRONMENT = VALGRIND=$(VALGRIND) srcdir=$(srcdir) $(srcdir)/run_test
 
 system_tests = client_test exception_test quick_perftest quick_topictest
 TESTS += run-unit-tests start_broker $(system_tests) python_tests stop_broker 

Added: incubator/qpid/trunk/qpid/cpp/src/tests/RefCountedMap.cpp
URL: http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/tests/RefCountedMap.cpp?rev=590907&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/tests/RefCountedMap.cpp (added)
+++ incubator/qpid/trunk/qpid/cpp/src/tests/RefCountedMap.cpp Wed Oct 31 22:58:50 2007
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "qpid/sys/RefCountedMap.h"
+
+#include <boost/test/auto_unit_test.hpp>
+BOOST_AUTO_TEST_SUITE(RefCountedMap);
+
+using namespace std;
+using namespace qpid;
+using namespace qpid::sys;
+
+struct TestMap : public  RefCountedMap<int,int> {
+    static int instances;
+    TestMap() { ++instances; }
+    ~TestMap() { --instances; }
+};
+
+int TestMap::instances=0;
+
+BOOST_AUTO_TEST_CASE(testRefCountedMap) {
+    BOOST_CHECK_EQUAL(0, TestMap::instances);
+    intrusive_ptr<TestMap> map=new TestMap();
+    BOOST_CHECK_EQUAL(1, TestMap::instances);
+
+    // Empty map
+    BOOST_CHECK(map->empty());
+    BOOST_CHECK_EQUAL(map->size(), 0u);
+    BOOST_CHECK(map->begin()==map->end());
+    BOOST_CHECK(!map->begin());
+    BOOST_CHECK(!map->end());
+    BOOST_CHECK(map->find(1)==map->end());
+    BOOST_CHECK(!map->find(1));
+
+    {
+        // Add and modify an entry
+        pair<TestMap::iterator, bool> ib=map->insert(TestMap::value_type(1,11));
+        BOOST_CHECK(ib.second);
+        TestMap::iterator p = ib.first;
+        ib.first.reset();
+        BOOST_CHECK(p);
+        BOOST_CHECK_EQUAL(p->second, 11);
+        p->second=22;
+        BOOST_CHECK_EQUAL(22, map->find(1)->second);
+        BOOST_CHECK(!map->empty());
+        BOOST_CHECK_EQUAL(map->size(), 1u);
+
+        // Find an entry
+        TestMap::iterator q=map->find(1);
+        BOOST_CHECK(q==p);
+        BOOST_CHECK_EQUAL(q->first, 1);
+    }
+
+    BOOST_CHECK(map->empty());
+    BOOST_CHECK_EQUAL(1, TestMap::instances); 
+
+    {
+        // Hold the map via a reference to an entry.
+        TestMap::iterator p=map->insert(TestMap::value_type(2,22)).first;
+        map=0;                      // Release the map->
+        BOOST_CHECK_EQUAL(1, TestMap::instances); // Held by entry.
+        BOOST_CHECK_EQUAL(p->second, 22);
+    }
+
+    BOOST_CHECK_EQUAL(0, TestMap::instances); 
+}
+
+
+BOOST_AUTO_TEST_CASE(testRefCountedMapIterator) {
+    BOOST_CHECK_EQUAL(TestMap::instances, 0);
+    {
+        intrusive_ptr<TestMap> map=new TestMap();
+        TestMap::iterator iter[4], p, q;
+        for (int i = 0; i < 4; ++i) 
+            iter[i] = map->insert(make_pair(i, 10+i)).first;
+        int j=0;
+        for (p = map->begin(); p != map->end(); ++p, ++j)  {
+            BOOST_CHECK_EQUAL(p->first, j);
+            BOOST_CHECK_EQUAL(p->second, 10+j);
+        }
+        BOOST_CHECK_EQUAL(4, j);
+
+        // Release two entries.
+        iter[0]=iter[2]=TestMap::iterator();
+        
+        p=map->begin();
+        BOOST_CHECK_EQUAL(p->second, 11);
+        ++p;
+        BOOST_CHECK_EQUAL(p->second, 13);
+    }
+    BOOST_CHECK_EQUAL(TestMap::instances, 0);
+}

Propchange: incubator/qpid/trunk/qpid/cpp/src/tests/RefCountedMap.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/trunk/qpid/cpp/src/tests/RefCountedMap.cpp
------------------------------------------------------------------------------
    svn:keywords = Rev Date



Mime
View raw message