1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
/* Test whether Objective-C runtime +initialize support is thread-safe
*/
#include "objc-common.g"
#include <stdio.h>
#if !defined(_MSC_VER)
#include <unistd.h>
#endif
#if defined(_WIN32)
#include <process.h>
typedef unsigned thread_id_t;
#define CREATE_THREAD(threadId, start, arg) \
_beginthreadex(NULL, 0, start, arg, 0, &threadId) != 0
#define mySleep(X) usleep(1000*(X))
#else
#include <pthread.h>
typedef pthread_t thread_id_t;
#define CREATE_THREAD(threadId, start, arg) \
pthread_create(&threadId, 0, start, arg) == 0
#define mySleep(X) sleep(X)
#endif
#if _MSC_VER
// Windows MSVC does not have usleep() (only MinGW does), so we use our own
#include <windows.h>
#ifdef interface
#undef interface // this is defined in windows.h but will break @interface
#endif
void usleep(__int64 usec) {
LARGE_INTEGER ft = {.QuadPart = -(10*usec)}; // convert to 100ns interval
HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
}
#endif
/* 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;
@interface MyClass : NSObject
@end
@implementation MyClass
+ (void) initialize
{
initialize_entered++;
while (NO == may_proceed)
;
initialize_exited++;
}
+ (Class) class
{
class_entered++;
return self;
}
@end
static
#if defined(_WIN32)
unsigned int __stdcall
#else
void *
#endif
test(void *arg)
{
[MyClass class];
return 0;
}
int
main()
{
thread_id_t t1;
thread_id_t t2;
unsigned counter;
if (CREATE_THREAD(t1, test, 0))
{
for (counter = 0; 0 == initialize_entered && counter < 5; counter++)
{
mySleep(1);
}
if (0 == initialize_entered)
{
fprintf(stderr, "Failed to initialize\n");
return 1;
}
if (CREATE_THREAD(t2, test, 0))
{
/* Wait long enough for t2 to try calling +class
*/
mySleep(1);
if (class_entered > 0)
{
fprintf(stderr, "class entered prematurely\n");
return 1;
}
/* Let t1 proceed and wait long enough for it to complete
* +initialize and for both threads to call +class
*/
may_proceed = YES;
for (counter = 0; 2 > class_entered && counter < 5; counter++)
{
mySleep(1);
}
if (2 == class_entered)
{
return 0; // OK
}
fprintf(stderr, "problem with initialize\n");
return 1;
}
else
{
fprintf(stderr, "failed to create t2\n");
return 1;
}
}
else
{
fprintf(stderr, "failed to create t1\n");
return 1;
}
}
|