multithreaded torture test for the (planned) TypedCounter
This commit is contained in:
parent
83cd7fd830
commit
517fb8b200
2 changed files with 294 additions and 92 deletions
|
|
@ -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 <tr1/memory>
|
||||
//#include <tr1/memory>
|
||||
|
||||
|
||||
|
||||
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<class X>
|
||||
size_t
|
||||
get() const
|
||||
{
|
||||
UNIMPLEMENTED ("get typed count");
|
||||
}
|
||||
|
||||
template<class X>
|
||||
void
|
||||
inc()
|
||||
{
|
||||
UNIMPLEMENTED ("increment typed count");
|
||||
}
|
||||
|
||||
template<class X>
|
||||
void
|
||||
dec()
|
||||
{
|
||||
UNIMPLEMENTED ("decrement typed count");
|
||||
}
|
||||
public:
|
||||
template<class X>
|
||||
size_t
|
||||
get() const
|
||||
{
|
||||
UNIMPLEMENTED ("get typed count");
|
||||
}
|
||||
|
||||
template<class X>
|
||||
void
|
||||
inc()
|
||||
{
|
||||
UNIMPLEMENTED ("increment typed count");
|
||||
}
|
||||
|
||||
template<class X>
|
||||
void
|
||||
dec()
|
||||
{
|
||||
UNIMPLEMENTED ("decrement typed count");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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 <tr1/functional>
|
||||
|
||||
#include <tr1/memory>
|
||||
//#include <tr1/memory>
|
||||
//#include <boost/ref.hpp>
|
||||
//#include <boost/format.hpp>
|
||||
//#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
//#include <string>
|
||||
|
||||
|
|
@ -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<uint siz>
|
||||
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<uint i>
|
||||
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<Dummy>();
|
||||
record_internal (+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
counter.dec<Dummy>();
|
||||
record_internal (-1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
collect_externalCount (TypedCounter& counter)
|
||||
{
|
||||
sum_TypedCounter_ += counter.get<Dummy>();
|
||||
}
|
||||
|
||||
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<DummyType> 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<SingleCheck> 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<TestFamily> 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<num_Families; ++i)
|
||||
testFamilies.manage (new TestFamily);
|
||||
|
||||
testFamilies.clear(); // blocks until all threads have terminated
|
||||
|
||||
for (uint i=0; i<num_Families; ++i)
|
||||
testFamilies[i].account();
|
||||
for_each (targetCollection, accountInternal);
|
||||
|
||||
ASSERT (sum_TypedCounter_ == sum_internal_);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue