2011-04-20 09:41:12 +00:00
|
|
|
#import <Foundation/NSThread.h>
|
|
|
|
#import <Foundation/NSLock.h>
|
2011-04-29 14:59:24 +00:00
|
|
|
#include <unistd.h>
|
2011-04-20 09:41:12 +00:00
|
|
|
#import "Testing.h"
|
|
|
|
|
2011-05-03 19:39:08 +00:00
|
|
|
@interface SlowInit0
|
|
|
|
@end
|
2011-04-20 09:41:12 +00:00
|
|
|
|
|
|
|
@interface SlowInit1 : SlowInit0
|
2011-05-03 19:39:08 +00:00
|
|
|
+ (void) doNothing;
|
2011-04-20 09:41:12 +00:00
|
|
|
@end
|
2011-05-03 19:39:08 +00:00
|
|
|
|
2011-04-20 09:41:12 +00:00
|
|
|
@interface SlowInit2
|
2011-05-03 19:39:08 +00:00
|
|
|
+ (void) doNothing;
|
2011-04-20 09:41:12 +00:00
|
|
|
@end
|
2011-05-03 19:39:08 +00:00
|
|
|
|
|
|
|
static NSCondition *l;
|
2011-04-20 09:41:12 +00:00
|
|
|
static volatile int init0, init1, init2, init3;
|
2011-05-03 19:39:08 +00:00
|
|
|
|
2011-04-20 09:41:12 +00:00
|
|
|
@implementation SlowInit0
|
2011-05-03 19:39:08 +00:00
|
|
|
+ (void) initialize
|
2011-04-20 09:41:12 +00:00
|
|
|
{
|
2011-05-03 19:39:08 +00:00
|
|
|
init0 = 1;
|
2011-04-20 09:41:12 +00:00
|
|
|
}
|
|
|
|
@end
|
2011-05-03 19:39:08 +00:00
|
|
|
|
2011-04-20 09:41:12 +00:00
|
|
|
@implementation SlowInit1
|
|
|
|
/**
|
|
|
|
* Called from main thread.
|
|
|
|
*/
|
2011-05-03 19:39:08 +00:00
|
|
|
+ (void) initialize
|
2011-04-20 09:41:12 +00:00
|
|
|
{
|
2011-05-03 19:39:08 +00:00
|
|
|
PASS(init0 == 1, "Superclass +initialize called before subclass");
|
|
|
|
// Spin until we've entered the second +initialize method
|
|
|
|
while (init2 == 0) {}
|
|
|
|
// Wake up the other thread
|
|
|
|
[l signal];
|
|
|
|
[l unlock];
|
|
|
|
// Trigger the
|
|
|
|
[SlowInit2 doNothing];
|
|
|
|
init1 = 1;
|
2011-04-20 09:41:12 +00:00
|
|
|
}
|
2011-05-03 19:39:08 +00:00
|
|
|
+ (void) doNothing {}
|
2011-04-20 09:41:12 +00:00
|
|
|
@end
|
2011-05-03 19:39:08 +00:00
|
|
|
|
2011-04-20 09:41:12 +00:00
|
|
|
@implementation SlowInit2
|
|
|
|
/**
|
|
|
|
* Called from the second thread.
|
|
|
|
*/
|
2011-05-03 19:39:08 +00:00
|
|
|
+ (void) initialize
|
2011-04-20 09:41:12 +00:00
|
|
|
{
|
2011-05-03 19:39:08 +00:00
|
|
|
init2++;
|
|
|
|
// Sleep until the main thread is ready for us
|
|
|
|
[l lock];
|
|
|
|
/* If the runtime is doing the wrong thing and this is called twice, then
|
|
|
|
* there will be no signal. We don't want to deadlock, so make sure that
|
|
|
|
* this times out after a short while.
|
|
|
|
*/
|
|
|
|
[l waitUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.5]];
|
|
|
|
[l unlock];
|
|
|
|
[NSThread sleepForTimeInterval: 1];
|
|
|
|
PASS(init1 == 0, "First initialize method did not finish too early");
|
|
|
|
init3++;
|
2011-04-20 09:41:12 +00:00
|
|
|
}
|
|
|
|
static volatile int finished = 2;
|
2011-05-03 19:39:08 +00:00
|
|
|
+ (void) doNothing
|
2011-04-20 09:41:12 +00:00
|
|
|
{
|
2011-05-03 19:39:08 +00:00
|
|
|
PASS(init2 == 1, "+initialize called exactly once");
|
|
|
|
PASS(init3 == 1, "+initialize completed before another method started");
|
|
|
|
finished--;
|
2011-04-20 09:41:12 +00:00
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface Trampoline : NSObject
|
2011-05-03 19:39:08 +00:00
|
|
|
+ (void) launch: (id)ignored;
|
2011-04-20 09:41:12 +00:00
|
|
|
@end
|
2011-05-03 19:39:08 +00:00
|
|
|
|
2011-04-20 09:41:12 +00:00
|
|
|
@implementation Trampoline
|
|
|
|
/**
|
|
|
|
* Launch the second thread. NSThread retains its arguments in the main
|
|
|
|
* thread, we need to ensure that nothing triggers the second +initialize
|
|
|
|
* method until we're in the second thread.
|
|
|
|
*/
|
2011-05-03 19:39:08 +00:00
|
|
|
+ (void) launch: (id)ignored
|
2011-04-20 09:41:12 +00:00
|
|
|
{
|
2011-05-03 19:39:08 +00:00
|
|
|
[NSAutoreleasePool new];
|
|
|
|
[SlowInit2 doNothing];
|
2011-04-20 09:41:12 +00:00
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2011-05-03 19:39:08 +00:00
|
|
|
static void
|
|
|
|
alarmed(int sig)
|
|
|
|
{
|
|
|
|
/* Generate a dashed hope for concurrency testing.
|
|
|
|
*/
|
|
|
|
testHopeful = YES;
|
|
|
|
PASS(0, "+initialize runs concurrently");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2011-04-20 09:41:12 +00:00
|
|
|
/**
|
2011-05-03 19:39:08 +00:00
|
|
|
* Test the behaviour of +initialize.
|
|
|
|
* It's an undocumented (but nivce) feature that the Apple runtime lets
|
|
|
|
* both of the +initialize methods run concurrently, however the first
|
2011-04-20 09:41:12 +00:00
|
|
|
* one will block implicitly until the second one has completed.
|
|
|
|
*/
|
|
|
|
int main(void)
|
|
|
|
{
|
2011-05-03 19:39:08 +00:00
|
|
|
[NSAutoreleasePool new];
|
|
|
|
|
|
|
|
/* Make sure we have initalised all the classes necessary for the test
|
|
|
|
* framework to record a test ... by passing one.
|
|
|
|
*/
|
|
|
|
PASS(1, "initialize test starts");
|
|
|
|
|
2011-08-25 08:46:31 +00:00
|
|
|
#if defined(SIGALRM)
|
2011-05-03 19:39:08 +00:00
|
|
|
/* End in a signal if the concurrency test deadlocks.
|
|
|
|
*/
|
|
|
|
signal(SIGALRM, alarmed);
|
|
|
|
alarm(5);
|
2011-08-25 09:00:41 +00:00
|
|
|
#else
|
|
|
|
SKIP("+initialize runs concurrently");
|
2011-08-25 08:46:31 +00:00
|
|
|
#endif
|
2011-05-03 19:39:08 +00:00
|
|
|
|
|
|
|
l = [NSCondition new];
|
|
|
|
[l lock];
|
|
|
|
[NSThread detachNewThreadSelector: @selector(launch:)
|
|
|
|
toTarget: [Trampoline class]
|
|
|
|
withObject: nil];
|
|
|
|
[NSThread sleepForTimeInterval: 0.5];
|
|
|
|
[SlowInit1 doNothing];
|
|
|
|
[l lock];
|
|
|
|
[l unlock];
|
|
|
|
while (finished)
|
|
|
|
{
|
|
|
|
[NSThread sleepForTimeInterval: 0.01];
|
|
|
|
}
|
|
|
|
return 0;
|
2011-04-20 09:41:12 +00:00
|
|
|
}
|