draft solution to factor out management of the resource handle
based on the idea of a diagnostic context stack
This commit is contained in:
parent
763f86fe0e
commit
4dfd7266b9
2 changed files with 189 additions and 27 deletions
178
src/lib/sync-nobug-resource-handle.hpp
Normal file
178
src/lib/sync-nobug-resource-handle.hpp
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
SYNC-NOBUG-RESOURCE-HANDLE.hpp - supplement: manage storage for NoBug resource handles
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2010, 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 sync-nobug-resource-handle.hpp
|
||||
** Supplement to sync.hpp: manage the storage for NoBug resource handles.
|
||||
** For resource tracking we need storage for each \em usage of a resource, in order
|
||||
** to provide a user handle for this usage situation. I consider this an internal
|
||||
** detail and want to keep it away from the code concerned with the resource itself
|
||||
** (here the object monitor).
|
||||
**
|
||||
** @todo experimental solution
|
||||
**/
|
||||
|
||||
|
||||
#ifndef LIB_SYNC_NOBUG_RESOURCE_HANDLE_H
|
||||
#define LIB_SYNC_NOBUG_RESOURCE_HANDLE_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/bool-checkable.hpp"
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace sync{
|
||||
|
||||
|
||||
/**
|
||||
* Thread local pointer without ownership management.
|
||||
* This (noncopyable) smart-ptr class cares for registering and
|
||||
* deregistering the per-instance access key, but besides that
|
||||
* behaves passively, like a normal pointer. When first accessed,
|
||||
* the pointer is NIL in each new thread; it may be set by assignment.
|
||||
*/
|
||||
template<typename TAR>
|
||||
class ThreadLocalPtr
|
||||
: public BoolCheckable< ThreadLocalPtr<TAR>
|
||||
, boost::noncopyable >
|
||||
{
|
||||
pthread_key_t key_;
|
||||
|
||||
public:
|
||||
ThreadLocalPtr()
|
||||
{
|
||||
if (pthread_key_create (&key_, NULL))
|
||||
throw lumiera::error::External ("unable to create key for thread local data");
|
||||
}
|
||||
|
||||
~ThreadLocalPtr()
|
||||
{
|
||||
WARN_IF (pthread_key_delete (key_), sync, "failure to drop thread-local data key");
|
||||
}
|
||||
|
||||
|
||||
bool isValid() const { return get(); }
|
||||
TAR& operator* () const { return *accessChecked(); }
|
||||
TAR* operator-> () const { return accessChecked(); }
|
||||
|
||||
void operator= (TAR& tar) { set(&tar); }
|
||||
|
||||
|
||||
TAR*
|
||||
get() const
|
||||
{
|
||||
return static_cast<TAR*> (pthread_getspecific (key_));
|
||||
}
|
||||
|
||||
void
|
||||
set (TAR* pointee)
|
||||
{
|
||||
if (pthread_setspecific (key_, pointee))
|
||||
throw lumiera::error::External ("failed to store thread local data");
|
||||
}
|
||||
|
||||
private:
|
||||
TAR*
|
||||
accessChecked()
|
||||
{
|
||||
TAR *p(get());
|
||||
if (!p)
|
||||
throw lumiera::error::State ("dereferencing a thread local NULL pointer"
|
||||
,lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE);
|
||||
return p;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//#ifdef NOBUG_MODE_ALPHA /////////TODO don't we need the handle in BETA builds for resource logging?
|
||||
|
||||
/**
|
||||
* Diagnostic context, housing the NoBug resource tracker handle.
|
||||
* Instances of this class should be created on the stack at appropriate scopes.
|
||||
* When used in nested scopes, a chain (stack) of contexts is maintained automatically.
|
||||
* Client code can access the innermost handle via static API.
|
||||
* @warning never store this into global data structures.
|
||||
*/
|
||||
class NobugResourceHandle
|
||||
: boost::noncopyable
|
||||
{
|
||||
typedef nobug_resource_user* Handle;
|
||||
typedef ThreadLocalPtr<NobugResourceHandle> ThreadLocalAccess;
|
||||
|
||||
Handle handle_;
|
||||
NobugResourceHandle * const prev_;
|
||||
|
||||
/** embedded thread local pointer
|
||||
* to the innermost context encountered */
|
||||
static ThreadLocalAccess&
|
||||
current()
|
||||
{
|
||||
static ThreadLocalAccess accessPoint;
|
||||
return accessPoint;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
NobugResourceHandle()
|
||||
: handle_(0)
|
||||
, prev_(current().get())
|
||||
{
|
||||
current().set (this);
|
||||
}
|
||||
|
||||
~NobugResourceHandle()
|
||||
{
|
||||
ASSERT (this == current().get());
|
||||
current().set (prev_);
|
||||
}
|
||||
|
||||
|
||||
operator Handle* () ///< payload: NoBug resource tracker user handle
|
||||
{
|
||||
return &handle_;
|
||||
}
|
||||
|
||||
/** accessing the innermost diagnostic context created */
|
||||
static NobugResourceHandle&
|
||||
access ()
|
||||
{
|
||||
NobugResourceHandle* 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;
|
||||
}
|
||||
};
|
||||
//#endif
|
||||
|
||||
|
||||
|
||||
}} // namespace lib::sync
|
||||
#endif
|
||||
|
|
@ -66,9 +66,9 @@
|
|||
#ifndef LIB_SYNC_H
|
||||
#define LIB_SYNC_H
|
||||
|
||||
#include "include/logging.h"
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/util.hpp"
|
||||
#include "lib/sync-nobug-resource-handle.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "lib/mutex.h"
|
||||
|
|
@ -229,26 +229,6 @@ namespace lib {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* housing the resource tracker handle in ALPHA builds,
|
||||
* an empty struct in BETA or RELEASE builds. /////////TODO don't we need the handle in BETA builds for resource logging?
|
||||
*/
|
||||
struct NobugResourceHandle
|
||||
{
|
||||
RESOURCE_USER (handle_);
|
||||
|
||||
NobugResourceHandle()
|
||||
{
|
||||
RESOURCE_USER_INIT (handle_);
|
||||
}
|
||||
|
||||
operator nobug_resource_user** ()
|
||||
{
|
||||
return NOBUG_IF_ALPHA (&handle_) NOBUG_IF_NOT_ALPHA(0); //////TODO is there a better way of achieving this?
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* ========== abstractions defining the usable synchronisation primitives ============== */
|
||||
|
|
@ -258,7 +238,11 @@ namespace lib {
|
|||
: protected MTX
|
||||
{
|
||||
protected:
|
||||
NobugResourceHandle usage_;
|
||||
NobugResourceHandle&
|
||||
_usage()
|
||||
{
|
||||
return NobugResourceHandle::access();
|
||||
}
|
||||
|
||||
~Mutex () { }
|
||||
Mutex () { }
|
||||
|
|
@ -269,13 +253,13 @@ namespace lib {
|
|||
void
|
||||
acquire()
|
||||
{
|
||||
MTX::lock (usage_);
|
||||
MTX::lock (_usage());
|
||||
}
|
||||
|
||||
void
|
||||
release()
|
||||
{
|
||||
MTX::unlock (usage_);
|
||||
MTX::unlock (_usage());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -307,9 +291,9 @@ namespace lib {
|
|||
bool ok = true;
|
||||
while (ok && !predicate())
|
||||
if (waitEndTime)
|
||||
ok = CDX::timedwait (&waitEndTime, this->usage_);
|
||||
ok = CDX::timedwait (&waitEndTime, this->_usage());
|
||||
else
|
||||
ok = CDX::wait (this->usage_);
|
||||
ok = CDX::wait (this->_usage());
|
||||
|
||||
if (!ok && lumiera_error_expect(LUMIERA_ERROR_LOCK_TIMEOUT)) return false;
|
||||
lumiera::throwOnError(); // any other error thows
|
||||
|
|
@ -475,7 +459,7 @@ namespace lib {
|
|||
|
||||
public:
|
||||
class Lock
|
||||
: private noncopyable
|
||||
: sync::NobugResourceHandle
|
||||
{
|
||||
Monitor& mon_;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue