ref.h
//
// ref.h --- definitions of the reference counting classes
//
// Copyright (C) 1996 Limit Point Systems, Inc.
//
// Author: Curtis Janssen <cljanss@limitpt.com>
// Maintainer: LPS
//
// This file is part of the SC Toolkit.
//
// The SC Toolkit is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// The SC Toolkit 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 Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with the SC Toolkit; see the file COPYING.LIB. If not, write to
// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
//
// The U.S. Government is granted a limited license as per AL 91-7.
//
// This is the main include file for the reference counting classes.
// This includes two other files: reftmpl.h and refmacr.h. The
// former is a template declaration for the reference counted classes
// and the latter is generated from the former by a perl script and
// provides CPP macros that declare reference counting classes.
//
// The behaviour of the package can be modified with the following five
// macros, each of which should be undefined, 0, or 1:
//
// REF_CHECK_STACK: If this is 1 referenced objects are checked to see if they
// reside on the stack, in which case storage for the object is not managed,
// if management is enabled. This feature can be confused by multiple threads
// and memory checking libraries.
//
// REF_MANAGE: If this is 1 the manage and unmanage members are enabled.
//
// REF_CHECK_MAX_NREF: If this is 1 the reference count is checked before
// it is incremented to make sure it isn't too big.
//
// REF_CHECK_MIN_NREF: If this is 1 the reference count is checked before
// it is decremented to make sure it isn't already zero.
//
// REF_USE_LOCKS: If this is 1 then critical regions are locked before they
// are entered. This prevents erroneous behavior when multiple threads
// share reference counted objects. This will slow down certain operations,
// so it should be set to 0 if your application does not need to be thread
// safe.
//
// If a macro is undefined, then the behaviour is architecture
// dependent--usually, the macro will be set to 1 in this case.
// For maximum efficiency and for normal operation after the program is
// debugged, compile with all of the above macros defined to zero.
// This can also be done with -DREF_OPTIMIZE.
//
// An include file can be used to set these options as well. This has
// the advantage that dependency checking will force an automatic
// recompile of all affected files if the options change. The file
// <scconfig.h> will be include if -DHAVE_CONFIG_H is specified.
//
// Note that all source code that uses references must be compiled with
// the same value REF_MANAGE. Changing this can change the storage layout
// and the interpretation of the reference count data.
#ifdef __GNUC__
#pragma interface
#endif
#ifndef _util_ref_ref_h
#define _util_ref_ref_h
#include <iostream>
#include <stdlib.h>
#include <limits.h>
#include <util/ref/identity.h>
#ifdef HAVE_CONFIG_H
#include <scconfig.h>
#endif
#ifdef REF_OPTIMIZE
#ifndef REF_CHECK_STACK
# define REF_CHECK_STACK 0
#endif
#ifndef REF_MANAGE
# define REF_MANAGE 0
#endif
#ifndef REF_CHECK_MAX_NREF
# define REF_CHECK_MAX_NREF 0
#endif
#ifndef REF_CHECK_MIN_NREF
# define REF_CHECK_MIN_NREF 0
#endif
#endif
#ifdef SUNMOS
#ifndef REF_CHECK_STACK
#define REF_CHECK_STACK 0
#endif
#else
#ifndef REF_CHECK_STACK
#define REF_CHECK_STACK 0
#endif
#endif
#ifndef REF_MANAGE
#define REF_MANAGE 1
#endif
#ifndef REF_CHECK_MAX_NREF
#define REF_CHECK_MAX_NREF 1
#endif
#ifndef REF_CHECK_MIN_NREF
#define REF_CHECK_MIN_NREF 1
#endif
#ifndef REF_USE_LOCKS
# if HAVE_STHREAD || HAVE_CREATETHREAD || HAVE_PTHREAD
# define REF_USE_LOCKS 1
# endif
#endif
#ifndef REF_ALWAYS_USE_LOCKS
# define REF_ALWAYS_USE_LOCKS 1
#endif
#if REF_CHECK_STACK
#include <unistd.h>
#ifndef HAVE_SBRK_DEC
extern "C" void * sbrk(ssize_t);
#endif
#define DO_REF_CHECK_STACK(p) (((void*) (p) > sbrk(0)) && (p)->managed())
#else // REF_CHECK_STACK
#define DO_REF_CHECK_STACK(p) (0)
#endif // REF_CHECK_STACK
#if REF_MANAGE
#define DO_REF_UNMANAGE(p) ((p)->unmanage())
#else // REF_MANAGE
#define DO_REF_UNMANAGE(p)
#endif // REF_MANAGE
#if REF_USE_LOCKS
#define __REF_LOCK__(p) p->lock_ptr()
#define __REF_UNLOCK__(p) p->unlock_ptr()
#if REF_ALWAYS_USE_LOCKS
#define __REF_INITLOCK__() use_locks(true)
#else
#define __REF_INITLOCK__() ref_lock_ = 0xff
#endif
#else
#define __REF_LOCK__(p)
#define __REF_UNLOCK__(p)
#define __REF_INITLOCK__()
#endif
namespace sc {
typedef unsigned long refcount_t;
class RefCount: public Identity {
private:
#if REF_MANAGE
# define REF_MAX_NREF (UINT_MAX - 1)
# define REF_MANAGED_CODE UINT_MAX
#else
# define REF_MAX_NREF UINT_MAX
#endif
unsigned int _reference_count_;
#if REF_USE_LOCKS
unsigned char ref_lock_;
#endif
void error(const char*) const;
void too_many_refs() const;
void not_enough_refs() const;
protected:
RefCount(): _reference_count_(0) {
__REF_INITLOCK__();
//std::cout << "ref_lock_ = " << (int) ref_lock_ << std::endl;
}
RefCount(const RefCount&): _reference_count_(0) {
__REF_INITLOCK__();
//std::cout << "ref_lock_ = " << (int) ref_lock_ << std::endl;
}
// Assigment should not overwrite the reference count.
RefCount& operator=(const RefCount&) { return *this; }
public:
virtual ~RefCount();
int lock_ptr() const;
int unlock_ptr() const;
void use_locks(bool inVal);
refcount_t nreference() const {
# if REF_MANAGE
if (!managed()) return 1;
# endif
return _reference_count_;
}
refcount_t reference() {
# if REF_MANAGE
if (!managed()) return 1;
# endif
__REF_LOCK__(this);
# if REF_CHECK_MAX_NREF
if (_reference_count_ >= REF_MAX_NREF) too_many_refs();
# endif
_reference_count_++;
refcount_t r = _reference_count_;
__REF_UNLOCK__(this);
return r;
}
refcount_t dereference() {
# if REF_MANAGE
if (!managed()) return 1;
# endif
__REF_LOCK__(this);
# if REF_CHECK_MIN_NREF
if (_reference_count_ == 0) not_enough_refs();
# endif
_reference_count_--;
refcount_t r = _reference_count_;
__REF_UNLOCK__(this);
return r;
}
#if REF_MANAGE
int managed() const {
return _reference_count_ != REF_MANAGED_CODE;
}
void unmanage() {
_reference_count_ = REF_MANAGED_CODE;
}
#else // REF_MANAGE
int managed() const { return 1; }
#endif // REF_MANAGE
};
class RefBase {
protected:
void warn ( const char * msg) const;
void warn_ref_to_stack() const;
void warn_skip_stack_delete() const;
void warn_bad_ref_count() const;
void ref_info(RefCount*p,std::ostream& os) const;
void ref_info(std::ostream& os) const;
void check_pointer() const;
void reference(RefCount *);
int dereference(RefCount *);
public:
RefBase() {};
virtual ~RefBase();
virtual RefCount* parentpointer() const = 0;
void require_nonnull() const;
};
template <class T>
class Ref : public RefBase {
private:
T* p;
public:
Ref(): p(0) {}
Ref(T*a) : p(0)
{
if (a) {
p = a;
reference(p);
}
}
Ref(const Ref<T> &a) : p(0)
{
if (a.pointer()) {
p = a.pointer();
reference(p);
}
}
template <class A> Ref(const Ref<A> &a): p(0)
{
if (a.pointer()) {
p = a.pointer();
reference(p);
}
}
// /** Create a reference to the object a. Do a
// dynamic_cast to convert a to the appropiate type. */
// Ref(const RefBase&a) {
// p = dynamic_cast<T*>(a.parentpointer());
// reference(p);
// }
// /** Create a reference to the object a. Do a
// dynamic_cast to convert a to the appropiate type. */
// Ref(RefCount*a): p(0) {
// operator<<(a);
// }
~Ref()
{
clear();
}
T* operator->() const { return p; }
T* pointer() const { return p; }
RefCount *parentpointer() const { return p; }
operator T*() const { return p; }
T& operator *() const { return *p; };
int null() const { return p == 0; }
int nonnull() const { return p != 0; }
template <class A> int operator==(const Ref<A>&a) const
{ return eq(p,a.pointer()); }
template <class A> int operator>=(const Ref<A>&a) const
{ return ge(p,a.pointer()); }
template <class A> int operator<=(const Ref<A>&a) const
{ return le(p,a.pointer()); }
template <class A> int operator>(const Ref<A>&a) const
{ return gt(p,a.pointer()); }
template <class A> int operator<(const Ref<A>&a) const
{ return lt(p,a.pointer()); }
template <class A> int operator!=(const Ref<A>&a) const
{ return ne(p,a.pointer()); }
int compare(const Ref<T> &a) const {
return eq(p,a.p)?0:((lt(p,a.p)?-1:1));
}
void clear()
{
if (p) {
int ref = dereference(p);
if (ref == 0)
delete p;
p = 0;
}
}
Ref<T>& operator=(const Ref<T> & c)
{
T *cp = c.pointer();
if (cp) {
cp->reference();
clear();
p=cp;
}
else {
clear();
}
return *this;
}
template <class A> Ref<T>& operator=(const Ref<A> & c)
{
A *cp = c.pointer();
if (cp) {
cp->reference();
clear();
p=cp;
}
else {
clear();
}
return *this;
}
Ref<T>& operator<<(const RefBase&a) {
T* cr = dynamic_cast<T*>(a.parentpointer());
if (cr) {
reference(cr);
clear();
}
p = cr;
return *this;
}
Ref<T>& operator<<(RefCount *a) {
T* cr = dynamic_cast<T*>(a);
if (cr) assign_pointer(cr);
else if (a && a->nreference() <= 0) delete a;
return *this;
}
Ref<T>& operator=(T* cr)
{
assign_pointer(cr);
return *this;
}
void assign_pointer(T* cr)
{
if (cr) {
if (DO_REF_CHECK_STACK(cr)) {
DO_REF_UNMANAGE(cr);
warn_ref_to_stack();
}
cr->reference();
}
clear();
p = cr;
}
void check_pointer() const
{
if (p && p->nreference() <= 0) {
warn_bad_ref_count();
}
}
void ref_info(std::ostream& os) const
{
RefBase::ref_info(p,os);
}
void warn(const char*s) const { RefBase::warn(s); }
};
}
#endif
// ///////////////////////////////////////////////////////////////////////////
// Local Variables:
// mode: c++
// c-file-style: "CLJ"
// End: