mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 08:26:27 +00:00
145 lines
2.8 KiB
Objective-C
145 lines
2.8 KiB
Objective-C
/* 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;
|
|
}
|
|
}
|
|
|