2011-03-05 13:11:47 +00:00
|
|
|
/* Test whether Objective-C runtime +initialize support is thread-safe
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "objc-common.g"
|
|
|
|
#include <pthread.h>
|
2011-03-06 11:53:57 +00:00
|
|
|
#include <stdio.h>
|
2011-03-05 14:47:58 +00:00
|
|
|
|
|
|
|
#if defined(_WIN32)
|
2011-03-06 13:00:46 +00:00
|
|
|
# define mySleep(X) usleep(1000*(X))
|
2011-03-05 14:47:58 +00:00
|
|
|
#else
|
2011-03-06 13:00:46 +00:00
|
|
|
# define mySleep(X) sleep(X)
|
2011-03-05 14:47:58 +00:00
|
|
|
#endif
|
|
|
|
|
2011-03-07 10:47:04 +00:00
|
|
|
/* Use volatile variables so compiler optimisation won't prevent one thread
|
|
|
|
* from seeing changes made by another.
|
|
|
|
*/
|
|
|
|
static volatile unsigned initialize_entered = 0;
|
|
|
|
static volatile unsigned initialize_exited = 0;
|
|
|
|
static volatile unsigned class_entered = 0;
|
|
|
|
static volatile BOOL may_proceed = NO;
|
2011-03-05 13:11:47 +00:00
|
|
|
|
|
|
|
@interface MyClass : NSObject
|
|
|
|
@end
|
2011-03-06 11:53:57 +00:00
|
|
|
|
2011-03-05 13:11:47 +00:00
|
|
|
@implementation MyClass
|
2011-03-06 11:53:57 +00:00
|
|
|
|
2011-03-05 13:11:47 +00:00
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
initialize_entered++;
|
|
|
|
while (NO == may_proceed)
|
2011-03-07 10:47:04 +00:00
|
|
|
;
|
2011-03-05 13:11:47 +00:00
|
|
|
initialize_exited++;
|
|
|
|
}
|
2011-03-06 11:53:57 +00:00
|
|
|
|
2011-03-05 13:11:47 +00:00
|
|
|
+ (Class) class
|
|
|
|
{
|
|
|
|
class_entered++;
|
|
|
|
return self;
|
|
|
|
}
|
2011-03-06 11:53:57 +00:00
|
|
|
|
2011-03-05 13:11:47 +00:00
|
|
|
@end
|
|
|
|
|
2011-03-07 10:25:21 +00:00
|
|
|
static void *
|
|
|
|
test(void *arg)
|
2011-03-05 13:11:47 +00:00
|
|
|
{
|
|
|
|
[MyClass class];
|
2011-03-07 10:25:21 +00:00
|
|
|
return 0;
|
2011-03-05 13:11:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main()
|
|
|
|
{
|
2011-03-06 11:53:57 +00:00
|
|
|
pthread_t t1;
|
|
|
|
pthread_t t2;
|
2011-03-06 13:08:20 +00:00
|
|
|
unsigned counter;
|
2011-03-05 13:11:47 +00:00
|
|
|
|
|
|
|
if (0 == pthread_create(&t1, 0, test, 0))
|
|
|
|
{
|
2011-03-06 13:08:20 +00:00
|
|
|
for (counter = 0; 0 == initialize_entered && counter < 5; counter++)
|
2011-03-06 13:00:46 +00:00
|
|
|
{
|
|
|
|
mySleep(1);
|
|
|
|
}
|
2011-03-06 11:53:57 +00:00
|
|
|
|
2011-03-05 13:11:47 +00:00
|
|
|
if (0 == initialize_entered)
|
2011-03-06 13:00:46 +00:00
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to initialize\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2011-03-06 11:53:57 +00:00
|
|
|
|
|
|
|
if (0 == pthread_create(&t2, 0, test, 0))
|
|
|
|
{
|
2011-03-06 13:00:46 +00:00
|
|
|
/* Wait long enough for t2 to try calling +class
|
|
|
|
*/
|
|
|
|
mySleep(1);
|
2011-03-06 11:53:57 +00:00
|
|
|
|
2011-03-06 13:00:46 +00:00
|
|
|
if (class_entered > 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "class entered prematurely\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2011-03-06 11:53:57 +00:00
|
|
|
|
2011-03-06 13:00:46 +00:00
|
|
|
/* Let t1 proceed and wait long enough for it to complete
|
|
|
|
* +initialize and for both threads to call +class
|
|
|
|
*/
|
|
|
|
may_proceed = YES;
|
2011-03-06 13:08:20 +00:00
|
|
|
for (counter = 0; 2 > class_entered && counter < 5; counter++)
|
|
|
|
{
|
|
|
|
mySleep(1);
|
|
|
|
}
|
2011-03-06 11:53:57 +00:00
|
|
|
|
2011-03-06 13:00:46 +00:00
|
|
|
if (2 == class_entered)
|
2011-03-06 11:53:57 +00:00
|
|
|
{
|
2011-03-06 13:00:46 +00:00
|
|
|
return 0; // OK
|
2011-03-06 11:53:57 +00:00
|
|
|
}
|
2011-03-06 13:00:46 +00:00
|
|
|
fprintf(stderr, "problem with initialize\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "failed to create t2\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2011-03-05 13:11:47 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "failed to create t1\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|