From 517fb8b2009c79d4ffdcb5390be94059509a0163 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 27 Sep 2009 02:46:26 +0200 Subject: [PATCH] multithreaded torture test for the (planned) TypedCounter --- src/lib/typed-counter.hpp | 112 +++++-------- tests/lib/typed-counter-test.cpp | 274 ++++++++++++++++++++++++++++--- 2 files changed, 294 insertions(+), 92 deletions(-) diff --git a/src/lib/typed-counter.hpp b/src/lib/typed-counter.hpp index fc5cba278..e90490382 100644 --- a/src/lib/typed-counter.hpp +++ b/src/lib/typed-counter.hpp @@ -21,43 +21,19 @@ */ -/** @file typed-allocation-manager.hpp - ** Abstract foundation for building custom allocation managers. - ** Currently (as of 8/09) this is a draft, factored out of the command-registry. - ** The expectation is that we'll face several similar situations, and thus it - ** would be good to build up a common set of operations and behaviour. - ** @todo WIP WIP. - ** - ** \par Concept Summary - ** The idea is rather to tie the memory manager to a very specific usage situation, - ** then to provide a general-purpose allocator to be used by any instance of a given - ** type. Typically, the goal is to handle memory management for an index or registry, - ** holding implementation objects to be shielded from the client code. Moreover, we'll - ** have to deal with families of types rather then with individual types; usually - ** there will be some common or combined handling for all family members. - ** - ** The intention is for this TypedAllocationManager template to be used both as a base - ** class providing the implementation skeleton for the actual custom allocation manager, - ** and as an abstract interface, which can be forwarded to the \em implementation classes - ** in case there is some cooperation required to get the allocation done (for example, - ** there might be some type erasure involved, leaving the (otherwise opaque) implementation - ** class as the only entity with a limited knowledge about the actual memory layout, and - ** thus the only way of creating a clone properly would be to forward down into this - ** implementation class). - ** - ** Thus, TypedAllocationManager provides the classical operations of an allocator - ** - allocate - ** - construct - ** - deallocate - ** But each of these operations is to be invoked in a \em typed context. Besides, - ** there is a facility allowing to create ref-counting handles and smart-pointers, - ** which are internally tied to this memory manager through a deleter function. - ** - ** @todo using a quick-n-dirty heap allocation implementation for now (8/09), - ** but should write a custom allocator based on cehteh's mpool! - ** - ** @see CommandRegistry - ** @see AllocationCluster (another custom allocation scheme, which could be united) +/** @file typed-counter.hpp + ** Creating sets of type-based contexts. + ** The idea is to get a "slot" for any given type, so we can build + ** tables or families of implementations based on these types. Currently, + ** the slot allocation is based on static variables and thus is global. + ** This leads to a waste of slots, as the various clients of this service + ** 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) ** */ @@ -68,52 +44,50 @@ //#include "pre.hpp" #include "lib/error.hpp" -#include "lib/format.hpp" -#include "include/logging.h" +//#include "lib/format.hpp" +//#include "include/logging.h" -#include +//#include namespace lib { - using std::tr1::shared_ptr; +//using std::tr1::shared_ptr; - /** - * temporary helper to count the number of allocations - * for diagnostic purpose. When actually implementing a - * custom allocation, this information should be available - * from the allocator. - */ + /** + * Helper providing a set of counters, each tied to a specific type. + */ class TypedCounter { - public: - template - size_t - get() const - { - UNIMPLEMENTED ("get typed count"); - } - - template - void - inc() - { - UNIMPLEMENTED ("increment typed count"); - } - - template - void - dec() - { - UNIMPLEMENTED ("decrement typed count"); - } + public: + template + size_t + get() const + { + UNIMPLEMENTED ("get typed count"); + } + + template + void + inc() + { + UNIMPLEMENTED ("increment typed count"); + } + + template + void + dec() + { + UNIMPLEMENTED ("decrement typed count"); + } }; - + + } // namespace lib #endif diff --git a/tests/lib/typed-counter-test.cpp b/tests/lib/typed-counter-test.cpp index e21272255..7335c9fed 100644 --- a/tests/lib/typed-counter-test.cpp +++ b/tests/lib/typed-counter-test.cpp @@ -24,17 +24,22 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" //#include "proc/control/command.hpp" -#include "proc/control/typed-allocation-manager.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/util.hpp" +#include -#include +//#include //#include //#include //#include +#include #include //#include @@ -47,14 +52,19 @@ namespace test{ // using boost::str; //using lumiera::Time; //using util::contains; - using std::tr1::shared_ptr; -// using std::tr1::bind; + using backend::Thread; + using backend::JoinHandle; +// using std::tr1::shared_ptr; + 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::isSameObject; + using util::for_each; // using util::contains; // using session::test::TestClip; @@ -66,38 +76,256 @@ namespace test{ // using lumiera::error::LUMIERA_ERROR_EXTERNAL; namespace { // test data and helpers... - - long checksum_ = 0; - - /** - * Yet-another fammily of dummy types.... - */ - template - class DummyType + + 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 + + + /** + * Interface to a family of dummy types + */ + class DummyType + { + public: + virtual ~DummyType() { } + + /** core test operation: do a random increment or decrement + * on the provided TypedCounter instance, and also save an + * account to a local embedded checksum for verification */ + virtual void doCount (TypedCounter&) =0; + + virtual void collect_externalCount (TypedCounter&) =0; + virtual void collect_internalCount () =0; + }; + + + /* === Checksums === */ + + long sum_TypedCounter_; ///< Sum1: calculated from TypedCounter + long sum_internal_; ///< Sum2: control value calculated from Dummy::localChecksum_ + + void + accountExternal (DummyType& target, TypedCounter& counter_to_use) { - char crap_[siz]; - }; + target.collect_externalCount (counter_to_use); + } + void + accountInternal (DummyType& target) + { + target.collect_internalCount(); + } + + + /** + * To actually drive the TypedCounter invocations, we need a family + * of different (but of course related) types. Actually, we use these + * subclasses here also to carry out the invocations and the accounting + * to build up the checksums for verification. + */ + template + class Dummy + : public DummyType + , public lib::Sync<> + { + long localChecksum_; + + void + record_internal (int increment) + { + Lock protect(this); + localChecksum_ += increment; + } + + + void + doCount (TypedCounter& counter) + { + // note: deliberately *not* synchronised + + if (rand() % 2) + { + counter.inc(); + record_internal (+1); + } + else + { + counter.dec(); + record_internal (-1); + } + } + + void + collect_externalCount (TypedCounter& counter) + { + sum_TypedCounter_ += counter.get(); + } + + void + collect_internalCount () + { + sum_internal_ += localChecksum_; + } + + public: + Dummy() : localChecksum_(0) {} + }; + + + + + + + /** + * Collection of target functions, + * to be invoked during the test run + */ + struct DummyTarget + { + typedef ScopedPtrVect TargetVect; + + TargetVect targets_; + + DummyTarget () + : targets_(10) + { + targets_.manage(new Dummy<0>); + targets_.manage(new Dummy<1>); + targets_.manage(new Dummy<2>); + targets_.manage(new Dummy<3>); + targets_.manage(new Dummy<4>); + targets_.manage(new Dummy<5>); + targets_.manage(new Dummy<6>); + targets_.manage(new Dummy<7>); + targets_.manage(new Dummy<8>); + targets_.manage(new Dummy<9>); + } + + + /** entry point for the SingleCheck instances + * to trigger off a single invocation + */ + void + torture (TypedCounter& counter_to_use) + { + uint victim = (rand() % 10); + targets_[victim].doCount (counter_to_use); + } + + /** allow Iteration over all targets in the TargetVect */ + TargetVect::iterator begin() { return targets_.begin(); } + TargetVect::iterator end() { return targets_.end(); } + }; + + DummyTarget targetCollection; + + + + /** + * Each single check runs in a separate thread + * and performs a random sequence of increments + * and decrements on random targets. + */ + class SingleCheck + : JoinHandle, + Thread + { + TypedCounter& counterSet_; + long localChecksum_; + + public: + SingleCheck (TypedCounter& counter_to_use) + : Thread("TypedCounter_test worker Thread", + bind (&SingleCheck::runCheckSequence, this, (rand() % MAX_ITERATIONS)), + (backend::JoinHandle&)*this + ) + , counterSet_(counter_to_use) + , localChecksum_(0) + { } + + ~SingleCheck () { this->join(); } + + + private: + void runCheckSequence(uint iterations) + { + while (--iterations) + { + usleep (1000 * (rand() % MAX_DELAY_ms)); + targetCollection.torture (counterSet_); + } } + }; + + + /** + * Family of individual checks, sharing + * a common TypedCounter instance. + */ + struct TestFamily + { + ScopedPtrVect checks_; + TypedCounter ourCounter_; + + TestFamily() + : checks_(MAX_MEMBERS) + { + uint members (1 + rand() % MAX_MEMBERS); + while (members--) + checks_.manage (new SingleCheck (ourCounter_)); + } + + void + account() + { + for_each ( targetCollection + , bind (accountExternal, _1, ourCounter_) + ); + } + }; + + + /** a series of independent context sets */ + typedef ScopedPtrVect FamilyTable; + } /*************************************************************************************** - * @test cover the basic implementation of a custom allocator, delegating to mpool. - * TypedAllocationManager is a base class, used e.g. to build the CommandRegistry. + * @test build multiple sets of type-based contexts and run a simple counting operation + * in each of them concurrently. Check the proper allocation of type-IDs in each + * context and verify correct counting operation by checksum. * - * @todo planned but not implemented as of 8/09 see also Ticket #217 + * @todo using currently (9/09) a simple implementation based on static variables. + * This causes waste of storage for the type-based tables. It would be better + * if each set would build it's own Type-IDs. Ticket # * - * @see CommandRegistry - * @see command-registry-test.cpp - * @see allocationclustertest.cpp + * @see TypedAllocationManager + * @see typed-counter.hpp */ class TypedCounter_test : public Test { - virtual void + void run (Arg) { - UNIMPLEMENTED ("type based contexts"); + sum_TypedCounter_ = 0; + sum_internal_ = 0; + + uint num_Families (1 + rand() % MAX_FAMILIES); + + FamilyTable testFamilies(num_Families); + for (uint i=0; i