From f6397ef11c844836598eaf75609c4a32998889e3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 27 Sep 2009 17:29:05 +0200 Subject: [PATCH] implemented TypedCounter and TypedContext. Test basically pass, but shows an interesting deadlock from time to time --- src/lib/typed-counter.hpp | 107 +++++++++++++++++++++++++---- src/lib/visitor-dispatcher.hpp | 1 - tests/lib/typed-counter-test.cpp | 114 ++++++++++++++++++++----------- 3 files changed, 167 insertions(+), 55 deletions(-) diff --git a/src/lib/typed-counter.hpp b/src/lib/typed-counter.hpp index e90490382..8b7ca90a7 100644 --- a/src/lib/typed-counter.hpp +++ b/src/lib/typed-counter.hpp @@ -30,7 +30,7 @@ ** typically will utilise different sets of types. ** @todo WIP WIP... this is the first, preliminary version of a facility, ** which is expected to get quite important for custom allocation management. - ** + ** ** @see typed-counter-test.cpp ** @see TypedAllocationManager ** @see AllocationCluster (custom allocation scheme using a similar idea inline) @@ -44,47 +44,128 @@ //#include "pre.hpp" #include "lib/error.hpp" -//#include "lib/format.hpp" -//#include "include/logging.h" +#include "lib/sync-classlock.hpp" - -//#include +#include namespace lib { -//using std::tr1::shared_ptr; + using std::vector; + + + /** + * Providing type-IDs for a specific context. + * This facility allows to access a numeric ID for each + * provided distinct type. Type-IDs may be used e.g. for + * dispatcher tables or for custom allocators. + * The type-IDs generated here are not completely global though. + * Rather, they are tied to a specific type context, e.g. a class + * implementing a custom allocator. + */ + template + class TypedContext + { + static size_t lastGeneratedTypeID; + + public: + static size_t + newTypeID (size_t& typeID) + { + ClassLock synchronised(); + if (!typeID) + typeID = ++lastGeneratedTypeID; + return typeID; + } + + /** type-ID */ + template + class ID + { + static size_t typeID; + + public: + static size_t + get() + { + if (typeID) + return typeID; + else + return newTypeID(typeID); + } + }; + }; + + /** storage for the type-ID generation mechanism */ + template + size_t TypedContext::lastGeneratedTypeID (0); + + /** table holding all the generated type-IDs */ + template + template + size_t TypedContext::ID::typeID (0); /** - * Helper providing a set of counters, each tied to a specific type. + * Helper providing a set of counters, each tied to a specific type. */ class TypedCounter + : public Sync<> { + mutable vector counters_; + + template + size_t + slot() const + { + size_t typeID = TypedContext::ID::get(); + if (size() < typeID) + counters_.resize (typeID); + + ENSURE (counters_.capacity() >= typeID); + return (typeID - 1); + } + + public: + TypedCounter() + { + counters_.reserve(5); // pre-allocated 5 slots + } + + template - size_t + long get() const { - UNIMPLEMENTED ("get typed count"); + Lock sync(this); + return counters_[slot()]; } template - void + long inc() { - UNIMPLEMENTED ("increment typed count"); + Lock sync(this); + return ++counters_[slot()]; } template - void + long dec() { - UNIMPLEMENTED ("decrement typed count"); + Lock sync(this); + return --counters_[slot()]; } + + + /* == diagnostics == */ + + size_t size() const { return counters_.size(); } + bool empty() const { return counters_.empty();} }; diff --git a/src/lib/visitor-dispatcher.hpp b/src/lib/visitor-dispatcher.hpp index 4ec682ea4..c0b60c997 100644 --- a/src/lib/visitor-dispatcher.hpp +++ b/src/lib/visitor-dispatcher.hpp @@ -60,7 +60,6 @@ namespace visitor { size_t tagID; ///< tag value static size_t lastRegisteredID; - private: static void generateID (size_t& id) { diff --git a/tests/lib/typed-counter-test.cpp b/tests/lib/typed-counter-test.cpp index 7335c9fed..60f712a27 100644 --- a/tests/lib/typed-counter-test.cpp +++ b/tests/lib/typed-counter-test.cpp @@ -21,66 +21,59 @@ * *****************************************************/ +/** @file typed-counter-test.cpp + ** Stress test to verify type-based contexts. + ** Besides a simple usage (unit) test, this test performs a massively multithreaded + ** test of the type-based contexts, through use of the TypedCounter. The idea behind + ** this facility is to provide a context, in which type-IDs can be allocated. In the + ** case of the TypedCounter, these type-IDs are used to index into a vector of counters, + ** this way allowing to access a counter for a given type. + **

+ ** This test builds several "families", which each share a TypedCounter. Each of these + ** families runs a set of member threads, which concurrently access the TypedCounter of + ** this family. After waiting for all threads to finish, we compare the checksum built + ** within the target objects with the checksum collected through the TypedCounters. + ** + ** @see thread-wrapper-test.cpp + ** @see thread-wrapper-join-test.cpp + ** + */ + + + #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" -//#include "proc/control/command.hpp" #include "lib/typed-counter.hpp" -//#include "proc/control/command-def.hpp" -//#include "lib/lumitime.hpp" -//#include "lib/symbol.hpp" -#include "lib/sync.hpp" #include "lib/scoped-ptrvect.hpp" #include "backend/thread-wrapper.hpp" +#include "lib/sync.hpp" #include "lib/util.hpp" #include - -//#include -//#include -//#include -//#include #include #include -//#include namespace lib { namespace test{ - - -// using boost::format; -// using boost::str; - //using lumiera::Time; - //using util::contains; + + using backend::Thread; using backend::JoinHandle; -// using std::tr1::shared_ptr; + using util::for_each; + using util::isnil; using std::tr1::bind; using std::tr1::placeholders::_1; -// using std::string; using std::vector; using std::rand; - //using std::cout; - //using std::endl; -// using lib::test::showSizeof; -// using util::isSameObject; - using util::for_each; -// using util::contains; - -// using session::test::TestClip; -// using lib::Symbol; -// using lumiera::P; - //using lumiera::typelist::BuildTupleAccessor; -// using lumiera::error::LUMIERA_ERROR_EXTERNAL; - namespace { // test data and helpers... const uint MAX_FAMILIES = 20; ///< maximum separate "families", each sharing a TypedCounter const uint MAX_MEMBERS = 5; ///< maximum members per family (member == test thread) const uint MAX_ITERATIONS = 5; ///< maximum iterations within a single test thread - const uint MAX_DELAY_ms = 50; ///< maximum delay between check iterations + const uint MAX_DELAY_ms = 3; ///< maximum delay between check iterations /** @@ -159,6 +152,7 @@ namespace test{ void collect_externalCount (TypedCounter& counter) { + // Lock not necessary, because of invocation sequence sum_TypedCounter_ += counter.get(); } @@ -171,11 +165,11 @@ namespace test{ public: Dummy() : localChecksum_(0) {} }; - - - - + + + + /** * Collection of target functions, @@ -264,8 +258,8 @@ namespace test{ */ struct TestFamily { - ScopedPtrVect checks_; TypedCounter ourCounter_; + ScopedPtrVect checks_; TestFamily() : checks_(MAX_MEMBERS) @@ -275,6 +269,12 @@ namespace test{ checks_.manage (new SingleCheck (ourCounter_)); } + ~TestFamily() + { + checks_.clear(); // blocks until all test threads finished + account(); + } + void account() { @@ -309,6 +309,41 @@ namespace test{ void run (Arg) + { + simpleUsageTest(); + tortureTest(); + } + + + void + simpleUsageTest () + { + TypedCounter myCounter; + ASSERT (isnil (myCounter)); + ASSERT (0==myCounter.size()); + + ASSERT (0 == myCounter.get()); + ASSERT (1 == myCounter.size()); + + ASSERT (0 == myCounter.get()); + ASSERT (2 == myCounter.size()); + + ASSERT (-1 == myCounter.dec()); + ASSERT (-2 == myCounter.dec()); + ASSERT (+1 == myCounter.inc()); + + ASSERT (-2 == myCounter.get()); + ASSERT (+1 == myCounter.get()); + + + ASSERT (1 == TypedContext::ID::get()); + ASSERT (2 == TypedContext::ID::get()); + ASSERT (2 == myCounter.size()); + } + + + void + tortureTest () { sum_TypedCounter_ = 0; sum_internal_ = 0; @@ -321,10 +356,7 @@ namespace test{ testFamilies.clear(); // blocks until all threads have terminated - for (uint i=0; i