/**
 * @file
 * $Id: uid.h 3010 2006-12-16 18:01:08Z plasmahh $
 * $Revision: 3010 $
 * $Author: plasmahh $
 * $Date: 2006-12-16 19:01:08 +0100 (Sa, 16 Dez 2006) $
 *
 * This file is part of The iWear Framework.
 * In particular this file is part of the Framework Core Library
 *
 * The iWear Framework 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 as in version 2 of the License.

 * 
 * The iWear Framework 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
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWEAR_INTRUSIVE_PTR_BASE_H
#define __IWEAR_INTRUSIVE_PTR_BASE_H

#include "iwear/boost_atomic.h"

namespace iwear 
{

/**
 * To prevent surprises, always derive virtually.
 */
class intrusive_ptr_base
{
private:
    friend void intrusive_ptr_add_ref( const intrusive_ptr_base* rc );
    friend void intrusive_ptr_release( const intrusive_ptr_base* rc );
    mutable int refcount;
public:
    intrusive_ptr_base( ) : refcount(0) { }
    /**
     * Creating a new and fresh object has a refcount of 0, no matter if it was
     * created from another copy.
     */
    intrusive_ptr_base( const intrusive_ptr_base& ) : refcount(0) { }

    /**
     * Assigning does not assign the reference count, since it is valid for all
     * the intrusive_ptrs that are there, not for the logical content of the object.
     */
    intrusive_ptr_base& operator=( const intrusive_ptr_base& ) { return *this; }

    /**
     * Unfortunately we need this to be virtual for the delete to function
     * properly.
     */
    virtual ~intrusive_ptr_base() { }
};

inline void intrusive_ptr_release( const intrusive_ptr_base* rc )
{
    if( ! rc ) return; // We dont care if the ptr is 0, really...

    if( atomic_exchange_and_add(rc->refcount,-1) == 1 )
    {
	// that means before adding -1 (e.g. decrementing by 1) the value was
	// 1, so now its 0, meaning that we dropped to 0 and have to delete.

	// A possible problem could be theoretically that just in this moment
	// somewhere else the count should be increased. But this is
	// impossible. Here we are only when the last intrusive_ptr is being
	// destroyed, but during that no new one (which could increment it)
	// could ever be created in a sane program.
	delete rc;
    }
}

inline void intrusive_ptr_add_ref( const intrusive_ptr_base* rc )
{
    if( ! rc ) return; // We dont care if the ptr is 0, really...
    atomic_increment(rc->refcount);
}


}

#endif
