#ifndef RTREFCNT_OBJECT_H
#define RTREFCNT_OBJECT_H
/******************************************************************************/
/******************************************************************************/
/*****************     Copyright 2004, 2005 Chronolytics Inc.   ***************/
/*****************     All Right Reserved                       ***************/
/******************************************************************************/
/******************************************************************************/

#include <stddef.h>
#include <RTMagic.h>
#include <RTCommon.h>
#include <RTAtomic.h>
#ifdef DEBUG
#include <RTLog.h>
#endif /* DEBUG */

static volatile const char __attribute__ ((unused)) rcsid_RTREFCNT_OBJECT_H[] = "$Id: RTRefCnt_Object.h,v 1.7 2005/06/29 15:53:29 dave Exp $";

typedef void RTRefCnt_Object;  /* return void * pointer to generic object */

#ifndef FAILS
#define FAILS (-1)
#endif /* FAILS */

#define RTRefCnt_Object_MAGIC MAGIC("RCnt")
#define RTRefCnt_Zero MAGIC("RC=0")

#ifndef unlikely
#ifdef __GNUC__
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x)  __builtin_expect(!!(x), 0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
#endif

/*
** NOTE: the destructor should destroy the content of the object.
** Freeing the actual storage of the object is controlled by RTRefCnt_Object.
*/

  /* used to pass a destructor for content of RefCnt_Object */
typedef void (*RTRefCnt_Destructor)(void *object);

RTRefCnt_Object *RTRefCnt_Constructor(int size, RTRefCnt_Destructor destructor);

typedef struct {
   RTMagic magic;  /* our magic */
   atomic_t rc;  /* reference count */
   void (*destructor)(void *);  /* destructor to call */
} PrivRefCnt;

#define RTREFCNTMAGIC MAGIC("RCnt")

  /* NOT FOR USER APPS:  very private call  */
#define RCPRIV_FROM_RCOBJ(x) (((PrivRefCnt *)(x)) - 1) /* go back 1 */  
  /* takes a validated RefCnt Private type */

static __inline__ int RTRefCnt_Validate(RTRefCnt_Object *rc_obp)
   {
   if (unlikely(!rc_obp))  return FAILS;

   return (RCPRIV_FROM_RCOBJ(rc_obp)->magic == RTREFCNTMAGIC) ? 0 : FAILS;
}


static __inline__ RTRefCnt_Object *RTRefCnt_Acquire(RTRefCnt_Object *rc_obp)
   {
   PrivRefCnt *prcp  = ((PrivRefCnt *)rc_obp) - 1;

   if (unlikely(!rc_obp) || unlikely(RTRefCnt_Validate(rc_obp) == FAILS)) return NULL;

   atomic_inc(&(RCPRIV_FROM_RCOBJ(rc_obp)->rc));

   return rc_obp;
}

#ifdef DEBUG
#define DEBUG_RELEASE(ptr) \
   do  {  \
     char magic[5]; \
     *(unsigned long *) magic = *(unsigned long *)(ptr); \
     magic[4] = '\0';  \
     RTLog(LOGCOMMON|LOGDEBUG, "RTRefCnt == 0: Freeing 0x%x magic %s.", (ptr), magic);  \
   } while (0)
#else
#define DEBUG_RELEASE(ptr) \
   do { } while(0)
#endif 


static void __rc_free(RTRefCnt_Object *rcobp)
  {
   extern void RTRefCnt_Free(void *refcntp);  /* NOT FOR APPS: free container internal to RTRefCnt */
  DEBUG_RELEASE(rcobp);
  if (RCPRIV_FROM_RCOBJ(rcobp)->destructor) RCPRIV_FROM_RCOBJ(rcobp)->destructor(rcobp);
  RTRefCnt_Free(RCPRIV_FROM_RCOBJ(rcobp));
}

static __inline__ void RTRefCnt_Release(RTRefCnt_Object **rc_obpp)
   {
   if ((unlikely(!rc_obpp)) || unlikely(RTRefCnt_Validate(*rc_obpp) == FAILS)) return; 

   if (unlikely(atomic_dec_and_test(&(RCPRIV_FROM_RCOBJ(*rc_obpp)->rc)))) { /* reference count is zero */
      __rc_free(*rc_obpp);
      *rc_obpp = NULL;  /* wiped clean */
   }
}

#endif /*  RTREFCNT_OBJECT_H */

