581 lines
20 KiB
C++
581 lines
20 KiB
C++
#ifndef HX_GC_H
|
|
#define HX_GC_H
|
|
|
|
#include <hx/Tls.h>
|
|
#include <stdio.h>
|
|
|
|
// Under the current scheme (as defined by HX_HCSTRING/HX_CSTRING in hxcpp.h)
|
|
// each constant string data is prepended with a 4-byte header that says the string
|
|
// is constant (ie, not part of GC) and whether there is(not) a pre-computed hash at
|
|
// the end of the data.
|
|
// When HX_SMART_STRINGS is active, a bit says whether it is char16_t encoded.
|
|
|
|
#define HX_GC_CONST_ALLOC_BIT 0x80000000
|
|
#define HX_GC_CONST_ALLOC_MARK_BIT 0x80
|
|
|
|
|
|
|
|
|
|
// Tell compiler the extra functions are supported
|
|
#define HXCPP_GC_FUNCTIONS_1
|
|
|
|
// Function called by the haxe code...
|
|
|
|
#ifdef HXCPP_TELEMETRY
|
|
extern void __hxt_gc_new(hx::StackContext *inStack, void* obj, int inSize, const char *inName);
|
|
#endif
|
|
|
|
|
|
// Helpers for debugging code
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_reachable(hx::Object *inKeep);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_enable(bool inEnable);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_collect(bool inMajor=true);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_gc_compact();
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES int __hxcpp_gc_trace(hx::Class inClass, bool inPrint);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES int __hxcpp_gc_used_bytes();
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES double __hxcpp_gc_mem_info(int inWhat);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_enter_gc_free_zone();
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_exit_gc_free_zone();
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_gc_safe_point();
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_spam_collects(int inEveryNCalls);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_set_minimum_working_memory(int inBytes);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_set_minimum_free_space(int inBytes);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_set_target_free_space_percentage(int inPercentage);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES bool __hxcpp_is_const_string(const ::String &inString);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES Dynamic _hx_gc_freeze(Dynamic inObject);
|
|
|
|
typedef void (hx::Object::*_hx_member_finalizer)(void);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_add_member_finalizer(hx::Object *inObject, _hx_member_finalizer, bool inPin);
|
|
|
|
typedef void (*_hx_alloc_finalizer)(void *inPtr);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void __hxcpp_add_alloc_finalizer(void *inAlloc, _hx_alloc_finalizer, bool inPin);
|
|
|
|
template<typename T>
|
|
inline void _hx_add_finalizable( hx::ObjectPtr<T> inObj, bool inPin)
|
|
{
|
|
_hx_member_finalizer finalizer = (_hx_member_finalizer)&T::finalize;
|
|
__hxcpp_add_member_finalizer(inObj.mPtr, finalizer, inPin);
|
|
}
|
|
template<typename T>
|
|
inline void _hx_add_finalizable( T *inObj, bool inPin)
|
|
{
|
|
_hx_member_finalizer finalizer = (_hx_member_finalizer)&T::finalize;
|
|
__hxcpp_add_member_finalizer(inObj, finalizer, inPin);
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
T _hx_allocate_extended(int inExtra)
|
|
{
|
|
typedef typename T::Obj Obj;
|
|
Obj *obj = new (inExtra) Obj();
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
template<typename T>
|
|
inline void _hx_allocate_extended( hx::ObjectPtr<T> inObj, bool inPin)
|
|
*/
|
|
|
|
|
|
// Finalizers from haxe code...
|
|
void __hxcpp_gc_do_not_kill(Dynamic inObj);
|
|
|
|
// This is the correctly typed version - no change of getting function proto wrong
|
|
void _hx_set_finalizer(Dynamic inObj, void (*inFunc)(Dynamic) );
|
|
|
|
void __hxcpp_set_finalizer(Dynamic inObj, void *inFunction);
|
|
hx::Object *__hxcpp_get_next_zombie();
|
|
|
|
#ifdef HXCPP_TELEMETRY
|
|
void __hxcpp_set_hxt_finalizer(void* inObj, void *inFunc);
|
|
#endif
|
|
|
|
hx::Object *__hxcpp_weak_ref_create(Dynamic inObject);
|
|
hx::Object *__hxcpp_weak_ref_get(Dynamic inRef);
|
|
|
|
|
|
unsigned int __hxcpp_obj_hash(Dynamic inObj);
|
|
int __hxcpp_obj_id(Dynamic inObj);
|
|
hx::Object *__hxcpp_id_obj(int);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace hx
|
|
{
|
|
// Generic allocation routine.
|
|
// If inSize is small (<4k) it will be allocated from the immix pool.
|
|
// Larger, and it will be allocated from a separate memory pool
|
|
// inIsObject specifies whether "__Mark" should be called on the resulting object
|
|
void *InternalNew(int inSize,bool inIsObject);
|
|
|
|
// Used internall - realloc array data
|
|
void *InternalRealloc(int inFromSize, void *inData,int inSize,bool inAllowExpansion=false);
|
|
|
|
void InternalReleaseMem(void *inMem);
|
|
|
|
unsigned int ObjectSizeSafe(void *inData);
|
|
|
|
// Const buffers are allocated outside the GC system, and do not require marking
|
|
// String buffers can optionally have a pre-computed hash appended with this method
|
|
void *InternalCreateConstBuffer(const void *inData,int inSize,bool inAddStringHash=false);
|
|
|
|
// Called after collection by an unspecified thread
|
|
typedef void (*finalizer)(hx::Object *v);
|
|
|
|
// Used internally by the runtime.
|
|
// The constructor will add this object to the internal list of finalizers.
|
|
// If the parent object is not marked by the end of the collect, the finalizer will trigger.
|
|
struct InternalFinalizer
|
|
{
|
|
InternalFinalizer(hx::Object *inObj, finalizer inFinalizer=0);
|
|
|
|
#ifdef HXCPP_VISIT_ALLOCS
|
|
void Visit(VisitContext *__inCtx);
|
|
#endif
|
|
void Detach();
|
|
|
|
bool mValid;
|
|
finalizer mFinalizer;
|
|
hx::Object *mObject;
|
|
};
|
|
|
|
// Attach a finalizer to any object allocation. This can be called from haxe code, but be aware that
|
|
// you can't make any GC calls from the finalizer.
|
|
void GCSetFinalizer( hx::Object *, hx::finalizer f );
|
|
|
|
// If another thread wants to do a collect, it will signal this variable.
|
|
// This automatically gets checked when you call "new", but if you are in long-running
|
|
// loop with no new call, you might starve another thread if you to not check this.
|
|
// 0xffffffff = pause requested
|
|
extern int gPauseForCollect;
|
|
|
|
|
|
// Minimum total memory - used + buffer for new objects
|
|
extern int sgMinimumWorkingMemory;
|
|
|
|
// Minimum free memory - not counting used memory
|
|
extern int sgMinimumFreeSpace;
|
|
|
|
// Also ensure that the free memory is larger than this amount of used memory
|
|
extern int sgTargetFreeSpacePercentage;
|
|
|
|
|
|
extern HXCPP_EXTERN_CLASS_ATTRIBUTES int gByteMarkID;
|
|
|
|
// Call in response to a gPauseForCollect. Normally, this is done for you in "new"
|
|
void PauseForCollect();
|
|
|
|
|
|
// Used by WeakHash to work out if it needs to dispose its keys
|
|
bool IsWeakRefValid(hx::Object *inPtr);
|
|
bool IsWeakRefValid(const HX_CHAR *inPtr);
|
|
|
|
// Used by CFFI to scan a block of memory for GC Pointers. May picks up random crap
|
|
// that points to real, active objects.
|
|
void MarkConservative(int *inBottom, int *inTop,hx::MarkContext *__inCtx);
|
|
|
|
|
|
// Create/Remove a root.
|
|
// All statics are explicitly registered - this saves adding the whole data segment
|
|
// to the collection list.
|
|
// It takes a pointer-pointer so it can move the contents, and the caller can change the contents
|
|
void GCAddRoot(hx::Object **inRoot);
|
|
void GCRemoveRoot(hx::Object **inRoot);
|
|
|
|
|
|
// This is used internally in hxcpp
|
|
// It calls InternalNew, and takes care of null-terminating the result
|
|
char *NewString(int inLen);
|
|
|
|
// The concept of 'private' is from the old conservative Gc method.
|
|
// Now with explicit marking, these functions do the same thing, which is
|
|
// to allocate some GC memory and optionally copy the 'inData' into those bytes
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void *NewGCBytes(void *inData,int inSize);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void *NewGCPrivate(void *inData,int inSize);
|
|
|
|
// Force a collect from the calling thread
|
|
// Only one thread should call this at a time
|
|
int InternalCollect(bool inMajor,bool inCompact);
|
|
|
|
|
|
// Disable the garbage collector. It will try to increase its internal buffers to honour extra requests.
|
|
// If it runs out of memory, it will actually try to do a collect.
|
|
void InternalEnableGC(bool inEnable);
|
|
|
|
// Record that fact that external memory has been allocated and associated with a haxe object
|
|
// eg. BitmapData. This will help the collector know when to collect
|
|
void GCChangeManagedMemory(int inDelta, const char *inWhy=0);
|
|
|
|
// Haxe threads can center GC free zones, where they can't make GC allocation calls, and should not mess with GC memory.
|
|
// This means that they do not need to pause while the GC collections happen, and other threads will not
|
|
// wait for them to "check in" before collecting. The standard runtime makes these calls around OS calls, such as "Sleep"
|
|
void EnterGCFreeZone();
|
|
void ExitGCFreeZone();
|
|
// retuns true if ExitGCFreeZone should be called
|
|
bool TryGCFreeZone();
|
|
// retuns true if ExitGCFreeZone was called
|
|
bool TryExitGCFreeZone();
|
|
|
|
class HXCPP_EXTERN_CLASS_ATTRIBUTES AutoGCFreeZone
|
|
{
|
|
public:
|
|
AutoGCFreeZone() : locked(true) { EnterGCFreeZone(); }
|
|
~AutoGCFreeZone() { if (locked) ExitGCFreeZone(); }
|
|
|
|
void close() { if (locked) ExitGCFreeZone(); locked = false; }
|
|
|
|
bool locked;
|
|
};
|
|
|
|
|
|
// Defined in Class.cpp, these function is called from the Gc to start the marking/visiting
|
|
void MarkClassStatics(hx::MarkContext *__inCtx);
|
|
#ifdef HXCPP_VISIT_ALLOCS
|
|
void VisitClassStatics(hx::VisitContext *__inCtx);
|
|
#endif
|
|
|
|
|
|
// Called by haxe/application code to mark allocations.
|
|
// "Object" allocs will recursively call __Mark
|
|
inline void MarkAlloc(void *inPtr ,hx::MarkContext *__inCtx);
|
|
inline void MarkObjectAlloc(hx::Object *inPtr ,hx::MarkContext *__inCtx);
|
|
|
|
// Implemented differently for efficiency
|
|
void MarkObjectArray(hx::Object **inPtr, int inLength, hx::MarkContext *__inCtx);
|
|
void MarkStringArray(String *inPtr, int inLength, hx::MarkContext *__inCtx);
|
|
|
|
// Provide extra debug info to the marking routines
|
|
#ifdef HXCPP_DEBUG
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void MarkSetMember(const char *inName ,hx::MarkContext *__inCtx);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void MarkPushClass(const char *inName ,hx::MarkContext *__inCtx);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void MarkPopClass(hx::MarkContext *__inCtx);
|
|
#endif
|
|
|
|
|
|
// Used by runtime if it is being paranoid about pointers. It checks that the pointer is real and alive at last collect.
|
|
void GCCheckPointer(void *);
|
|
void GCOnNewPointer(void *);
|
|
|
|
|
|
// Called internally before and GC operations
|
|
void CommonInitAlloc();
|
|
|
|
|
|
// Threading ...
|
|
void RegisterNewThread(void *inTopOfStack);
|
|
void RegisterCurrentThread(void *inTopOfStack);
|
|
void UnregisterCurrentThread();
|
|
void GCPrepareMultiThreaded();
|
|
|
|
|
|
|
|
|
|
} // end namespace hx
|
|
|
|
|
|
// Inline code tied to the immix implementation
|
|
|
|
namespace hx
|
|
{
|
|
|
|
#define HX_USE_INLINE_IMMIX_OPERATOR_NEW
|
|
|
|
//#define HX_STACK_CTX ::hx::ImmixAllocator *_hx_stack_ctx = hx::gMultiThreadMode ? hx::tlsImmixAllocator : hx::gMainThreadAlloc;
|
|
|
|
|
|
// Each line ast 128 bytes (2^7)
|
|
#define IMMIX_LINE_BITS 7
|
|
#define IMMIX_LINE_LEN (1<<IMMIX_LINE_BITS)
|
|
|
|
#define HX_GC_REMEMBERED 0x40
|
|
|
|
// The size info is stored in the header 8 bits to the right
|
|
#define IMMIX_ALLOC_SIZE_SHIFT 6
|
|
|
|
// Indicates that __Mark must be called recursively
|
|
#define IMMIX_ALLOC_IS_CONTAINER 0x00800000
|
|
// String is char16_t type
|
|
#define HX_GC_STRING_CHAR16_T 0x00200000
|
|
// String has hash data at end
|
|
#define HX_GC_STRING_HASH 0x00100000
|
|
|
|
#define HX_GC_STRING_HASH_BIT 0x10
|
|
|
|
#ifdef HXCPP_BIG_ENDIAN
|
|
#define HX_GC_STRING_HASH_OFFSET -3
|
|
#define HX_GC_CONST_ALLOC_MARK_OFFSET -4
|
|
#define HX_ENDIAN_MARK_ID_BYTE -4
|
|
#else
|
|
#define HX_GC_STRING_HASH_OFFSET -2
|
|
#define HX_GC_CONST_ALLOC_MARK_OFFSET -1
|
|
#define HX_ENDIAN_MARK_ID_BYTE -1
|
|
#endif
|
|
|
|
|
|
|
|
// The gPauseForCollect bits will turn spaceEnd negative, and so force the slow path
|
|
#ifndef HXCPP_SINGLE_THREADED_APP
|
|
#define WITH_PAUSE_FOR_COLLECT_FLAG | hx::gPauseForCollect
|
|
#else
|
|
#define WITH_PAUSE_FOR_COLLECT_FLAG
|
|
#endif
|
|
|
|
|
|
|
|
struct StackContext;
|
|
|
|
EXTERN_FAST_TLS_DATA(StackContext, tlsStackContext);
|
|
|
|
extern StackContext *gMainThreadContext;
|
|
|
|
extern unsigned int gImmixStartFlag[128];
|
|
extern int gMarkID;
|
|
extern int gMarkIDWithContainer;
|
|
extern void BadImmixAlloc();
|
|
|
|
|
|
class ImmixAllocator
|
|
{
|
|
public:
|
|
virtual ~ImmixAllocator() {}
|
|
virtual void *CallAlloc(int inSize,unsigned int inObjectFlags) = 0;
|
|
virtual void SetupStackAndCollect(bool inMajor, bool inForceCompact, bool inLocked=false,bool inFreeIsFragged=false) = 0;
|
|
|
|
#ifdef HXCPP_GC_NURSERY
|
|
unsigned char *spaceFirst;
|
|
unsigned char *spaceOversize;
|
|
#else
|
|
int spaceStart;
|
|
int spaceEnd;
|
|
#endif
|
|
unsigned int *allocStartFlags;
|
|
unsigned char *allocBase;
|
|
|
|
|
|
|
|
// These allocate the function using the garbage-colleced malloc
|
|
inline static void *alloc(ImmixAllocator *alloc, size_t inSize, bool inContainer, const char *inName )
|
|
{
|
|
#ifdef HXCPP_GC_NURSERY
|
|
|
|
unsigned char *buffer = alloc->spaceFirst;
|
|
unsigned char *end = buffer + (inSize + 4);
|
|
|
|
if ( end > alloc->spaceOversize )
|
|
{
|
|
// Fall back to external method
|
|
buffer = (unsigned char *)alloc->CallAlloc(inSize, inContainer ? IMMIX_ALLOC_IS_CONTAINER : 0);
|
|
}
|
|
else
|
|
{
|
|
alloc->spaceFirst = end;
|
|
|
|
if (inContainer)
|
|
((unsigned int *)buffer)[-1] = inSize | IMMIX_ALLOC_IS_CONTAINER;
|
|
else
|
|
((unsigned int *)buffer)[-1] = inSize;
|
|
}
|
|
|
|
#ifdef HXCPP_TELEMETRY
|
|
__hxt_gc_new((hx::StackContext *)alloc,buffer, inSize, inName);
|
|
#endif
|
|
|
|
return buffer;
|
|
|
|
#else
|
|
#ifndef HXCPP_ALIGN_ALLOC
|
|
// Inline the fast-path if we can
|
|
// We know the object can hold a pointer (vtable) and that the size is int-aligned
|
|
int start = alloc->spaceStart;
|
|
int end = start + sizeof(int) + inSize;
|
|
|
|
if ( end <= alloc->spaceEnd )
|
|
{
|
|
alloc->spaceStart = end;
|
|
|
|
unsigned int *buffer = (unsigned int *)(alloc->allocBase + start);
|
|
|
|
int startRow = start>>IMMIX_LINE_BITS;
|
|
|
|
alloc->allocStartFlags[ startRow ] |= gImmixStartFlag[start&127];
|
|
|
|
if (inContainer)
|
|
*buffer++ = (( (end+(IMMIX_LINE_LEN-1))>>IMMIX_LINE_BITS) -startRow) |
|
|
(inSize<<IMMIX_ALLOC_SIZE_SHIFT) |
|
|
hx::gMarkIDWithContainer;
|
|
else
|
|
*buffer++ = (( (end+(IMMIX_LINE_LEN-1))>>IMMIX_LINE_BITS) -startRow) |
|
|
(inSize<<IMMIX_ALLOC_SIZE_SHIFT) |
|
|
hx::gMarkID;
|
|
|
|
#if defined(HXCPP_GC_CHECK_POINTER) && defined(HXCPP_GC_DEBUG_ALWAYS_MOVE)
|
|
hx::GCOnNewPointer(buffer);
|
|
#endif
|
|
|
|
#ifdef HXCPP_TELEMETRY
|
|
__hxt_gc_new((hx::StackContext *)alloc,buffer, inSize, inName);
|
|
#endif
|
|
return buffer;
|
|
}
|
|
#endif // HXCPP_ALIGN_ALLOC
|
|
|
|
// Fall back to external method
|
|
void *result = alloc->CallAlloc(inSize, inContainer ? IMMIX_ALLOC_IS_CONTAINER : 0);
|
|
|
|
#ifdef HXCPP_TELEMETRY
|
|
__hxt_gc_new((hx::StackContext *)alloc,result, inSize, inName);
|
|
#endif
|
|
|
|
return result;
|
|
#endif // HXCPP_GC_NURSERY
|
|
}
|
|
};
|
|
|
|
typedef ImmixAllocator GcAllocator;
|
|
typedef ImmixAllocator Ctx;
|
|
|
|
|
|
#ifdef HXCPP_GC_GENERATIONAL
|
|
#define HX_OBJ_WB_CTX(obj,value,ctx) { \
|
|
unsigned char &mark = ((unsigned char *)(obj))[ HX_ENDIAN_MARK_ID_BYTE]; \
|
|
if (mark == hx::gByteMarkID && value && !((unsigned char *)(value))[ HX_ENDIAN_MARK_ID_BYTE ] ) { \
|
|
mark|=HX_GC_REMEMBERED; \
|
|
ctx->pushReferrer(obj); \
|
|
} }
|
|
#define HX_OBJ_WB_PESSIMISTIC_CTX(obj,ctx) { \
|
|
unsigned char &mark = ((unsigned char *)(obj))[ HX_ENDIAN_MARK_ID_BYTE]; \
|
|
if (mark == hx::gByteMarkID) { \
|
|
mark|=HX_GC_REMEMBERED; \
|
|
ctx->pushReferrer(obj); \
|
|
} }
|
|
// I'm not sure if this will ever trigger...
|
|
#define HX_OBJ_WB_NEW_MARKED_OBJECT(obj) { \
|
|
if (((unsigned char *)(obj))[ HX_ENDIAN_MARK_ID_BYTE]==hx::gByteMarkID) hx::NewMarkedObject(obj); \
|
|
}
|
|
#else
|
|
#define HX_OBJ_WB_CTX(obj,value,ctx)
|
|
#define HX_OBJ_WB_PESSIMISTIC_CTX(obj,ctx)
|
|
#define HX_OBJ_WB_NEW_MARKED_OBJECT(obj)
|
|
#endif
|
|
|
|
#define HX_OBJ_WB(obj,value) HX_OBJ_WB_CTX(obj,value,_hx_ctx)
|
|
#define HX_ARRAY_WB(array,index,value) HX_OBJ_WB(array,value)
|
|
#define HX_OBJ_WB_PESSIMISTIC(obj) HX_OBJ_WB_PESSIMISTIC_CTX(obj,_hx_ctx)
|
|
#define HX_OBJ_WB_GET(obj,value) HX_OBJ_WB_CTX(obj,value,HX_CTX_GET)
|
|
#define HX_OBJ_WB_PESSIMISTIC_GET(obj) HX_OBJ_WB_PESSIMISTIC_CTX(obj,HX_CTX_GET)
|
|
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES extern unsigned int gPrevMarkIdMask;
|
|
|
|
// Called only once it is determined that a new mark is required
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void MarkAllocUnchecked(void *inPtr ,hx::MarkContext *__inCtx);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void MarkObjectAllocUnchecked(hx::Object *inPtr ,hx::MarkContext *__inCtx);
|
|
HXCPP_EXTERN_CLASS_ATTRIBUTES void NewMarkedObject(hx::Object *inPtr);
|
|
|
|
inline void MarkAlloc(void *inPtr ,hx::MarkContext *__inCtx)
|
|
{
|
|
#ifdef EMSCRIPTEN
|
|
// Unaligned must be constants...
|
|
if ( !( ((size_t)inPtr) & 3) )
|
|
#endif
|
|
// This will also skip const regions
|
|
if ( !(((unsigned int *)inPtr)[-1] & gPrevMarkIdMask) )
|
|
MarkAllocUnchecked(inPtr,__inCtx);
|
|
}
|
|
inline void MarkObjectAlloc(hx::Object *inPtr ,hx::MarkContext *__inCtx)
|
|
{
|
|
#ifdef EMSCRIPTEN
|
|
// Unaligned must be constants...
|
|
if ( !( ((size_t)inPtr) & 3) )
|
|
#endif
|
|
// This will also skip const regions
|
|
if ( !(((unsigned int *)inPtr)[-1] & gPrevMarkIdMask) )
|
|
MarkObjectAllocUnchecked(inPtr,__inCtx);
|
|
}
|
|
|
|
|
|
} // end namespace hx
|
|
|
|
|
|
|
|
|
|
// It was theoretically possible to redefine the MarkContext arg type (or skip it)
|
|
// incase the particular GC scheme did not need it. This may take a bit of extra
|
|
// work to get going again
|
|
|
|
#define HX_MARK_ARG __inCtx
|
|
//#define HX_MARK_ADD_ARG ,__inCtx
|
|
#define HX_MARK_PARAMS hx::MarkContext *__inCtx
|
|
//#define HX_MARK_ADD_PARAMS ,hx::MarkContext *__inCtx
|
|
|
|
#ifdef HXCPP_VISIT_ALLOCS
|
|
#define HX_VISIT_ARG __inCtx
|
|
#define HX_VISIT_PARAMS hx::VisitContext *__inCtx
|
|
#else
|
|
#define HX_VISIT_ARG
|
|
#define HX_VISIT_PARAMS
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// These macros add debug to the mark/visit calls if required
|
|
// They also perform some inline checking to avoid function calls if possible
|
|
|
|
|
|
#ifdef HXCPP_DEBUG
|
|
|
|
#define HX_MARK_MEMBER_NAME(x,name) { hx::MarkSetMember(name, __inCtx); hx::MarkMember(x, __inCtx ); }
|
|
#define HX_MARK_BEGIN_CLASS(x) hx::MarkPushClass(#x, __inCtx );
|
|
#define HX_MARK_END_CLASS() hx::MarkPopClass(__inCtx );
|
|
#define HX_MARK_MEMBER(x) { hx::MarkSetMember(0, __inCtx); hx::MarkMember(x, __inCtx ); }
|
|
#define HX_MARK_MEMBER_ARRAY(x,len) { hx::MarkSetMember(0, __inCtx); hx::MarkMemberArray(x, len, __inCtx ); }
|
|
|
|
#else
|
|
|
|
#define HX_MARK_MEMBER_NAME(x,name) hx::MarkMember(x, __inCtx )
|
|
#define HX_MARK_BEGIN_CLASS(x)
|
|
#define HX_MARK_END_CLASS()
|
|
#define HX_MARK_MEMBER(x) hx::MarkMember(x, __inCtx )
|
|
#define HX_MARK_MEMBER_ARRAY(x,len) hx::MarkMemberArray(x, len, __inCtx )
|
|
|
|
#endif
|
|
|
|
#define HX_MARK_OBJECT(ioPtr) if (ioPtr) hx::MarkObjectAlloc(ioPtr, __inCtx );
|
|
|
|
|
|
|
|
|
|
#define HX_MARK_STRING(ioPtr) \
|
|
if (ioPtr) hx::MarkAlloc((void *)ioPtr, __inCtx );
|
|
|
|
#define HX_MARK_ARRAY(ioPtr) { if (ioPtr) hx::MarkAlloc((void *)ioPtr, __inCtx ); }
|
|
|
|
|
|
|
|
|
|
#define HX_VISIT_MEMBER_NAME(x,name) hx::VisitMember(x, __inCtx )
|
|
#define HX_VISIT_MEMBER(x) hx::VisitMember(x, __inCtx )
|
|
|
|
#define HX_VISIT_OBJECT(ioPtr) \
|
|
{ if (ioPtr && !(((unsigned char *)ioPtr)[HX_GC_CONST_ALLOC_MARK_OFFSET] & HX_GC_CONST_ALLOC_MARK_BIT) ) __inCtx->visitObject( (hx::Object **)&ioPtr); }
|
|
|
|
#define HX_VISIT_STRING(ioPtr) \
|
|
if (ioPtr && !(((unsigned char *)ioPtr)[HX_GC_CONST_ALLOC_MARK_OFFSET] & HX_GC_CONST_ALLOC_MARK_BIT) ) __inCtx->visitAlloc((void **)&ioPtr);
|
|
|
|
#define HX_VISIT_ARRAY(ioPtr) { if (ioPtr) __inCtx->visitAlloc((void **)&ioPtr); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|