implemented TypedCounter and TypedContext.

Test basically pass, but shows an interesting
deadlock from time to time
This commit is contained in:
Fischlurch 2009-09-27 17:29:05 +02:00
parent 373f9a5724
commit f6397ef11c
3 changed files with 167 additions and 55 deletions

View file

@ -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 <tr1/memory>
#include <vector>
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 CX>
class TypedContext
{
static size_t lastGeneratedTypeID;
public:
static size_t
newTypeID (size_t& typeID)
{
ClassLock<TypedContext> synchronised();
if (!typeID)
typeID = ++lastGeneratedTypeID;
return typeID;
}
/** type-ID */
template<typename TY>
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<class CX>
size_t TypedContext<CX>::lastGeneratedTypeID (0);
/** table holding all the generated type-IDs */
template<class CX>
template<typename TY>
size_t TypedContext<CX>::ID<TY>::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<long> counters_;
template<typename TY>
size_t
slot() const
{
size_t typeID = TypedContext<TypedCounter>::ID<TY>::get();
if (size() < typeID)
counters_.resize (typeID);
ENSURE (counters_.capacity() >= typeID);
return (typeID - 1);
}
public:
TypedCounter()
{
counters_.reserve(5); // pre-allocated 5 slots
}
template<class X>
size_t
long
get() const
{
UNIMPLEMENTED ("get typed count");
Lock sync(this);
return counters_[slot<X>()];
}
template<class X>
void
long
inc()
{
UNIMPLEMENTED ("increment typed count");
Lock sync(this);
return ++counters_[slot<X>()];
}
template<class X>
void
long
dec()
{
UNIMPLEMENTED ("decrement typed count");
Lock sync(this);
return --counters_[slot<X>()];
}
/* == diagnostics == */
size_t size() const { return counters_.size(); }
bool empty() const { return counters_.empty();}
};

View file

@ -60,7 +60,6 @@ namespace visitor {
size_t tagID; ///< tag value
static size_t lastRegisteredID;
private:
static void
generateID (size_t& id)
{

View file

@ -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.
** <P>
** 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 <tr1/functional>
//#include <tr1/memory>
//#include <boost/ref.hpp>
//#include <boost/format.hpp>
//#include <iostream>
#include <vector>
#include <cstdlib>
//#include <string>
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<Dummy>();
}
@ -171,11 +165,11 @@ namespace test{
public:
Dummy() : localChecksum_(0) {}
};
/**
* Collection of target functions,
@ -264,8 +258,8 @@ namespace test{
*/
struct TestFamily
{
ScopedPtrVect<SingleCheck> checks_;
TypedCounter ourCounter_;
ScopedPtrVect<SingleCheck> 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<short>());
ASSERT (1 == myCounter.size());
ASSERT (0 == myCounter.get<long>());
ASSERT (2 == myCounter.size());
ASSERT (-1 == myCounter.dec<short>());
ASSERT (-2 == myCounter.dec<short>());
ASSERT (+1 == myCounter.inc<long>());
ASSERT (-2 == myCounter.get<short>());
ASSERT (+1 == myCounter.get<long>());
ASSERT (1 == TypedContext<TypedCounter>::ID<short>::get());
ASSERT (2 == TypedContext<TypedCounter>::ID<long>::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<num_Families; ++i)
testFamilies[i].account();
for_each (targetCollection, accountInternal);
ASSERT (sum_TypedCounter_ == sum_internal_);
}
};