Skip to content

Commit

Permalink
Merge branch 'master' into test-objcxx-interop-twice
Browse files Browse the repository at this point in the history
  • Loading branch information
davidchisnall authored Dec 18, 2021
2 parents 28d0445 + 14619f2 commit cf20045
Show file tree
Hide file tree
Showing 11 changed files with 324 additions and 89 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ endif()
# threading implementation (we do for everything except thread-local storage)
set(CMAKE_THREAD_PREFER_PTHREAD)
include(FindThreads)
set(objc_LINK_FLAGS "${objc_LINK_FLAGS} ${CMAKE_THREAD_LIBS_INIT}")
target_link_libraries(objc Threads::Threads)



Expand Down
16 changes: 16 additions & 0 deletions Test/ARCTest_arc.m
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ - (void)dealloc
}
@end

static __weak id weakRef;

@interface CheckDeallocWeakRef : Test
@end
@implementation CheckDeallocWeakRef
- (void)dealloc
{
weakRef = self;
}
@end


int main(void)
{
Expand Down Expand Up @@ -115,5 +126,10 @@ int main(void)
[obj setWeak: x];
}
assert([obj loadWeak] != nil);
// Check setting weak references during dealloc
{
[CheckDeallocWeakRef new];
}
assert(weakRef == nil);
return 0;
}
2 changes: 2 additions & 0 deletions Test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ set(TESTS
Category.m
ExceptionTest.m
FastARC.m
FastARCPool.m
FastRefCount.m
Forward.m
ManyManySelectors.m
NestedExceptions.m
Expand Down
41 changes: 41 additions & 0 deletions Test/FastARCPool.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "Test.h"

#define POOL_SIZE (4096 / sizeof(void*))

static BOOL called;

@interface Canary : Test
@end
@implementation Canary
- (void)dealloc
{
called = YES;
[super dealloc];
}
@end

@interface Creator : Test
@end
@implementation Creator
- (void)dealloc
{
// Add a new page of autorelease references to see if we can still release
// the reference on the canary object.
for (int i = 0; i < POOL_SIZE; i++)
[[Test new] autorelease];
[super dealloc];
}
@end

int main()
{
called = NO;
@autoreleasepool
{
[[Canary new] autorelease];
[[Creator new] autorelease];
}
assert(called == YES);

return 0;
}
167 changes: 167 additions & 0 deletions Test/FastRefCount.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#include "Test.h"

void direct_saturation_test();

@interface TestWithDelloc : Test
@end

@implementation TestWithDelloc
- (void)dealloc
{
id obj = nil;
objc_storeStrong(&obj, self);
assert(obj == self);
assert(object_getRetainCount_np(obj) == 0);
[super dealloc];
}
@end

int main()
{
id obj = [Test new];
assert(object_getRetainCount_np(obj) == 1);

for (int i = 0; i < 2; i++)
{
size_t count = object_getRetainCount_np(obj);
id ret = objc_retain_fast_np(obj);
assert(ret == obj);
assert(object_getRetainCount_np(obj) == ++count);
}

for (int i = 0; i < 2; i++)
{
size_t count = object_getRetainCount_np(obj);
BOOL destroy = objc_release_fast_no_destroy_np(obj);
assert(destroy == NO);
assert(object_getRetainCount_np(obj) == --count);
}

{
// Final release should prevent further retains and releases.
assert(objc_release_fast_no_destroy_np(obj) == YES);
assert(object_getRetainCount_np(obj) == 0);
assert(objc_retain_fast_np(obj) == obj);
assert(object_getRetainCount_np(obj) == 0);
assert(objc_release_fast_no_destroy_np(obj) == NO);
assert(object_getRetainCount_np(obj) == 0);
}

object_dispose(obj);
obj = [Test new];

{
// Should not be able to delete weak refs until final release.
id weak;
assert(objc_initWeak(&weak, obj) == obj);
assert(weak != nil);
assert(objc_loadWeakRetained(&weak) == obj);
assert(objc_release_fast_no_destroy_np(obj) == NO);
// Assumes a return of NO means no effect on obj at all.
assert(objc_delete_weak_refs(obj) == NO);
assert(objc_loadWeakRetained(&weak) == obj);
assert(objc_release_fast_no_destroy_np(obj) == NO);
// This will also call objc_delete_weak_refs() and succeed.
assert(objc_release_fast_no_destroy_np(obj) == YES);
objc_destroyWeak(&weak);
// Check what happens when the weak refs were already deleted.
assert(objc_delete_weak_refs(obj) == YES);
}

object_dispose(obj);
// Check we can use strong references inside a dealloc method.
obj = [TestWithDelloc new];
[obj release];
obj = nil;

direct_saturation_test();
return 0;
}


// ----------------
// This test has knowledge of the implementation details of the ARC
// reference counting and may need modification if the details change.

const long refcount_shift = 1;
const size_t weak_mask = ((size_t)1)<<((sizeof(size_t)*8)-refcount_shift);
const size_t refcount_mask = ~weak_mask;
const size_t refcount_max = refcount_mask - 1;

size_t get_refcount(id obj)
{
size_t *refCount = ((size_t*)obj) - 1;
return *refCount & refcount_mask;
}

void set_refcount(id obj, size_t count)
{
size_t *refCount = ((size_t*)obj) - 1;
*refCount = (*refCount & weak_mask) | (count & refcount_mask);
}

void direct_saturation_test()
{
{
id obj = [Test new];
// sanity check
objc_retain_fast_np(obj);
assert(object_getRetainCount_np(obj) == 2);
assert(get_refcount(obj) == 1);

// Check the behaviour close to the maximum refcount.
set_refcount(obj, refcount_max - 3);
assert(object_getRetainCount_np(obj) == refcount_max - 2);

assert(objc_retain_fast_np(obj) == obj);
assert(object_getRetainCount_np(obj) == refcount_max - 1);

id weak;
assert(objc_initWeak(&weak, obj) == obj);
assert(weak != nil);
assert(objc_loadWeakRetained(&weak) == obj);
assert(object_getRetainCount_np(obj) == refcount_max);

// This retain should cause the count to saturate.
assert(objc_retain_fast_np(obj) == obj);
assert(object_getRetainCount_np(obj) == refcount_max + 1);

// A saturated count is no longer affected by retains or releases.
assert(objc_release_fast_no_destroy_np(obj) == NO);
assert(object_getRetainCount_np(obj) == refcount_max + 1);
assert(objc_retain_fast_np(obj) == obj);
assert(object_getRetainCount_np(obj) == refcount_max + 1);

// Nor should any weak refs be deleted.
assert(objc_delete_weak_refs(obj) == NO);
assert(objc_loadWeakRetained(&weak) == obj);
assert(object_getRetainCount_np(obj) == refcount_max + 1);

// Cleanup (can skip this if it becomes an issue)
objc_destroyWeak(&weak);
set_refcount(obj, 0);
objc_release_fast_no_destroy_np(obj);
object_dispose(obj);
}

{
id obj = [Test new];
set_refcount(obj, refcount_max - 2);
assert(objc_retain_fast_np(obj) == obj);
assert(objc_retain_fast_np(obj) == obj);
assert(object_getRetainCount_np(obj) == refcount_max + 1);

// Check we can init a weak ref to an object with a saturated count.
id weak;
assert(objc_initWeak(&weak, obj) == obj);
assert(weak != nil);
assert(objc_loadWeakRetained(&weak) == obj);
assert(object_getRetainCount_np(obj) == refcount_max + 1);

// Cleanup (can skip this if it becomes an issue)
objc_destroyWeak(&weak);
set_refcount(obj, 0);
objc_release_fast_no_destroy_np(obj);
object_dispose(obj);
}
}
Loading

0 comments on commit cf20045

Please sign in to comment.