draft solution to factor out management of the resource handle

based on the idea of a diagnostic context stack
This commit is contained in:
Fischlurch 2010-02-13 04:03:27 +01:00
parent 763f86fe0e
commit 4dfd7266b9
2 changed files with 189 additions and 27 deletions

View 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

View file

@ -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_;