LUMIERA.clone/src/lib/typed-counter.hpp
Ichthyostega 2045132d3e SessionCommand: multithreaded stress test PASS (closes #1046)
Writing and debugging such tests is always an interesting challenge...

Fortunately this exercise didn't unveil any problem in the newly written
code, only some insidious problems in the test fixture itself. Which
again highlights the necessity, that each *command instance* needs
to be an independent clone from the original *command prototype*,
since argument binding messages and trigger messages can appear
in arbitrary order.
2017-01-14 08:37:46 +01:00

250 lines
6.7 KiB
C++

/*
TYPED-COUNTER.hpp - maintain a set of type based contexts
Copyright (C) Lumiera.org
2009, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file typed-counter.hpp
** Creating series 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. Each of
** those "slots" can be addressed by a distinct (compile time) type, but
** at the same time holds a numeric ID (runtime assigned on demand). This
** setup allows to bridge between metaprogramming and (runtime) dispatcher tables.
**
** Each such series of type-id-slots is associated to a distinct usage context.
** Those usage contexts are discerned by the template parameter \c CX. Each of
** these usage contexts uses a separate numbering scheme on his own, i.e. every
** new type encountered at runtime gets the next higher ID number (slot).
** @warning the actual ID numbers depend on the sequence of first encountering
** a given type. If this sequence isn't reproducible between runs, then
** also the generated type-IDs aren't reproducible. Thus its advisable
** \em not to rely on any specific numeric value here, but always just
** access the service through the type slots.
** @note Locking is based on a class lock per usage context, but a lock needs
** only be acquired to allocate a new ID number (double checked locking).
** Thus lock contention is considered not to be a problem, yet we need
** actually to verify this by real measurements (as of 2011)
** @todo 2010 ... 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)
**
*/
#ifndef LIB_TYPED_COUNTER_H
#define LIB_TYPED_COUNTER_H
#include "lib/error.hpp"
#include "lib/sync-classlock.hpp"
#include <vector>
#include <string>
namespace util {
std::string showSize (size_t) noexcept;
}
namespace lib {
typedef size_t IxID; //////////////////////TICKET #863
using std::vector;
using std::string;
/**
* Provide type-IDs for a specific context.
* This facility allows to access a numeric ID for each
* given 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. These typed contexts are
* considered to be orthogonal and independent of each other.
*/
template<class CX>
class TypedContext
{
static IxID lastGeneratedTypeID;
public:
static IxID
newTypeID (IxID& typeID)
{
ClassLock<TypedContext> synchronised;
if (!typeID)
typeID = ++lastGeneratedTypeID;
return typeID;
}
/** type-ID */
template<typename TY>
class ID
{
static IxID typeID;
public:
static IxID
get()
{
if (typeID)
return typeID;
else
return newTypeID(typeID);
}
};
};
/** storage for the type-ID generation mechanism */
template<class CX>
IxID TypedContext<CX>::lastGeneratedTypeID (0);
/** table holding all the generated type-IDs */
template<class CX>
template<typename TY>
IxID TypedContext<CX>::ID<TY>::typeID (0);
/**
* Utility providing a set of counters, each tied to a specific type.
*/
class TypedCounter
: public Sync<>
{
mutable vector<long> counters_;
template<typename TY>
IxID
slot() const
{
IxID 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>
long
get() const
{
Lock sync(this);
return counters_[slot<X>()];
}
template<class X>
long
inc()
{
Lock sync(this);
return ++counters_[slot<X>()];
}
template<class X>
long
dec()
{
Lock sync(this);
return --counters_[slot<X>()];
}
/* == diagnostics == */
size_t size() const { return counters_.size(); }
bool empty() const { return counters_.empty();}
};
/**
* Utility to produce member IDs
* for objects belonging to a "Family",
* as defined by a distinguishing type.
*/
template<typename TY>
class FamilyMember
{
const size_t id_;
/** member counter shared per template instance */
static size_t memberCounter;
/** threadsafe allocation of member ID */
static size_t
allocateNextMember()
{
ClassLock<FamilyMember> synchronised;
return memberCounter++;
}
public:
FamilyMember()
: id_{allocateNextMember()}
{ }
operator size_t() const
{
return id_;
}
operator string() const
{
return util::showSize (this->id_);
}
friend string
operator+ (string const& prefix, FamilyMember id)
{
return prefix+string(id);
}
friend string
operator+ (const char* prefix, FamilyMember id)
{
return string(prefix)+id;
}
};
/** allocate storage for the counter per type family */
template<typename TY>
size_t FamilyMember<TY>::memberCounter{0};
} // namespace lib
#endif