mirror of
https://github.com/darlinghq/darling-libobjc2.git
synced 2025-03-03 22:19:34 +00:00
More GCKit work. Most stuff is working now, with the big exception being tracing of collectable regions. Test case demonstrates this failure and the success of everything else.
This commit is contained in:
parent
81f0c58a86
commit
2350156aba
@ -22,7 +22,7 @@ GCKit_HEADER_FILES = \
|
||||
|
||||
GCKit_LIBRARIES_DEPEND_UPON += -lpthread
|
||||
|
||||
GCKit_OBJCFLAGS += -Werror -std=c99 -march=native
|
||||
GCKit_OBJCFLAGS += -Werror -std=c99 -march=native -fno-inline
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/library.make
|
||||
include $(GNUSTEP_MAKEFILES)/tool.make
|
||||
|
@ -3,6 +3,7 @@
|
||||
#import "malloc.h"
|
||||
#import "thread.h"
|
||||
#import "visit.h"
|
||||
#include <stdio.h>
|
||||
|
||||
id GCRetain(id anObject)
|
||||
{
|
||||
@ -47,6 +48,7 @@ void GCRelease(id anObject)
|
||||
}
|
||||
}
|
||||
|
||||
void GCAddObjectForTracing(id object);
|
||||
|
||||
/**
|
||||
* Scan children turning them black and incrementing the reference count. Used
|
||||
@ -72,6 +74,7 @@ static void GCScan(id anObject, void* unused, BOOL isWeak)
|
||||
// If the object is not grey, then we've visited it already.
|
||||
if (colour == GCColourGrey)
|
||||
{
|
||||
//fprintf(stderr, "%x has retain count of %d\n", (int)anObject, (int)GCGetRetainCount(anObject));
|
||||
// If the retain count is still > 0, we didn't account for all of the
|
||||
// references with cycle detection, so mark it as black and reset the
|
||||
// retain count of every object that it references.
|
||||
@ -79,6 +82,9 @@ static void GCScan(id anObject, void* unused, BOOL isWeak)
|
||||
// If it did reach 0, then this is part of a garbage cycle so colour it
|
||||
// accordingly. Any objects reachable from this object do not get
|
||||
// their reference counts restored.
|
||||
//
|
||||
// FIXME: We need to be able to resurrect objects if they are
|
||||
// GCRetain()'d when they are white
|
||||
if (GCGetRetainCount(anObject) > 0)
|
||||
{
|
||||
GCSetColourOfObject(anObject, GCColourBlack);
|
||||
@ -103,12 +109,12 @@ static void GCScan(id anObject, void* unused, BOOL isWeak)
|
||||
static void GCCollectWhite(id anObject, void *ignored, BOOL isWeak)
|
||||
{
|
||||
//fprintf(stderr, "Looking at object %x with colour %s\n", (unsigned) anObject, [GCStringFromColour(GCColourOfObject(anObject)) UTF8String]);
|
||||
if ((GCColourOfObject(anObject) == GCColourWhite)
|
||||
&&
|
||||
YES)
|
||||
//!isObjectBuffered(anObject))
|
||||
if ((GCColourOfObject(anObject) == GCColourWhite))
|
||||
{
|
||||
GCAddObject(anObject);
|
||||
GCSetColourOfObject(anObject, GCColourRed);
|
||||
//fprintf(stderr, "%x marked red. Red's dead, baby!\n", (int)anObject);
|
||||
//fprintf(stderr, " has refcount %d!\n", (int)GCGetRetainCount(anObject));
|
||||
GCAddObjectForTracing(anObject);
|
||||
GCVisitChildren(anObject, GCCollectWhite, NULL, NO);
|
||||
}
|
||||
}
|
||||
@ -124,6 +130,7 @@ static void GCCollectWhite(id anObject, void *ignored, BOOL isWeak)
|
||||
*/
|
||||
void GCMarkGreyChildren(id anObject, void *ignored, BOOL isWeak)
|
||||
{
|
||||
//fprintf(stderr, "Marking %x as grey\n", (int)anObject);
|
||||
// FIXME: This should probably check if the colour is green. Green objects
|
||||
// can't be parts of cycles, and we need to restore the green colour after
|
||||
// scanning anyway.
|
||||
@ -143,8 +150,10 @@ void GCScanForCycles(id *loopBuffer, unsigned count)
|
||||
for (unsigned i=0 ; i<count ; i++)
|
||||
{
|
||||
next = loopBuffer[i];
|
||||
//fprintf(stderr, "Looking at %x\n", (int)next);
|
||||
// Check that this object is still eligible for cycle detection
|
||||
if (nil == next) continue;
|
||||
if (GCTestFlag(next, GCFlagNotObject)) continue;
|
||||
if (!GCTestFlag(next, GCFlagBuffered))
|
||||
{
|
||||
loopBuffer[i] = nil;
|
||||
@ -156,6 +165,7 @@ void GCScanForCycles(id *loopBuffer, unsigned count)
|
||||
if (colour == GCColourPurple)
|
||||
{
|
||||
// Mark it, and all of its children, as grey.
|
||||
//fprintf(stderr, "Marking grey: %d...\n", colour);
|
||||
GCSetColourOfObject(next, GCColourGrey);
|
||||
GCVisitChildren(next, GCMarkGreyChildren, nil, NO);
|
||||
}
|
||||
@ -166,7 +176,7 @@ void GCScanForCycles(id *loopBuffer, unsigned count)
|
||||
// tracer can't find them.
|
||||
if ((colour == GCColourBlack) && (GCGetRetainCount(next) <= 0))
|
||||
{
|
||||
GCAddObject(next);
|
||||
GCAddObjectForTracing(next);
|
||||
}
|
||||
loopBuffer[i] = nil;
|
||||
}
|
||||
@ -186,6 +196,8 @@ void GCScanForCycles(id *loopBuffer, unsigned count)
|
||||
if (nil == next) continue;
|
||||
GCCollectWhite(next, NULL, NO);
|
||||
}
|
||||
void GCRunTracerIfNeeded(BOOL);
|
||||
GCRunTracerIfNeeded(YES);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -13,7 +13,7 @@
|
||||
/**
|
||||
* Pointer comparison. Needed for the hash table.
|
||||
*/
|
||||
static int pointer_compare(const id a, const id b)
|
||||
static int pointer_compare(const void *a, const void *b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
@ -92,15 +92,13 @@ void *GCAllocateBufferWithZone(void *zone, size_t size, BOOL scan)
|
||||
char *buffer = ((char*)region) + headerSize(gc_buffer_header);
|
||||
if (scan)
|
||||
{
|
||||
GCTracedRegion region;
|
||||
region.start = buffer;
|
||||
region.end = buffer + size;
|
||||
// FIXME: Implement
|
||||
//GCTraceRegion(region);
|
||||
}
|
||||
// Reference count is 0, so set visited to prevent it from being collected
|
||||
// immediately
|
||||
GCSetFlag((id)buffer, GCFlagVisited);
|
||||
GCSetFlag((id)buffer, GCFlagNotObject);
|
||||
// Mark as free or in use.
|
||||
GCSetColourOfObject((id)buffer, GCColourBlack);
|
||||
// Add to traced map later, if it hasn't been retained
|
||||
@ -108,6 +106,22 @@ void *GCAllocateBufferWithZone(void *zone, size_t size, BOOL scan)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void freeObject(id object)
|
||||
{
|
||||
void * addr;
|
||||
if (GCTestFlag(object, GCFlagNotObject))
|
||||
{
|
||||
fprintf(stderr, "Freeing bufer %x\n", (int)object);
|
||||
addr = GCHeaderForBuffer(object);
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = GCHeaderForObject(object);
|
||||
}
|
||||
fprintf(stderr, "Freeing %x\n", (int)object);
|
||||
gc_free_with_zone(GCHeaderForObject(object)->zone, addr);
|
||||
}
|
||||
|
||||
void GCWeakRelease(id anObject)
|
||||
{
|
||||
if (!GCObjectIsDynamic(anObject)) { return; }
|
||||
@ -115,7 +129,7 @@ void GCWeakRelease(id anObject)
|
||||
// If the object has been finalized and this is the last weak ref, free it.
|
||||
if (count == 0 && GCColourOfObject(anObject) == GCColourOrange)
|
||||
{
|
||||
gc_free_with_zone(GCHeaderForObject(anObject)->zone, anObject);
|
||||
freeObject(anObject);
|
||||
}
|
||||
}
|
||||
id GCWeakRetain(id anObject)
|
||||
@ -131,15 +145,37 @@ id GCWeakRetain(id anObject)
|
||||
}
|
||||
// NOTE: Weak read should add the object for tracing.
|
||||
|
||||
void GCAddObjectForTracing(id object);
|
||||
|
||||
static BOOL foundRedObjects;
|
||||
|
||||
static void releaseObjects(id object, void *context, BOOL isWeak)
|
||||
{
|
||||
//fprintf(stderr, "Releasing %x\n", (int)object);
|
||||
if (isWeak)
|
||||
{
|
||||
GCWeakRelease(object);
|
||||
}
|
||||
else
|
||||
{
|
||||
GCRelease(object);
|
||||
GCColour colour = GCColourOfObject(object);
|
||||
// If we're freeing a cycle, mark this object as orange, finalize it,
|
||||
// then tell the tracing code to really delete it later
|
||||
if (colour == GCColourRed)
|
||||
{
|
||||
foundRedObjects = YES;
|
||||
GCSetColourOfObject(object, GCColourOrange);
|
||||
[object finalize];
|
||||
}
|
||||
else if (colour != GCColourOrange)
|
||||
{
|
||||
GCRelease(object);
|
||||
}
|
||||
//fprintf(stderr, "Object has refcount %d\n", (int)GCGetRetainCount(object));
|
||||
if (GCGetRetainCount(object) <= 0)
|
||||
{
|
||||
GCAddObjectForTracing(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,20 +187,19 @@ static void releaseObjects(id object, void *context, BOOL isWeak)
|
||||
void GCFreeObjectUnsafe(id object)
|
||||
{
|
||||
if (!GCObjectIsDynamic(object)) { return; }
|
||||
|
||||
//fprintf(stderr, "Going to Free object %x\n", (int)(object));
|
||||
|
||||
foundRedObjects = NO;
|
||||
if (GCColourOrange != GCSetColourOfObject(object, GCColourOrange))
|
||||
{
|
||||
GCTracedRegion region = {object, object};
|
||||
// If this is really an object, kill all of its references and then
|
||||
// finalize it.
|
||||
if (!GCTestFlag(object, GCFlagNotObject))
|
||||
{
|
||||
if (0)
|
||||
GCVisitChildren(object, releaseObjects, NULL, YES);
|
||||
[object finalize];
|
||||
region.end += class_getInstanceSize(object->isa);
|
||||
}
|
||||
else
|
||||
{
|
||||
region.end += GCHeaderForBuffer(object)->size;
|
||||
}
|
||||
// FIXME: Implement this.
|
||||
//GCRemoveRegionFromTracingUnsafe(region);
|
||||
@ -172,7 +207,11 @@ void GCFreeObjectUnsafe(id object)
|
||||
if (GCGetWeakRefCount(object) == 0)
|
||||
{
|
||||
//fprintf(stderr, "Freeing object %x\n", (int)(object));
|
||||
gc_free_with_zone(GCHeaderForObject(object)->zone, object);
|
||||
freeObject(object);
|
||||
}
|
||||
if (foundRedObjects)
|
||||
{
|
||||
GCRunTracerIfNeeded(NO);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,10 @@ typedef enum
|
||||
/** Potential root of a cycle. */
|
||||
GCColourPurple = 4,
|
||||
/** Object currently being freed. */
|
||||
GCColourOrange = 5
|
||||
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
|
||||
@ -106,6 +109,7 @@ inline static const char *GCStringFromColour(GCColour aColour)
|
||||
case GCColourPurple: return "purple";
|
||||
case GCColourGreen: return "green";
|
||||
case GCColourOrange: return "orange";
|
||||
case GCColourRed: return "red";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
74
GCKit/test.m
74
GCKit/test.m
@ -81,6 +81,7 @@ int main(int argc, char **argv, char **env)
|
||||
#import "cycle.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
@interface NSConstantString
|
||||
{
|
||||
@ -105,13 +106,21 @@ int main(int argc, char **argv, char **env)
|
||||
}
|
||||
- (void)log
|
||||
{
|
||||
fprintf(stderr, "Simple object %x is still alive\n", (int)self);
|
||||
printf("Simple object %x is still alive\n", (int)self);
|
||||
}
|
||||
- (void)finalize
|
||||
{
|
||||
fprintf(stderr, "Simple object finalised\n");
|
||||
printf("%s %x finalised\n", class_getName(isa), (int)self);
|
||||
}
|
||||
@end
|
||||
// The test program calls GCDrain() repeatedly to force the GC to run. In real
|
||||
// code, this will be triggered automatically as a result of object allocations
|
||||
// and reference count changes. In this code, however, it is not. The test
|
||||
// case will exit before the GC would run in normal use. This is not a bug;
|
||||
// there's no point spending CPU time collecting objects a few milliseconds
|
||||
// before the process exits and the OS reclaims them all at once. The point of
|
||||
// a garbage collector is to reclaim memory for reuse, and if no reuse is going
|
||||
// to take place, there is no point reclaiming it.
|
||||
|
||||
void makeObject(void)
|
||||
{
|
||||
@ -119,7 +128,6 @@ void makeObject(void)
|
||||
[foo log];
|
||||
GCDrain(YES);
|
||||
GCDrain(YES);
|
||||
sleep(1);
|
||||
[foo log];
|
||||
foo = nil;
|
||||
[foo log];
|
||||
@ -154,7 +162,6 @@ void putObjectInBuffer(void)
|
||||
[*buffer log];
|
||||
GCDrain(YES);
|
||||
GCDrain(YES);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
void testTracedMemory(void)
|
||||
@ -162,18 +169,73 @@ void testTracedMemory(void)
|
||||
putObjectInBuffer();
|
||||
GCDrain(YES);
|
||||
}
|
||||
@interface Pair : SimpleObject
|
||||
{
|
||||
@public
|
||||
Pair *a, *b;
|
||||
}
|
||||
@end
|
||||
@implementation Pair @end
|
||||
|
||||
void makeObjectCycle(void)
|
||||
{
|
||||
Pair *obj = [Pair new];
|
||||
obj->a = GCRetain([Pair new]);
|
||||
obj->b = GCRetain([Pair new]);
|
||||
obj->a->a = GCRetain(obj->b);
|
||||
obj->b->b = GCRetain(obj->a);
|
||||
obj->a->b = GCRetain(obj);
|
||||
obj->b->a = GCRetain(obj);
|
||||
[obj log];
|
||||
GCRelease(GCRetain(obj));
|
||||
GCDrain(YES);
|
||||
}
|
||||
|
||||
void testCycle(void)
|
||||
{
|
||||
makeObjectCycle();
|
||||
GCDrain(YES);
|
||||
GCDrain(YES);
|
||||
}
|
||||
|
||||
void makeTracedCycle(void)
|
||||
{
|
||||
// These two buffers are pointing to each other
|
||||
id *b1 = GCAllocateBufferWithZone(NULL, sizeof(id), YES);
|
||||
Pair *p = [Pair new];
|
||||
id *b2 = GCAllocateBufferWithZone(NULL, sizeof(id), YES);
|
||||
fprintf(stderr, "Expected to leak %x and %x\n", (int)b1, (int)b2);
|
||||
//objc_assign_strongCast((id)b2, b1);
|
||||
objc_assign_strongCast(p, b1);
|
||||
objc_assign_strongCast((id)b1, b2);
|
||||
p->a = (id)b2;
|
||||
}
|
||||
|
||||
void testTracedCycle(void)
|
||||
{
|
||||
makeTracedCycle();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testTracedCycle();
|
||||
// Not required on main thread:
|
||||
//GCRegisterThread();
|
||||
doStuff();
|
||||
GCDrain(YES);
|
||||
sleep(2);
|
||||
doRefCountStuff();
|
||||
GCDrain(YES);
|
||||
sleep(2);
|
||||
testTracedMemory();
|
||||
buffer[0] = objc_assign_strongCast(nil, buffer);
|
||||
GCDrain(YES);
|
||||
|
||||
testCycle();
|
||||
GCDrain(YES);
|
||||
GCDrain(YES);
|
||||
GCDrain(YES);
|
||||
sched_yield();
|
||||
GCDrain(YES);
|
||||
GCDrain(YES);
|
||||
printf("Waiting to make sure the GC thread has caught up before the test exits\n");
|
||||
sleep(1);
|
||||
}
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
/**
|
||||
* Size of the buffers used in each thread before passing stuff over to the GC
|
||||
* thread. Once either BUFFER_SIZE objects are queued waiting for tracing or
|
||||
@ -129,7 +131,6 @@ void GCRegisterThread(void)
|
||||
|
||||
thr->cycleBuffer = calloc(BUFFER_SIZE, sizeof(void*));
|
||||
thr->freeBuffer = calloc(BUFFER_SIZE, sizeof(void*));
|
||||
fprintf(stderr, "Stack starts at %x\n", (unsigned)thr->stackTop);
|
||||
GCPerform(GCAddThread, thr);
|
||||
}
|
||||
void GCAddObject(id anObject)
|
||||
@ -178,13 +179,16 @@ static void traceTrampoline(void *c)
|
||||
GCTraceContext *context = c;
|
||||
|
||||
// Scan for any new garbage cycles.
|
||||
GCScanForCycles(context->cycleBuffer, context->cycleBufferSize);
|
||||
if (context->cycleBufferSize)
|
||||
{
|
||||
GCScanForCycles(context->cycleBuffer, context->cycleBufferSize);
|
||||
free(context->cycleBuffer);
|
||||
}
|
||||
// Now add the objects that might be garbage to the collector.
|
||||
// These won't actually be freed until after this
|
||||
GCRunTracerIfNeeded(context->forceTrace);
|
||||
|
||||
free(context->cycleBuffer);
|
||||
free(c);
|
||||
//free(c);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -203,13 +207,20 @@ static void GCDrainThread(GCThread *thread, BOOL forceCollect)
|
||||
// Mark all objects on this thread's stack as visited.
|
||||
GCTraceStackSynchronous(thread);
|
||||
|
||||
GCTraceContext *context = malloc(sizeof(GCTraceContext));
|
||||
context->cycleBuffer = thread->cycleBuffer;
|
||||
context->cycleBufferSize = thread->cycleBufferInsert;
|
||||
GCTraceContext *context = calloc(sizeof(GCTraceContext), 1);
|
||||
void *
|
||||
valloc(size_t size);
|
||||
//GCTraceContext *context = valloc(4096);
|
||||
if (thread->cycleBufferInsert)
|
||||
{
|
||||
context->cycleBuffer = thread->cycleBuffer;
|
||||
context->cycleBufferSize = thread->cycleBufferInsert;
|
||||
thread->cycleBuffer = calloc(BUFFER_SIZE, sizeof(void*));
|
||||
thread->cycleBufferInsert = 0;
|
||||
}
|
||||
context->forceTrace = forceCollect;
|
||||
//mprotect(context, 4096, PROT_READ);
|
||||
GCPerform(traceTrampoline, context);
|
||||
thread->cycleBuffer = calloc(BUFFER_SIZE, sizeof(void*));
|
||||
thread->cycleBufferInsert = 0;
|
||||
thread->freeBufferInsert = 0;
|
||||
}
|
||||
void GCDrain(BOOL forceCollect)
|
||||
|
@ -3,6 +3,7 @@
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
struct gc_buffer_header *buffer;
|
||||
void *start;
|
||||
void *end;
|
||||
} GCTracedRegion;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#import "object.h"
|
||||
#import "thread.h"
|
||||
#import "trace.h"
|
||||
#import "cycle.h"
|
||||
#import "malloc.h"
|
||||
#import "static.h"
|
||||
|
||||
@ -59,12 +60,12 @@ static int traced_pointer_compare(const void *a, const GCTracedPointer b)
|
||||
static int traced_pointer_hash(const GCTracedPointer obj)
|
||||
{
|
||||
intptr_t ptr = (intptr_t)obj.pointer;
|
||||
return (ptr >> 8) | (ptr << 8);
|
||||
return (ptr >> 4) | (ptr << 4);
|
||||
}
|
||||
static int traced_pointer_key_hash(const void *obj)
|
||||
{
|
||||
intptr_t ptr = (intptr_t)obj;
|
||||
return (ptr >> 8) | (ptr << 8);
|
||||
return (ptr >> 4) | (ptr << 4);
|
||||
}
|
||||
static int traced_pointer_is_null(const GCTracedPointer obj)
|
||||
{
|
||||
@ -83,7 +84,7 @@ static int traced_pointer_is_null(const GCTracedPointer obj)
|
||||
/**
|
||||
* Pointer comparison. Needed for the hash table.
|
||||
*/
|
||||
static int pointer_compare(const id a, const id b)
|
||||
static int pointer_compare(const void *a, const void *b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
@ -106,8 +107,6 @@ static traced_object_table *traced_objects;
|
||||
*/
|
||||
static pthread_rwlock_t traced_objects_lock;
|
||||
|
||||
|
||||
|
||||
typedef struct _GCTracedRegionTreeNode
|
||||
{
|
||||
GCTracedRegion region;
|
||||
@ -450,6 +449,7 @@ static void GCTraceRegion(GCTracedRegion region, void *c)
|
||||
*/
|
||||
void GCTraceStackSynchronous(GCThread *thr)
|
||||
{
|
||||
//fprintf(stderr, "Scanning the stack...\n");
|
||||
int generation = GCGeneration;
|
||||
pthread_rwlock_rdlock(&traced_objects_lock);
|
||||
if (NULL == thr->unescapedObjects)
|
||||
@ -474,6 +474,7 @@ void GCTraceStackSynchronous(GCThread *thr)
|
||||
// is guaranteed, at this point, not to be referenced by another
|
||||
// thread.
|
||||
GCSetFlag(*object, GCFlagVisited);
|
||||
//fprintf(stderr, "Tracing found %x\n", (int)*object);
|
||||
}
|
||||
GCTracedPointer *foundObject =
|
||||
traced_object_table_get(traced_objects, *object);
|
||||
@ -548,6 +549,7 @@ void GCRunTracer(void)
|
||||
do
|
||||
{
|
||||
oldPtr = object;
|
||||
//fprintf(stderr, "Thinking of freeing %x. Visited: %d, clear gen: %d, thread gen: %d\n", (int)object->pointer, GCTestFlag(object->pointer, GCFlagVisited), object->visitClearedGeneration , threadGeneration);
|
||||
// If an object hasn't been visited and we have scanned everywhere
|
||||
// since we cleared its visited flag, delete it. This works
|
||||
// because the heap write barrier sets the visited flag.
|
||||
@ -571,12 +573,14 @@ void GCRunTracerIfNeeded(BOOL forceCollect)
|
||||
{
|
||||
id object = ptr->pointer;
|
||||
// Throw away any objects that are referenced by the heap
|
||||
if (GCGetRetainCount(object) > 0)
|
||||
if (GCGetRetainCount(object) > 0 &&
|
||||
GCColourOfObject(object) != GCColourRed)
|
||||
{
|
||||
// Make sure that the retain count is still > 0. If not then it
|
||||
// may have been released but not added to the tracing list
|
||||
// (because it was marked for tracing already)
|
||||
if (GCGetRetainCount(object) > 0)
|
||||
if (GCGetRetainCount(object) > 0 &&
|
||||
GCColourOfObject(object) != GCColourRed)
|
||||
{
|
||||
pthread_rwlock_wrlock(&traced_objects_lock);
|
||||
traced_object_remove(traced_objects, object);
|
||||
@ -586,10 +590,12 @@ void GCRunTracerIfNeeded(BOOL forceCollect)
|
||||
}
|
||||
if (GCTestFlag(object, GCFlagVisited))
|
||||
{
|
||||
//fprintf(stderr, "Clearing visited flag for %x\n", (int)object);
|
||||
GCClearFlag(object, GCFlagVisited);
|
||||
ptr->visitClearedGeneration = GCGeneration;
|
||||
}
|
||||
}
|
||||
//fprintf(stderr, "Incrementing generation\n");
|
||||
// Invalidate all stack scans.
|
||||
GCGeneration++;
|
||||
// Only actually run the tracer if we have more than a few objects that
|
||||
@ -601,6 +607,18 @@ void GCRunTracerIfNeeded(BOOL forceCollect)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an object for tracing that the cycle detector has decided needs freeing.
|
||||
*/
|
||||
void GCAddObjectForTracing(id object)
|
||||
{
|
||||
if (!traced_object_table_get(traced_objects, object))
|
||||
{
|
||||
//fprintf(stderr, "Cycle detector nominated %x for tracing\n", (int)object);
|
||||
GCTracedPointer obj = {object, 0, 0, 0};
|
||||
traced_object_insert(traced_objects, obj);
|
||||
}
|
||||
}
|
||||
|
||||
void GCAddObjectsForTracing(GCThread *thr)
|
||||
{
|
||||
@ -621,12 +639,17 @@ void GCAddObjectsForTracing(GCThread *thr)
|
||||
id object = buffer[i];
|
||||
if (!GCObjectIsDynamic(object))
|
||||
{
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
// Skip objects that have a strong retain count > 0. They are
|
||||
// definitely still referenced.
|
||||
if (GCGetRetainCount(object) > 0)
|
||||
// definitely still referenced...
|
||||
if (GCGetRetainCount(object) > 0 &&
|
||||
GCColourOfObject(object) != GCColourRed)
|
||||
{
|
||||
// ...but they might have become part of a cycle
|
||||
GCSetFlag(object, GCFlagBuffered);
|
||||
GCSetColourOfObject(object, GCColourPurple);
|
||||
GCScanForCycles(&object, 1);
|
||||
continue;
|
||||
}
|
||||
if (GCTestFlag(object, GCFlagEscaped))
|
||||
@ -685,6 +708,7 @@ id objc_assign_strongCast(id obj, id *ptr)
|
||||
if (old->heapAddress == ptr)
|
||||
{
|
||||
old->heapAddress = 0;
|
||||
old->visitClearedGeneration = GCGeneration + 1;
|
||||
GCClearFlag(*ptr, GCFlagVisited);
|
||||
}
|
||||
}
|
||||
|
@ -132,10 +132,7 @@ void GCVisitChildren(id object, visit_function_t function, void *argument,
|
||||
if (child != nil)
|
||||
{
|
||||
BOOL isWeak = (intptr_t)child & 1;
|
||||
if (isWeak)
|
||||
{
|
||||
function(object, argument, isWeak);
|
||||
}
|
||||
function(object, argument, isWeak);
|
||||
}
|
||||
}
|
||||
if (NULL != info->extraChildren)
|
||||
|
Loading…
x
Reference in New Issue
Block a user