/*
* lockptr.h
*
* This file is part of NEST.
*
* Copyright (C) 2004 The NEST Initiative
*
* NEST 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.
*
* NEST 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 NEST. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef LOCK_PTR_H
#define LOCK_PTR_H
#ifdef NDEBUG
#define LOCK_PTR_NDEBUG
#endif
#include <cassert>
#include <cstddef>
/**
\class lockPTR
This template is the standard safe-pointer implementation
of SYNOD.
In order for this scheme to work smoothly, the user has to take some
precautions:
1. each pointer should only be used ONCE to initialize a lockPTR.
2. The lockPTR assumes that there are no other access points to the
protected pointer.
3. The lockPTR can freely be copied and passed between objects and functions.
4. lockPTR objects should be used like the pointer to the object.
5. lockPTR objects should be passed as objects in function calls and
function return values.
6. There should be no pointers to lockPTR objects.
Class lockPTR is designed to behave just like the pointer would. You
can use the dereference operators (* and ->) to access the protected
object. However, the pointer itself is (with exceptions) never
exposed to the user.
Since all access to the referenced object is done via a lockPTR, it
is possible to maintain a count of all active references. If this
count dropts to zero, the referenced object can savely be destroyed.
For dynamically allocated objects, delete is envoked on the stored
pointer.
class lockPTR distinguishes between dynamically and automatically
allocated objects by the way it is initialised:
If a lockPTR is initialised with a pointer, it assumes that the
referenced object was dynamically allocated and will call the
destructor once the reference count drops to zero.
If the lockPTR is initialised with a reference, it assumes that the
object is automatically allocated. Thus, the lockPTR wil NOT call the
destructor.
In some cases it is necessary for a routine to actually get hold of
the pointer, contained in the lockPTR object. This can be done by
using the member function get(). After the pointer has been exposed
this way, the lockPTR will regard the referenced object as unsafe,
since the user might call delete on the pointer. Thus, lockPTR will
"lock" the referenced object and deny all further access.
The object can be unlocked by calling the unlock() member.
*/
template<class D>
class lockPTR
{
class PointerObject
{
private:
D *pointee; // pointer to handled Datum object
size_t number_of_references;
bool deletable;
bool locked;
// forbid this constructor!
PointerObject(PointerObject const&){assert(false);}
public:
PointerObject(D* p = NULL)
:pointee(p),
number_of_references(1),
deletable(true), locked(false){}
PointerObject(D& p_o)
:pointee(&p_o),
number_of_references(1),
deletable(false), locked(false){}
~PointerObject()
{
assert(number_of_references == 0); // This will invalidate the still
// existing pointer!
assert(!locked);
if((pointee != NULL) && deletable && (!locked))
delete pointee;
}
D * get(void) const
{
return pointee;
}
void addReference(void)
{
++number_of_references;
}
void removeReference(void)
{
// assert(number_of_references > 0);
--number_of_references;
if(number_of_references == 0)
{
delete this;
}
}
size_t references(void) const
{
return number_of_references;
}
bool islocked(void) const
{
return locked;
}
bool isdeletable(void) const
{
return deletable;
}
void lock(void)
{
assert(locked == false);
locked = true;
}
void unlock(void)
{
assert(locked == true);
locked = false;
}
};
PointerObject *obj;
public:
// lockPTR() ; // generated automatically.
// The default, argumentless constructor is used in
// class declarations and generates an empty lockPTR
// object which must then be initialised, for example
// by assignement.
explicit lockPTR(D* p=NULL)
{
obj = new PointerObject(p);
assert(obj != NULL);
}
explicit lockPTR(D& p_o)
{
obj = new PointerObject(p_o);
assert(obj != NULL);
}
lockPTR(const lockPTR<D>& spd)
: obj(spd.obj)
{
assert(obj != NULL);
obj->addReference();
}
virtual ~lockPTR()
{
assert(obj != NULL);
obj->removeReference();
}
lockPTR<D> operator=(const lockPTR<D>&spd)
{
// assert(obj != NULL);
// assert(spd.obj != NULL);
// The following order of the expressions protects
// against a=a;
spd.obj->addReference();
obj->removeReference();
obj = spd.obj;
return *this;
}
lockPTR<D> operator=(D &s)
{
*this = lockPTR<D>(s);
assert(!(obj->isdeletable()));
return *this;
}
lockPTR<D> operator=(D const &s)
{
*this = lockPTR<D>(s);
assert(!(obj->isdeletable()));
return *this;
}
D* get(void)
{
assert(!obj->islocked());
obj->lock(); // Try to lock Object
return obj->get();
}
D* get(void) const
{
assert(!obj->islocked());
obj->lock(); // Try to lock Object
return obj->get();
}
D* operator->() const
{
assert(obj->get() != NULL);
return obj->get();
}
D* operator->()
{
assert(obj->get() != NULL);
return obj->get();
}
D& operator*()
{
assert(obj->get() != NULL);
return *(obj->get());
}
const D& operator*() const
{
assert(obj->get() != NULL);
return *(obj->get());
}
bool operator!() const //!< returns true if and only if obj->pointee == NULL
{
// assert(obj != NULL);
return (obj->get() == NULL);
}
bool operator==(const lockPTR<D>& p) const
{
return (obj == p.obj);
}
bool operator!=(const lockPTR<D>& p) const
{
return (obj != p.obj);
}
bool valid(void) const //!< returns true if and only if obj->pointee != NULL
{
assert(obj != NULL);
return (obj->get() != NULL);
}
bool islocked(void) const
{
assert(obj != NULL);
return (obj->islocked());
}
bool deletable(void) const
{
assert(obj != NULL);
return (obj->isdeletable());
}
void lock(void) const
{
assert(obj!=NULL);
obj->lock();
}
void unlock(void) const
{
assert(obj!=NULL);
obj->unlock();
}
void unlock(void)
{
assert(obj!=NULL);
obj->unlock();
}
size_t references(void) const
{
return (obj==NULL)? 0: obj->references();
}
};
#ifndef LOCK_PTR_NDEBUG
#undef NDEBUG
#endif
#endif