implemented TypedCounter and TypedContext.
Test basically pass, but shows an interesting deadlock from time to time
This commit is contained in:
parent
373f9a5724
commit
f6397ef11c
3 changed files with 167 additions and 55 deletions
|
|
@ -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();}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ namespace visitor {
|
|||
size_t tagID; ///< tag value
|
||||
static size_t lastRegisteredID;
|
||||
|
||||
private:
|
||||
static void
|
||||
generateID (size_t& id)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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_);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue