Files
2013-04-25 13:38:03 -07:00

388 lines
10 KiB
C

// Copyright (c) 2013 Adobe Systems Inc
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#define THREADS 4
#define BIG_NUMBER_SYNC 1000000
#define BIG_NUMBER_MUTEX 60000
#define BIG_NUMBER_SPECIFIC 10000000
int sync_pass = 0;
int mutex_pass = 0;
int specific_pass = 0;
int cond_signal_pass = 0;
int cond_broadcast_pass = 0;
int rwlock_pass = 0;
int rwlock_acquired_0 = 0;
int rwlock_acquired_1 = 0;
int rwlock_acquired_2 = 0;
int rwlock_acquired_3 = 0;
int rwlock_released_0 = 0;
int rwlock_released_1 = 0;
int rwlock_released_2 = 0;
int rwlock_released_3 = 0;
int rwkill_pass = 0;
int azr_error = 0;
int final_pass = 0;
int specific_counter = 0;
pthread_mutex_t specific_counter_lock;
#define AZR(X) \
do { if((X) != 0){ \
err("[" #X "] had non-zero return"); \
azr_error = 1; \
} \
} while(0)
static void err(const char *str)
{
fprintf(stderr, "error: %s\n", str);
exit(-1);
}
static void *test_sync_tp(void *arg)
{
int i;
for(i = 0; i < BIG_NUMBER_SYNC; i++)
__sync_fetch_and_add((int *)arg, 1);
return NULL;
}
static void test_sync()
{
int counter = 0;
int i;
pthread_t threads[THREADS];
for(i = 0; i < THREADS; i++)
AZR(pthread_create(threads + i, NULL, test_sync_tp, &counter));
for(i = 0; i < THREADS; i++)
AZR(pthread_join(threads[i], NULL));
printf("\tcounter: %d\n", counter);
if(counter == BIG_NUMBER_SYNC * THREADS){
sync_pass = 1;
}
}
struct tm_args
{
int counter;
pthread_mutex_t mutex;
};
static void *test_mutex_tp(void *arg)
{
int i;
volatile struct tm_args *args = (struct tm_args *)arg;
for(i = 0; i < BIG_NUMBER_MUTEX; i++)
{
AZR(pthread_mutex_lock(&args->mutex));
args->counter++;
AZR(pthread_mutex_unlock(&args->mutex));
}
return NULL;
}
static void test_mutex()
{
int i;
pthread_t threads[THREADS];
volatile struct tm_args args = { 0, PTHREAD_MUTEX_INITIALIZER };
for(i = 0; i < THREADS; i++)
AZR(pthread_create(threads + i, NULL, test_mutex_tp, &args));
for(i = 0; i < THREADS; i++)
AZR(pthread_join(threads[i], NULL));
AZR(pthread_mutex_destroy(&args.mutex));
printf("\tcounter: %d\n", args.counter);
if(args.counter == BIG_NUMBER_MUTEX * THREADS){
mutex_pass = 1;
}
}
static void test_specific_kd(void *val)
{
printf("\tspecific: %d\n", (int)val);
pthread_mutex_lock(&specific_counter_lock);
// Every thread must report BIG_NUMBER_SPECIFIC, and
// we must get that THREADS times.
if((int)val == BIG_NUMBER_SPECIFIC){
++specific_counter;
}
if(specific_counter == THREADS){
specific_pass = 1;
}else{
specific_pass = 0;
}
pthread_mutex_unlock(&specific_counter_lock);
}
static void *test_specific_tp(void *arg)
{
pthread_key_t *key = (pthread_key_t *)arg;
int i;
for(i = 0; i < BIG_NUMBER_SPECIFIC; i++)
AZR(pthread_setspecific(*key, (void *)(1 + (int)pthread_getspecific(*key))));
return NULL;
}
static void test_specific()
{
int i;
pthread_t threads[THREADS];
pthread_key_t key;
AZR(pthread_key_create(&key, &test_specific_kd));
for(i = 0; i < THREADS; i++)
AZR(pthread_create(threads + i, NULL, test_specific_tp, &key));
for(i = 0; i < THREADS; i++)
AZR(pthread_join(threads[i], NULL));
AZR(pthread_key_delete(key));
}
struct tcs_args
{
int tid;
int counter;
pthread_cond_t cond;
pthread_mutex_t mutex;
};
static void *test_cond_tp(void *arg)
{
volatile struct tcs_args *args = (struct tcs_args *)arg;
int tid = __sync_fetch_and_add(&args->tid, 1);
AZR(pthread_mutex_lock(&args->mutex));
__sync_fetch_and_add(&args->counter, 1);
AZR(pthread_cond_wait(&args->cond, &args->mutex));
__sync_fetch_and_sub(&args->counter, 1);
AZR(pthread_mutex_unlock(&args->mutex));
}
static void test_cond_signal()
{
int i;
pthread_t threads[THREADS];
volatile struct tcs_args args = { 0, 0, PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER };
for(i = 0; i < THREADS; i++)
AZR(pthread_create(threads + i, NULL, test_cond_tp, &args));
while(args.counter != THREADS);
printf("\tall threads started\n");
// make sure they're all waiting
AZR(pthread_mutex_lock(&args.mutex));
AZR(pthread_mutex_unlock(&args.mutex));
printf("\tall threads waiting\n");
// they're all waiting on the condition now, signal one at a time
for(i = THREADS-1; i >= 0; i--)
{
if(args.counter != (i+1))
err("unexpected counter value in test_cond_signal");
AZR(pthread_mutex_lock(&args.mutex));
AZR(pthread_cond_signal(&args.cond));
AZR(pthread_mutex_unlock(&args.mutex));
while(args.counter != i);
}
printf("\tall threads signaled\n");
cond_signal_pass = 1;
for(i = 0; i < THREADS; i++)
AZR(pthread_join(threads[i], NULL));
AZR(pthread_cond_destroy(&args.cond));
AZR(pthread_mutex_destroy(&args.mutex));
}
static void test_cond_broadcast()
{
int i;
pthread_t threads[THREADS];
volatile struct tcs_args args = { 0, 0, PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER };
for(i = 0; i < THREADS; i++)
AZR(pthread_create(threads + i, NULL, test_cond_tp, &args));
while(args.counter != THREADS); // wait for them all to start up
printf("\tall threads started\n");
// make sure they're all waiting
AZR(pthread_mutex_lock(&args.mutex));
AZR(pthread_mutex_unlock(&args.mutex));
printf("\tall threads waiting\n");
// they're all waiting on the condition now, signal all at once!
AZR(pthread_mutex_lock(&args.mutex));
AZR(pthread_cond_broadcast(&args.cond));
AZR(pthread_mutex_unlock(&args.mutex));
while(args.counter != 0);
printf("\tall threads signaled\n");
cond_broadcast_pass = 1;
for(i = 0; i < THREADS; i++)
AZR(pthread_join(threads[i], NULL));
AZR(pthread_cond_destroy(&args.cond));
AZR(pthread_mutex_destroy(&args.mutex));
}
static void *test_kill_tp(void *arg)
{
for(;;)
__sync_fetch_and_add((int *)arg, 1);
retun NULL;
}
static void test_kill()
{
pthread_t thread;
volatile int counter = 0;
AZR(pthread_create(&thread, NULL, test_kill_tp, &counter));
printf("\tthread created\n");
while(counter < 100);
AZR(pthread_kill(thread, SIGUSR1));
AZR(pthread_join(thread, NULL));
printf("\tthread killed\n");
rwkill_pass = 1;
}
struct trw_args
{
int tid;
int readers;
pthread_rwlock_t rwlock;
};
static void *test_rwlock_tp(void *arg)
{
volatile struct trw_args *args = (struct trw_args *)arg;
int tid = __sync_fetch_and_add(&args->tid, 1);
while(tid != args->readers);
AZR(pthread_rwlock_rdlock(&args->rwlock));
printf("\trwlock acquired %d\n", tid);
if(tid == 0)
rwlock_acquired_0 = 1;
else if(tid == 1)
rwlock_acquired_1 = 1;
else if(tid == 2)
rwlock_acquired_2 = 1;
else if(tid == 3)
rwlock_acquired_3 = 1;
args->readers++;
while(args->readers != tid);
AZR(pthread_rwlock_unlock(&args->rwlock));
printf("\trwlock released %d\n", tid);
if(tid == 0)
rwlock_released_0 = 1;
else if(tid == 1)
rwlock_released_1 = 1;
else if(tid == 2)
rwlock_released_2 = 1;
else if(tid == 3)
rwlock_released_3 = 1;
args->readers--;
return NULL;
}
static void test_rwlock()
{
int i;
pthread_t threads[THREADS];
volatile struct trw_args args = { 0, 0, PTHREAD_RWLOCK_INITIALIZER };
AZR(pthread_rwlock_wrlock(&args.rwlock));
for(i = 0; i < THREADS; i++)
AZR(pthread_create(threads + i, NULL, test_rwlock_tp, &args));
AZR(pthread_rwlock_unlock(&args.rwlock));
while(args.readers != THREADS);
printf("\tall readers acquired locks\n");
if(pthread_rwlock_trywrlock(&args.rwlock) != EBUSY)
err("pthread_rwlock_trywrlock didn't return EBUSY in test_rwlock");
printf("\twr lock not acquired\n");
args.readers--;
AZR(pthread_rwlock_wrlock(&args.rwlock));
printf("\twr lock acquired\n");
AZR(pthread_rwlock_unlock(&args.rwlock));
for(i = 0; i < THREADS; i++)
AZR(pthread_join(threads[i], NULL));
AZR(pthread_rwlock_destroy(&args.rwlock));
rwlock_pass = rwlock_acquired_0 && rwlock_acquired_1 && rwlock_acquired_2 && rwlock_acquired_3 &&
rwlock_released_0 && rwlock_released_1 && rwlock_released_2 && rwlock_released_3;
}
void sigusr1(int dummy)
{
pthread_exit(NULL); // just exit this thread (emulate behavior we'll have in player...)
}
int main()
{
if (pthread_mutex_init(&specific_counter_lock, NULL) != 0)
{
printf("\nspecific_counter_lock init failed\n");
return 1;
}
signal(SIGUSR1, sigusr1);
printf("testing __sync\n");
test_sync(); // test __sync stuff
printf("testing mutex\n");
test_mutex(); // test mutex stuff
printf("testing specific\n");
test_specific(); // test get/setspecific stuff
printf("testing cond_signal\n");
test_cond_signal();
printf("testing cond_broadcast\n");
test_cond_broadcast();
printf("testing rwlock\n");
test_rwlock();
printf("testing kill\n");
test_kill();
final_pass = !azr_error && sync_pass && mutex_pass && specific_pass &&
cond_signal_pass && cond_broadcast_pass &&
rwlock_pass && rwkill_pass;
if( final_pass ){
printf("RESULT=PASS\n");
}else{
printf("RESULT=FAIL\n");
}
return 0;
}