#include <objc/objc-internal.h>
#include "test.h"
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#pragma clang diagnostic ignored "-Warc-unsafe-retained-assign"
// This is StripedMap's pointer hash
-uintptr_t hash(id obj) {
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+ enum { StripeCount = 8 };
+#else
+ enum { StripeCount = 64 };
+#endif
+uintptr_t stripehash(id obj) {
uintptr_t addr = (uintptr_t)obj;
- return ((addr >> 4) ^ (addr >> 9)) % 64;
+ return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
bool sameAlignment(id o1, id o2)
{
- return hash(o1) == hash(o2);
+ return stripehash(o1) == stripehash(o2);
}
-// Return a new string object that uses the same striped weak locks as `obj`.
-NSMutableString *newAlignedString(id obj)
+// Return a new non-tagged object that uses the same striped weak locks as `obj`
+NSObject *newAlignedObject(id obj)
{
- NSMutableArray *strings = [NSMutableArray new];
- NSMutableString *result;
- do {
- result = [NSMutableString new];
- [strings addObject:result];
- } while (!sameAlignment(obj, result));
+ // Use immutable arrays because their contents are stored inline,
+ // which prevents Guard Malloc from using the same alignment for all of them
+ NSArray *result = [NSArray new];
+ while (!sameAlignment(obj, result)) {
+ result = [result arrayByAddingObject:result];
+ }
return result;
}
__weak NSObject *weak1;
-__weak NSMutableString *weak2;
-NSMutableString *strong2;
+__weak NSObject *weak2;
+NSObject *strong2;
@interface A : NSObject @end
@implementation A
// without holding locks.
@autoreleasepool {
A *obj = [A new];
- strong2 = newAlignedString(obj);
+ strong2 = newAlignedObject(obj);
[obj addObserver:obj forKeyPath:@"foo" options:0 context:0];
weak1 = obj; // weak store #1
[obj removeObserver:obj forKeyPath:@"foo"];
__weak NSObject *weak3;
-__weak NSMutableString *weak4;
-NSMutableString *strong4;
+__weak NSObject *weak4;
+NSObject *strong4;
@interface B : NSObject @end
@implementation B
// without holding locks.
@autoreleasepool {
B *obj = [B new];
- strong4 = newAlignedString(obj);
+ strong4 = newAlignedObject(obj);
weak3 = obj;
[obj addObserver:obj forKeyPath:@"foo" options:0 context:0];
[weak3 self]; // weak load #3
}
-int main()
+__weak id weak6;
+NSObject *strong6;
+semaphore_t Dgo;
+semaphore_t Ddone;
+
+void *Dthread(void *arg __unused)
+{
+ @autoreleasepool {
+ semaphore_wait(Dgo);
+ for (int i = 0; i < 1000; i++) {
+ id x = weak6;
+ testassert(x == strong6);
+ }
+ return nil;
+ }
+}
+
+@interface D : NSObject @end
+@implementation D
++(void)initialize {
+ strong6 = [self new];
+ weak6 = strong6;
+ semaphore_signal(Dgo);
+ for (int i = 0; i < 1000; i++) {
+ id x = weak6;
+ testassert(x == strong6);
+ }
+}
+@end
+
+void testD()
{
- alarm(10); // replace hangs with crashes
+ // +initialize performs a weak store of itself, then another thread
+ // tries to load that weak variable before +initialize completes.
+ // Deadlock occurs if the +initialize thread tries to acquire the
+ // sidetable lock for another operation and the second thread holds
+ // the sidetable lock while waiting for +initialize.
+
+ @autoreleasepool {
+ semaphore_create(mach_task_self(), &Dgo, 0, 0);
+ semaphore_create(mach_task_self(), &Ddone, 0, 0);
+ pthread_t th;
+ pthread_create(&th, nil, Dthread, nil);
+ [D self];
+ pthread_join(th, nil);
+ }
+}
- testA();
- testB();
- testC();
+int main()
+{
+ if (is_guardmalloc() && getenv("MALLOC_PROTECT_BEFORE")) {
+ testwarn("fixme malloc guard before breaks this with debug libobjc");
+ }
+ else {
+ alarm(10); // replace hangs with crashes
+
+ testA();
+ testB();
+ testC();
+ testD();
+ }
succeed(__FILE__);
}