diff --git a/include/Array.h b/include/Array.h index ed6f7386c..097fdbfa4 100644 --- a/include/Array.h +++ b/include/Array.h @@ -126,6 +126,18 @@ class ArrayKeyValueIterator : public cpp::FastIterator_obj namespace hx { +#if (HXCPP_API_LEVEL>=500) +class HXCPP_EXTERN_CLASS_ATTRIBUTES ArrayPin +{ + char* ptr; +public: + ArrayPin(char* inPtr); + + ~ArrayPin(); + + char* GetBase(); +}; +#endif // Also used by cpp::VirtualArray class HXCPP_EXTERN_CLASS_ATTRIBUTES ArrayCommon : public hx::Object @@ -190,6 +202,9 @@ class HXCPP_EXTERN_CLASS_ATTRIBUTES ArrayBase : public ArrayCommon mAlloc = -1; } +#if (HXCPP_API_LEVEL>=500) + ArrayPin* Pin(); +#endif int __GetType() const { return vtArray; } diff --git a/include/hx/GC.h b/include/hx/GC.h index 5370c142e..f5835cad1 100644 --- a/include/hx/GC.h +++ b/include/hx/GC.h @@ -187,6 +187,10 @@ void MarkConservative(int *inBottom, int *inTop,hx::MarkContext *__inCtx); void GCAddRoot(hx::Object **inRoot); void GCRemoveRoot(hx::Object **inRoot); +#if (HXCPP_API_LEVEL>=500) +void GCPinPtr(void* inPtr); +void GCUnpinPtr(void* inPtr); +#endif // This is used internally in hxcpp // It calls InternalNew, and takes care of null-terminating the result diff --git a/src/Array.cpp b/src/Array.cpp index 19366ea16..e8bb35ece 100644 --- a/src/Array.cpp +++ b/src/Array.cpp @@ -36,7 +36,33 @@ ArrayBase::ArrayBase(int inSize,int inReserve,int inElementSize,bool inAtomic) inElementSize==sizeof(String) ? aciStringArray : aciObjectArray; } +#if (HXCPP_API_LEVEL>=500) +ArrayPin::ArrayPin(char* inPtr) : ptr(inPtr) +{ + hx::GCPinPtr(ptr); +} + +ArrayPin::~ArrayPin() +{ + hx::GCUnpinPtr(ptr); +} + +char* ArrayPin::GetBase() +{ + return ptr; +} +ArrayPin* ArrayBase::Pin() +{ + if (!mBase) + { + return nullptr; + } + + // If the array holds unmanaged data is it safe to put this in the ArrayPin if we don't know the lifetime of that pointer? + return new ArrayPin(mBase); +} +#endif void ArrayBase::reserve(int inSize) const { diff --git a/src/hx/gc/Immix.cpp b/src/hx/gc/Immix.cpp index 4894f79e3..f5c1dca1d 100644 --- a/src/hx/gc/Immix.cpp +++ b/src/hx/gc/Immix.cpp @@ -2364,6 +2364,9 @@ static RootSet sgRootSet; typedef hx::UnorderedMap OffsetRootSet; static OffsetRootSet *sgOffsetRootSet=0; +typedef hx::UnorderedSet PinSet; +static PinSet sgPinSet; + void GCAddRoot(hx::Object **inRoot) { AutoLock lock(*sGCRootLock); @@ -2376,7 +2379,6 @@ void GCRemoveRoot(hx::Object **inRoot) sgRootSet.erase(inRoot); } - void GcAddOffsetRoot(void *inRoot, int inOffset) { AutoLock lock(*sGCRootLock); @@ -3184,6 +3186,14 @@ class GlobalAllocator unsigned int *blob = ((unsigned int *)inLarge) - 2; unsigned int size = *blob; mLargeListLock.Lock(); + + if (hx::sgPinSet.count(inLarge)) + { + mLargeListLock.Unlock(); + + return; + } + mLargeAllocated -= size; // Could somehow keep it in the list, but mark as recycled? mLargeList.qerase_val(blob); @@ -4716,10 +4726,37 @@ class GlobalAllocator hx::Object *obj = (hx::Object *)(ptr - offset); if (obj) - hx::MarkObjectAlloc(obj , &mMarker ); + hx::MarkObjectAlloc(obj , &mMarker); } } // automark + { + hx::AutoMarkPush info(&mMarker, "Pins", "pin"); + + for (hx::PinSet::iterator i = hx::sgPinSet.begin(); i != hx::sgPinSet.end(); ++i) + { + void* const ptr = *i; + if (!ptr) + { + continue; + } + + auto ptr_i = reinterpret_cast(ptr) - sizeof(int); + auto flags = *reinterpret_cast(ptr_i); + auto onLOH = (flags & 0xffff) == 0; + + if (!onLOH) + { + BlockData* block = reinterpret_cast(reinterpret_cast(ptr) & IMMIX_BLOCK_BASE_MASK); + BlockDataInfo* info = (*gBlockInfo)[block->mId]; + + info->pin(); + } + + hx::MarkAlloc(ptr, &mMarker); + } + } + #ifdef PROFILE_COLLECT hx::rootObjects = sObjectMarks; hx::rootAllocs = sAllocMarks; @@ -5585,6 +5622,29 @@ class GlobalAllocator namespace hx { +#if (HXCPP_API_LEVEL>=430) +void GCPinPtr(void* inPtr) +{ + if (IsConstAlloc(inPtr)) + { + return; + } + + AutoLock(sGlobalAlloc->mLargeListLock); + sgPinSet.emplace(inPtr); +} + +void GCUnpinPtr(void* inPtr) +{ + if (IsConstAlloc(inPtr)) + { + return; + } + + AutoLock(sGlobalAlloc->mLargeListLock); + sgPinSet.erase(inPtr); +} +#endif MarkChunk *MarkChunk::swapForNew() {