generalised diagnostic context passes unit test

This commit is contained in:
Fischlurch 2011-12-24 05:38:54 +01:00
parent 5498ace9fc
commit 24a8d6a926
5 changed files with 93 additions and 139 deletions

View file

@ -57,7 +57,6 @@ namespace lib {
#ifdef NOBUG_MODE_ALPHA ////////////////////////TODO don't we need the handle in BETA builds for resource logging?
/**
* Diagnostic data frame to collect specific information concerning a scope.
@ -66,19 +65,15 @@ namespace lib {
* such information frames concerning nested scopes is maintained automatically.
* It can be accessed via static API.
* @warning relies on thread-local access; never use this within global data structures.
* @note as of 2/10 used for housing the NoBug resource tracker handle
* in conjunction with object monitor based locking
* @see sync.hpp
* @todo 12/2011 : not in use currently, but the concept seems useful
* and should be generalised. //////////////////////////////////////TICKET #687
*/
template<typename VAL>
class DiagnosticContext
: boost::noncopyable
{
typedef nobug_resource_user* Handle;
typedef ThreadLocalPtr<DiagnosticContext> ThreadLocalAccess;
typedef std::vector<VAL> ValSequence;
Handle handle_;
const VAL value_;
DiagnosticContext * const prev_;
/** embedded thread local pointer
@ -92,8 +87,8 @@ namespace lib {
public:
DiagnosticContext()
: handle_(0)
DiagnosticContext(VAL const& value_to_log = VAL())
: value_(value_to_log)
, prev_(current().get())
{
current().set (this);
@ -106,9 +101,9 @@ namespace lib {
}
operator Handle* () ///< payload: NoBug resource tracker user handle
operator VAL const&() ///< access the payload by conversion
{
return &handle_;
return value_;
}
/** accessing the innermost diagnostic context created */
@ -118,39 +113,33 @@ namespace lib {
DiagnosticContext* innermost = current().get();
if (!innermost)
throw lumiera::error::Logic ("Accessing Diagnostic context out of order; "
"an instance should have been created in "
"an instance should have been created within "
"an enclosing scope");
return *innermost;
}
};
#else /* not NOBUG_ALPHA */
/**
* Disabled placeholder for the Diagnostic context, not used in release builds.
*/
class DiagnosticContext
: boost::noncopyable
{
public:
operator Handle* () ///< payload: NoBug resource tracker user handle
/** snapshot of the current stack of diagnostic frames
* @return vector with all the payload values currently
* on the thread-local diagnostic stack. Might
* be empty. Values start with frame next to
* the current scope and end with outermost.
*/
static ValSequence
extractStack()
{
return 0;
}
/** accessing the innermost diagnostic context created */
static DiagnosticContext&
access ()
{
UNIMPLEMENTED ("how to disable DiagnosticContext with minimum overhead");
ValSequence loggedValues;
DiagnosticContext* next = current().get();
while (next)
{
loggedValues.push_back (*next);
next = next->prev_;
}
return loggedValues;
}
};
#endif /* NOBUG_ALPHA? */

View file

@ -1,5 +1,5 @@
/*
DIAGNOSTIC-CONTEXT.hpp - thread local stack for explicitly collecting diagnostic context info
NOBUG-RESOURCE-HANDLE-CONTEXT.hpp - thread local stack to manage NoBug resource handles
Copyright (C) Lumiera.org
2010, Hermann Vosseler <Ichthyostega@web.de>
@ -20,29 +20,28 @@
*/
/** @file diagnostic-context.hpp
** Facility for collecting diagnostic context information explicitly.
** Unlike a trace logging run, this facility is intended to be fed explicitly with
** diagnostic information describing the currently ongoing operation in a semantic
** high-level manner. The rationale is to pinpoint \em those pieces of information,
** which aren't obvious when just looking at a callstack with the debugger.
** Instances of the class DiagnosticContext should be placed explicitly as automatic
** (stack) variable into selected relevant scopes; these "information frames" could
** be accessed from an enclosed scope as a per-thread stack. DiagnosticContext
** provides an controlled environment for adding diagnostic code on demand; typically
** to be configured such as to resolve into an empty class for release builds.
/** @file nobug-resource-handle-context.hpp
** Thread-local stack of NoBug resource handles.
** This helper allows to access the resource handle in the nearest enclosing scope.
** The motivation for this approach was to avoid passing the resource handle over
** several intermediary function calls when using a scoped variable to control
** object monitor locking. Within this usage context, the necessity of passing
** a NoBug resource handle seems to be a cross-cutting concern, and not directly
** related to the core concern (controlling a mutex).
**
** As of 2/10, this is an experimental feature in evaluation. To start with,
** I'll use it to solve the problem of providing a NoBug resource tracker handle
** without tangling the object monitor code (sync.hpp) with low level, NoBug related
** implementation details.
** @remarks as of 8/2011, this feature is not used anymore. In 12/2011, the concept
** of a diagnostic context stack was generalised. At that point, this header was
** created as an off-spin, now based on the generalised feature, to document this
** usage possibility, which might be required again at some point in the future.
**
** @see lib::DiagnosticContext
** @see diagnostic-context-test.hpp
**
** @todo add the actual diagnostic content
**/
#ifndef LIB_DIAGNOSTIC_CONTEXT_H
#define LIB_DIAGNOSTIC_CONTEXT_H
#ifndef LIB_NOBUG_RESOURCE_HANDLE_CONTEXT_H
#define LIB_NOBUG_RESOURCE_HANDLE_CONTEXT_H
#include "lib/error.hpp"
@ -60,68 +59,14 @@ namespace lib {
#ifdef NOBUG_MODE_ALPHA ////////////////////////TODO don't we need the handle in BETA builds for resource logging?
/**
* Diagnostic data frame to collect specific information concerning a scope.
* To be placed explicitly as an automatic (stack) variable. Provides a controlled
* environment for hooking up diagnostic code. Within each thread, a stack of
* such information frames concerning nested scopes is maintained automatically.
* It can be accessed via static API.
* Diagnostic data frame to hold a NoBug resource handle.
* This way, code in nested function calls may pick up the nearest available handle.
* @warning relies on thread-local access; never use this within global data structures.
* @note as of 2/10 used for housing the NoBug resource tracker handle
* in conjunction with object monitor based locking
* @see sync.hpp
* @todo 12/2011 : not in use currently, but the concept seems useful
* and should be generalised. //////////////////////////////////////TICKET #687
*/
class DiagnosticContext
: boost::noncopyable
class NobugResourceHandleContext
: DiagnosticContext<nobug_resource_user*>
{
typedef nobug_resource_user* Handle;
typedef ThreadLocalPtr<DiagnosticContext> ThreadLocalAccess;
Handle handle_;
DiagnosticContext * const prev_;
/** embedded thread local pointer
* to the innermost context encountered */
static ThreadLocalAccess&
current()
{
static ThreadLocalAccess accessPoint;
return accessPoint;
}
public:
DiagnosticContext()
: handle_(0)
, prev_(current().get())
{
current().set (this);
}
~DiagnosticContext()
{
ASSERT (this == current().get());
current().set (prev_);
}
operator Handle* () ///< payload: NoBug resource tracker user handle
{
return &handle_;
}
/** accessing the innermost diagnostic context created */
static DiagnosticContext&
access ()
{
DiagnosticContext* innermost = current().get();
if (!innermost)
throw lumiera::error::Logic ("Accessing Diagnostic context out of order; "
"an instance should have been created in "
"an enclosing scope");
return *innermost;
}
};
@ -131,20 +76,21 @@ namespace lib {
/**
* Disabled placeholder for the Diagnostic context, not used in release builds.
*/
class DiagnosticContext
class NobugResourceHandleContext
: boost::noncopyable
{
typedef nobug_resource_user* Handle;
public:
operator Handle* () ///< payload: NoBug resource tracker user handle
operator Handle () ///< payload: NoBug resource tracker user handle
{
return 0;
}
/** accessing the innermost diagnostic context created */
static DiagnosticContext&
static NobugResourceHandleContext&
access ()
{
UNIMPLEMENTED ("how to disable DiagnosticContext with minimum overhead");

View file

@ -25,10 +25,10 @@
** A closure enabling self-contained execution of commands within the ProcDispatcher.
** After defining a proc-layer command, at some point the function arguments
** of the contained operation are "closed" by storing concrete argument values.
** These values will later on be fed to the operation when the command is invoked.
** These values will be fed later on to the operation when the command is invoked.
**
** Most of the command machinery accesses this function closure through the interface
** CmdClosure, while, when defining a command, subclasses typed to the specific
** Most of the command machinery accesses this function closure through the generic
** interface CmdClosure, while, when defining a command, subclasses typed to the specific
** function arguments are created. Especially, there is an ArgumentHolder template,
** which is used to define the storage for the concrete arguments. This ArgumentHolder
** internally contains an Closure<SIG> instance (where SIG is the signature of the
@ -43,10 +43,10 @@
**
** Later on, any command needs to be made ready for execution by binding it to a specific
** execution environment, which especially includes the target objects to be mutated by the
** command. Effectively, this means "closing" the Mutation (and UNDO) functor(s) with the
** command. Effectively, this means "closing" the Mutation (and UNDO) functor(s)) with the
** actual function arguments. These arguments are stored embedded within an ArgumentHolder,
** which thereby acts as closure. Besides, the ArgumentHolder also has to accommodate for
** storage holding the captured UNDO state (memento). Thus, internally the ArgumentHolder
** storage holding the captured UNDO state (memento). Internally the ArgumentHolder
** has to keep track of the actual types, thus allowing to re-construct the concrete
** function signature when closing the Mutation.
**
@ -214,7 +214,7 @@ namespace control {
friend bool
compare (ParamAccessor const&, ParamAccessor const&)
{
return true;;
return true;
}
};

View file

@ -653,6 +653,11 @@ return: 0
END
TEST "Thread-local diagnostic context" DiagrnsticContext_test <<END
return: 0
END
TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END
return: 0
END

View file

@ -42,9 +42,14 @@ namespace test{
const uint NUM_THREADS = 100;
const uint MAX_RAND = 1000*1000;
inline bool
isOdd (uint val)
{
return bool (val % 2);
}
} // (End) test setup....
using test::Test;
using backend::ThreadJoinable;
using error::LUMIERA_ERROR_LOGIC;
using std::rand;
@ -56,6 +61,7 @@ namespace test{
*/
typedef DiagnosticContext<uint> Marker;
typedef std::vector<uint> VecI;
@ -80,7 +86,7 @@ namespace test{
virtual void
run (Arg)
{
verify_simpleProperties();
verify_simpleAccess();
verify_heavilyParallelUsage();
}
@ -93,32 +99,41 @@ namespace test{
void
verify_simpleAccess()
{
VERIFY_ERROR (LOGIC, Marker.access());
VERIFY_ERROR (LOGIC, Marker::access());
VecI loggedValues;
Marker zero(0);
CHECK (0 == zero);
CHECK (0 == Marker.access());
CHECK (0 == Marker::access());
{ // nested scope
CHECK (0 == Marker.access());
CHECK (0 == Marker::access());
Marker one(1);
CHECK (1 == Marker.access());
CHECK (1 == Marker::access());
CHECK (1 == one);
CHECK (0 == zero);
{ // nested scope
CHECK (1 == Marker.access());
CHECK (1 == Marker::access());
Marker two(2);
CHECK (2 == Marker.access());
CHECK (2 == Marker::access());
CHECK (2 == two);
CHECK (1 == one);
CHECK (0 == zero);
loggedValues = Marker::extractStack();
}
CHECK (1 == Marker.access());
CHECK (1 == Marker::access());
}
CHECK (0 == Marker.access());
CHECK (0 == Marker::access());
CHECK (3 == loggedValues.size());
CHECK (2 == loggedValues[0]);
CHECK (1 == loggedValues[1]);
CHECK (0 == loggedValues[2]);
}
@ -152,8 +167,6 @@ namespace test{
{ }
};
typedef std::vector<uint> VecI;
/** the actual test operation running in a separate thread */
static void
@ -163,11 +176,11 @@ namespace test{
VecI sequence = descend (seed);
uint prev = seed;
uint prev = 0;
for (uint i=0; i < sequence.size(); ++i)
{
uint val = sequence[i];
if (! (isOdd(val) && val < prev))
if (! (isOdd(val) && seed >= val && val > prev ))
throw error::Fatal ("thread-local diagnostic stack");
prev = val;
}
@ -177,8 +190,9 @@ namespace test{
descend (uint current)
{
if (current < 2)
return Marker.extractStack();
return Marker::extractStack();
usleep (500);
if (isOdd(current))
{
Marker remember(current);