You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
6.3 KiB
C

/**
* object.h defines the layout of the object header for GCKit-managed objects.
* These objects are allocated with GCAllocateObject() and are not freed by
* code outside of GCKit.
*/
/**
* GCINLINEPUBLIC functions are functions that are inline for GCKit but
* exported symbols for the rest of the world.
*/
#ifndef GCINLINEPUBLIC
#define GCINLINEPUBLIC inline static
#endif
/**
* GCINLINEPRIVATE functions are inline in GCKit and are not exported.
*/
#define GCINLINEPRIVATE inline static __attribute__((unused))
/**
* Modified version of the object header. Stores a 16-bit reference count and
* a 16-bit flags field. Three bits of the flags are used for the object
* colour and one to indicate if it is buffered.
*
* Note: On 64-bit platforms we have to add some padding, so it might be better
* to make the ref countfields bigger.
*/
__attribute__((packed))
struct gc_object_header
{
/**
* Garbage collection Flags associated with this object. This includes the
* object's colour while performing cycle detection. */
char flags;
/**
* Number of weak references held to this object. An object may be
* finalized, but may not be deleted while weak references are held to it.
*/
char weak_ref_count;
/**
* Number of strong references to the object. This count is modified by
* GCRetain() and GCRelease(). When it reaches 0, the object has no strong
* references to it. It may, however, have references from the stack or
* traced memory. When the strong reference count reaches 0, the object
* will be added to the trace pile.
*/
short strong_ref_count;
/**
* The allocation zone for this object. This is an opaque pointer from the
* perspective of GCKit. In GNUstep, this will be an NSZone.
*/
void *zone;
};
__attribute__((packed))
struct gc_buffer_header
{
size_t size;
struct gc_object_header object_header;
};
/**
* Cycle detection is a graph colouring algorithm. This type specifies the
* possible colours.
*/
typedef enum
{
/** Acyclic */
GCColourGreen = 0,
/** In use or free. */
GCColourBlack = 1,
/** Possible member of a cycle. */
GCColourGrey = 2,
/** Member of a garbage cycle. */
GCColourWhite = 3,
/** Potential root of a cycle. */
GCColourPurple = 4,
/** Object currently being freed. */
GCColourOrange = 5,
/** Object is a member of a cycle to be freed when the last traced
* reference is removed, or resurrected if retained. */
GCColourRed = 6
} GCColour;
typedef enum
{
/** Set when the object has been added to the potential-garbage list. */
GCFlagBuffered = (1<<3),
/** Set when an object has been assigned on a traced part of the heap. */
GCFlagEscaped = (1<<4),
/** Visited by the tracing code. */
GCFlagVisited = (1<<5),
/** This object is a memory buffer, not an Objective-C object. */
GCFlagNotObject = (1<<6),
/** Object uses CoreFoundation-style semantics and won't ever by traced. */
GCFlagCFObject = (1<<7)
} GCFlag;
/**
* Debugging function used to return a colour as a human-readable string.
*/
__attribute__((unused))
inline static const char *GCStringFromColour(GCColour aColour)
{
switch(aColour)
{
case GCColourBlack: return "black";
case GCColourGrey: return "grey";
case GCColourWhite: return "white";
case GCColourPurple: return "purple";
case GCColourGreen: return "green";
case GCColourOrange: return "orange";
case GCColourRed: return "red";
}
return "unknown";
}
GCINLINEPRIVATE struct gc_object_header*GCHeaderForObject(id anObject)
{
return &((struct gc_object_header*)anObject)[-1];
}
GCINLINEPRIVATE struct gc_buffer_header*GCHeaderForBuffer(id anObject)
{
return &((struct gc_buffer_header*)anObject)[-1];
}
/**
* Returns the flags for a specified object.
*/
GCINLINEPRIVATE unsigned short GCObjectFlags(id anObject)
{
return GCHeaderForObject(anObject)->flags;
}
/**
* Returns the colour of the specified object.
*/
GCINLINEPRIVATE GCColour GCColourOfObject(id anObject)
{
// Lowest 3 bits of the flags field contain the colour.
return GCObjectFlags(anObject) & 0x7;
}
/**
* Tries to set the flags for a given object. Returns the old value.
*/
GCINLINEPRIVATE unsigned short GCTrySetFlags(id anObject, unsigned char old,
unsigned char value)
{
return __sync_bool_compare_and_swap(
&(((struct gc_object_header*)anObject)[-1].flags), old, value);
}
/**
* Sets the colour of the specified object, returning the old colour
*/
GCINLINEPRIVATE GCColour GCSetColourOfObject(id anObject, GCColour colour)
{
char oldFlags;
char newFlags;
do
{
oldFlags = GCObjectFlags(anObject);
newFlags = oldFlags;
// Clear the old colour.
newFlags &= 0xf8;
// Set the new colour
newFlags |= colour;
} while(!GCTrySetFlags(anObject, oldFlags, newFlags));
return oldFlags & 0x7;
}
/**
* Sets the specified flag for a given object.
*/
GCINLINEPRIVATE void GCSetFlag(id anObject, GCFlag flag)
{
unsigned oldFlags;
unsigned newFlags;
do
{
oldFlags = GCObjectFlags(anObject);
newFlags = oldFlags;
newFlags |= flag;
} while(!GCTrySetFlags(anObject, oldFlags, newFlags));
}
/**
* Clears the specified flag on an object.
*/
GCINLINEPRIVATE void GCClearFlag(id anObject, GCFlag flag)
{
unsigned oldFlags;
unsigned newFlags;
do
{
oldFlags = GCObjectFlags(anObject);
newFlags = oldFlags;
newFlags &= ~flag;
} while(!GCTrySetFlags(anObject, oldFlags, newFlags));
}
/**
* Returns whether the specified object's buffered flag is set.
*/
GCINLINEPRIVATE BOOL GCTestFlag(id anObject, GCFlag flag)
{
return GCObjectFlags(anObject) & flag;
}
GCINLINEPUBLIC long GCGetRetainCount(id anObject)
{
unsigned short refcount = ((struct gc_object_header*)anObject)[-1].strong_ref_count;
return (long) refcount;
}
GCINLINEPRIVATE long GCDecrementRetainCount(id anObject)
{
return __sync_sub_and_fetch(&(GCHeaderForObject(anObject)->strong_ref_count), 1);
}
GCINLINEPRIVATE long GCIncrementRetainCount(id anObject)
{
return __sync_add_and_fetch(&(GCHeaderForObject(anObject)->strong_ref_count), 1);
}
GCINLINEPUBLIC long GCGetWeakRefCount(id anObject)
{
unsigned short refcount = ((struct gc_object_header*)anObject)[-1].weak_ref_count;
return (long) refcount;
}
GCINLINEPRIVATE long GCDecrementWeakCount(id anObject)
{
return __sync_sub_and_fetch(&(GCHeaderForObject(anObject)->weak_ref_count), 1);
}
GCINLINEPRIVATE long GCIncrementWeakCount(id anObject)
{
return __sync_add_and_fetch(&(GCHeaderForObject(anObject)->weak_ref_count), 1);
}