generalised diagnostic context passes unit test
This commit is contained in:
parent
5498ace9fc
commit
24a8d6a926
5 changed files with 93 additions and 139 deletions
|
|
@ -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? */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue