diff --git a/src/lib/diagnostic-context.hpp b/src/lib/diagnostic-context.hpp index 1fc658e69..221f59fad 100644 --- a/src/lib/diagnostic-context.hpp +++ b/src/lib/diagnostic-context.hpp @@ -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 class DiagnosticContext : boost::noncopyable { - typedef nobug_resource_user* Handle; typedef ThreadLocalPtr ThreadLocalAccess; + typedef std::vector 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? */ + + diff --git a/src/lib/nobug-resource-handle-context.hpp b/src/lib/nobug-resource-handle-context.hpp index 1fc658e69..ac4ccca52 100644 --- a/src/lib/nobug-resource-handle-context.hpp +++ b/src/lib/nobug-resource-handle-context.hpp @@ -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 @@ -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 { - typedef nobug_resource_user* Handle; - typedef ThreadLocalPtr 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"); diff --git a/src/proc/control/command-closure.hpp b/src/proc/control/command-closure.hpp index a2246125c..b03f88405 100644 --- a/src/proc/control/command-closure.hpp +++ b/src/proc/control/command-closure.hpp @@ -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 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; } }; diff --git a/tests/40components.tests b/tests/40components.tests index fc23dc900..54862528f 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -653,6 +653,11 @@ return: 0 END +TEST "Thread-local diagnostic context" DiagrnsticContext_test < Marker; + typedef std::vector 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 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);