blob: 3da82f17784c1f6471907e930417fb2035edb46f [file] [log] [blame]
/*
* Copyright 2009 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/*
* simple test for NativeClient threads
*/
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <string.h>
#define kNumThreads 2
#define kNumRounds 10
#define kHelloWorldString "hello world\n"
#ifdef __GLIBC__
/*
* This is a non-standard typedef that nacl-newlib provides but
* nacl-glibc does not provide.
*/
typedef int AtomicInt32;
#endif
/* tls */
__thread int tls_var_data_int = 666;
__thread char tls_var_data_string[100] = kHelloWorldString;
__thread int tls_var_bss_int;
__thread char tls_var_bss_string[100];
int GlobalCounter = 0;
AtomicInt32 GlobalError = 0;
void IncError(void) {
__sync_fetch_and_add(&GlobalError, 1);
}
#define USE_PTHREAD_LIB
#if defined(USE_PTHREAD_LIB)
pthread_mutex_t mutex;
void Init(void) {
pthread_mutex_init(&mutex, NULL);
}
void CriticalSectionEnter(void) {
pthread_mutex_lock(&mutex);
}
void CriticalSectionLeave(void) {
pthread_mutex_unlock(&mutex);
}
#else
AtomicWord GlobalMutex = 0;
void Init(void) {
}
void CriticalSectionEnter(void) {
int old_value;
do {
old_value = AtomicExchange(&GlobalMutex, 1);
} while (1 == old_value);
}
void CriticalSectionLeave(void) {
AtomicExchange(&GlobalMutex, 0);
}
#endif
void CounterWait(int id, int round) {
for (;;) {
CriticalSectionEnter();
if (round * kNumThreads + id == GlobalCounter) {
CriticalSectionLeave();
break;
}
CriticalSectionLeave();
/* Give another thread a chance to do its work. */
sched_yield();
}
}
void CounterAdvance(int id, int round) {
CriticalSectionEnter();
GlobalCounter = round * kNumThreads + id + 1;
CriticalSectionLeave();
/* Give another thread a chance to do its work. */
sched_yield();
}
void CheckAndUpdateTlsVars(int id, int round) {
if (0 == round) {
if (666 != tls_var_data_int ||
0 != strcmp(tls_var_data_string, kHelloWorldString)) {
printf("[%d] ERROR: bad initial tls data\n", id);
IncError();
}
if ( 0 != tls_var_bss_int ||
0 != strcmp(tls_var_bss_string, "")) {
printf("[%d] ERROR: bad initial tls bss\n", id);
IncError();
}
} else {
if (round * kNumThreads + id != tls_var_data_int ||
0 != strcmp(tls_var_data_string, kHelloWorldString)) {
printf("[%d] ERROR: bad tls data\n", id);
IncError();
}
if (round * kNumThreads + id != tls_var_bss_int ||
0 != strcmp(tls_var_bss_string, kHelloWorldString)) {
printf("[%d] ERROR: bad tls bss\n", id);
IncError();
}
}
/* prepare tls data for next round */
tls_var_data_int = (round + 1) * kNumThreads + id;
tls_var_bss_int = (round + 1) * kNumThreads + id;
strcpy(tls_var_bss_string, tls_var_data_string);
}
void* MyThreadFunction(void* arg) {
int id = *(int *)arg;
int r;
printf("[%d] entering thread\n", id);
if (id < 0 || id >= kNumThreads) {
printf("[%d] ERROR: bad id\n", id);
return 0;
}
for (r = 0; r < kNumRounds; ++r) {
printf("[%d] before round %d\n", id, r);
CounterWait(id, r);
CheckAndUpdateTlsVars(id, r);
CounterAdvance(id, r);
printf("[%d] after round %d\n", id, r);
}
printf("[%d] exiting thread\n", id);
return 0;
}
int main(int argc, char *argv[]) {
pthread_t tid[kNumThreads];
int ids[kNumThreads];
int i = 0;
Init();
for (i = 0; i < kNumThreads; ++i) {
int rv;
ids[i] = i;
printf("creating thread %d\n", i);
rv = pthread_create(&tid[i], NULL, MyThreadFunction, &ids[i]);
if (rv != 0) {
printf("ERROR: in thread creation\n");
IncError();
}
}
for (i = 0; i < kNumThreads; ++i) {
pthread_join(tid[i], NULL);
}
return GlobalError;
}