/* * %CopyrightBegin% * * Copyright Ericsson AB 2004-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * %CopyrightEnd% */ /* * Description: Test suite for the ethread thread library. * Author: Rickard Green */ #define ETHR_NO_SUPP_THR_LIB_NOT_FATAL #include "ethread.h" #include "erl_misc_utils.h" #include #include #include #ifndef __WIN32__ #include #endif #include /* * Auxiliary functions */ #define PRINT_VA_LIST(FRMT) \ do { \ if (FRMT && FRMT != '\0') { \ va_list args; \ va_start(args, FRMT); \ vfprintf(stderr, FRMT, args); \ va_end(args); \ } \ } while (0) #define ASSERT(B) \ do { \ if (!(B)) \ fail("%s:%d: Assertion \"%s\" failed!",__FILE__,__LINE__,#B); \ } while (0) #define ASSERT_PRINT(B, PRNT) \ do { \ if (!(B)) { \ print PRNT; \ fail("%s:%d: Assertion \"%s\" failed!",__FILE__,__LINE__,#B); \ } \ } while (0) #define ASSERT_EQ(VAR, VAL, FSTR) \ do { \ if ((VAR) != (VAL)) { \ print("%s=" FSTR "\n", #VAR, (VAR)); \ fail("%s:%d: Assertion \"%s == " FSTR "\" failed!", \ __FILE__, __LINE__, #VAR, (VAL)); \ } \ } while (0) #ifdef __WIN32_ #define EOL "\r\n" #else #define EOL "\n" #endif static void print_eol(void) { fprintf(stderr, EOL); fflush(stderr); } static void print_line(char *frmt,...) { PRINT_VA_LIST(frmt); print_eol(); } #if 0 /* Currently not used; silence annoying warning... */ static void print(char *frmt,...) { PRINT_VA_LIST(frmt); } #endif static void fail(char *frmt,...) { char *abrt_env; print_eol(); fprintf(stderr, "ETHR-TEST-FAILURE"); PRINT_VA_LIST(frmt); print_eol(); abrt_env = getenv("ERL_ABORT_ON_FAILURE"); if (abrt_env && strcmp("true", abrt_env) == 0) abort(); else exit(1); } static void skip(char *frmt,...) { print_eol(); fprintf(stderr, "ETHR-TEST-SKIP"); PRINT_VA_LIST(frmt); print_eol(); exit(0); } static void succeed(char *frmt,...) { print_eol(); fprintf(stderr, "ETHR-TEST-SUCCESS"); PRINT_VA_LIST(frmt); print_eol(); exit(0); } static void do_sleep(unsigned secs) { while (erts_milli_sleep(secs*1000) != 0); } #define WAIT_UNTIL_INTERVAL 10 #define WAIT_UNTIL_LIM(TEST, LIM) \ do { \ int ms__ = (LIM)*1000; \ while (!(TEST)) { \ while (erts_milli_sleep(WAIT_UNTIL_INTERVAL) != 0); \ ms__ -= WAIT_UNTIL_INTERVAL; \ if (ms__ <= 0) \ break; \ } \ } while (0) static void send_my_pid(void) { #ifndef __WIN32__ int pid = (int) getpid(); fprintf(stderr, EOL "ETHR-TEST-PID%d" EOL, pid); #endif } /* * The test-cases */ #ifndef ETHR_NO_THREAD_LIB /* * The create join thread test case. * * Tests ethr_thr_create and ethr_thr_join. */ #define CJTT_NO_THREADS 64 ethr_tid cjtt_tids[CJTT_NO_THREADS + 1]; int cjtt_ix[CJTT_NO_THREADS + 1]; int cjtt_res[CJTT_NO_THREADS + 1]; void *cjtt_thread(void *vpix) { int ix = *((int *) vpix); cjtt_res[ix] = ix; return (void *) &cjtt_res[ix]; } static void create_join_thread_test(void) { int i, res; for (i = 1; i <= CJTT_NO_THREADS; i++) { cjtt_ix[i] = i; cjtt_res[i] = 0; } for (i = 1; i <= CJTT_NO_THREADS; i++) { res = ethr_thr_create(&cjtt_tids[i], cjtt_thread, (void *) &cjtt_ix[i], NULL); ASSERT(res == 0); } for (i = 1; i <= CJTT_NO_THREADS; i++) { void *tres; res = ethr_thr_join(cjtt_tids[i], &tres); ASSERT(res == 0); ASSERT(tres == &cjtt_res[i]); ASSERT(cjtt_res[i] == i); } } /* * The eq tid test case. * * Tests ethr_equal_tids. */ #define ETT_THREADS 100000 static ethr_tid ett_tids[3]; static ethr_mutex ett_mutex; static ethr_cond ett_cond; static int ett_terminate; static void * ett_thread(void *my_tid) { ASSERT(!ethr_equal_tids(ethr_self(), ett_tids[0])); ASSERT(ethr_equal_tids(ethr_self(), *((ethr_tid *) my_tid))); return NULL; } static void * ett_thread2(void *unused) { int res; ethr_mutex_lock(&ett_mutex); while (!ett_terminate) { res = ethr_cond_wait(&ett_cond, &ett_mutex); ASSERT(res == 0); } ethr_mutex_unlock(&ett_mutex); return NULL; } static void equal_tids_test(void) { int res, i; res = ethr_mutex_init(&ett_mutex); ASSERT(res == 0); res = ethr_cond_init(&ett_cond); ASSERT(res == 0); ett_tids[0] = ethr_self(); res = ethr_thr_create(&ett_tids[1], ett_thread, (void *) &ett_tids[1], NULL); ASSERT(res == 0); ASSERT(ethr_equal_tids(ethr_self(), ett_tids[0])); ASSERT(!ethr_equal_tids(ethr_self(), ett_tids[1])); res = ethr_thr_join(ett_tids[1], NULL); res = ethr_thr_create(&ett_tids[2], ett_thread, (void *) &ett_tids[2], NULL); ASSERT(res == 0); ASSERT(ethr_equal_tids(ethr_self(), ett_tids[0])); ASSERT(!ethr_equal_tids(ethr_self(), ett_tids[1])); ASSERT(!ethr_equal_tids(ethr_self(), ett_tids[2])); #if 0 /* This fails on some linux platforms. Until we decides if a tid * is allowed to be reused right away or not, we disable the test. */ ASSERT(!ethr_equal_tids(ett_tids[1], ett_tids[2])); #endif res = ethr_thr_join(ett_tids[2], NULL); ASSERT(res == 0); /* Second part of test */ ett_terminate = 0; res = ethr_thr_create(&ett_tids[1], ett_thread2, NULL, NULL); ASSERT(res == 0); ASSERT(!ethr_equal_tids(ett_tids[0], ett_tids[1])); for (i = 0; i < ETT_THREADS; i++) { res = ethr_thr_create(&ett_tids[2], ett_thread, (void*)&ett_tids[2], NULL); ASSERT(res == 0); ASSERT(!ethr_equal_tids(ett_tids[0], ett_tids[2])); ASSERT(!ethr_equal_tids(ett_tids[1], ett_tids[2])); res = ethr_thr_join(ett_tids[2], NULL); ASSERT(res == 0); } ethr_mutex_lock(&ett_mutex); ett_terminate = 1; ethr_cond_signal(&ett_cond); ethr_mutex_unlock(&ett_mutex); res = ethr_thr_join(ett_tids[1], NULL); ASSERT(res == 0); res = ethr_cond_destroy(&ett_cond); ASSERT(res == 0); res = ethr_mutex_destroy(&ett_mutex); ASSERT(res == 0); } /* * The mutex test case. * * Tests mutexes. */ static ethr_mutex mt_mutex; static int mt_data; void * mt_thread(void *unused) { print_line("Aux thread tries to lock mutex"); ethr_mutex_lock(&mt_mutex); print_line("Aux thread locked mutex"); ASSERT(mt_data == 0); mt_data = 1; print_line("Aux thread wrote"); print_line("Aux thread goes to sleep for 1 second"); do_sleep(1); print_line("Aux thread woke up"); ASSERT(mt_data == 1); ethr_mutex_unlock(&mt_mutex); print_line("Aux thread unlocked mutex"); return NULL; } static void mutex_test(void) { int res; ethr_tid tid; print_line("Trying to initialize mutex"); res = ethr_mutex_init(&mt_mutex); ASSERT(res == 0); print_line("Initialized mutex"); mt_data = 0; print_line("Main thread tries to lock mutex"); ethr_mutex_lock(&mt_mutex); print_line("Main thread locked mutex"); ASSERT(mt_data == 0); print_line("Main thread about to create aux thread"); res = ethr_thr_create(&tid, mt_thread, NULL, NULL); ASSERT(res == 0); print_line("Main thread created aux thread"); print_line("Main thread goes to sleep for 1 second"); do_sleep(1); print_line("Main thread woke up"); ASSERT(mt_data == 0); ethr_mutex_unlock(&mt_mutex); print_line("Main thread unlocked mutex"); print_line("Main thread goes to sleep for 1 second"); do_sleep(1); print_line("Main thread woke up"); print_line("Main thread tries to lock mutex"); ethr_mutex_lock(&mt_mutex); print_line("Main thread locked mutex"); ASSERT(mt_data == 1); print_line("Main thread goes to sleep for 1 second"); do_sleep(1); print_line("Main thread woke up"); ASSERT(mt_data == 1); ethr_mutex_unlock(&mt_mutex); print_line("Main thread unlocked mutex"); res = ethr_thr_join(tid, NULL); ASSERT(res == 0); print_line("Main thread joined aux thread"); res = ethr_mutex_destroy(&mt_mutex); ASSERT(res == 0); print_line("Main thread destroyed mutex"); } /* * The try lock mutex test case. * * Tests try lock mutex operation. */ static ethr_mutex tlmt_mtx1; static ethr_mutex tlmt_mtx2; static ethr_cond tlmt_cnd2; static int tlmt_mtx1_locked; static int tlmt_mtx1_do_unlock; static void * tlmt_thread(void *unused) { int res; ethr_mutex_lock(&tlmt_mtx1); ethr_mutex_lock(&tlmt_mtx2); tlmt_mtx1_locked = 1; ethr_cond_signal(&tlmt_cnd2); while (!tlmt_mtx1_do_unlock) { res = ethr_cond_wait(&tlmt_cnd2, &tlmt_mtx2); ASSERT(res == 0 || res == EINTR); } ethr_mutex_unlock(&tlmt_mtx2); ethr_mutex_unlock(&tlmt_mtx1); ethr_mutex_lock(&tlmt_mtx2); tlmt_mtx1_locked = 0; ethr_cond_signal(&tlmt_cnd2); ethr_mutex_unlock(&tlmt_mtx2); return NULL; } static void try_lock_mutex_test(void) { int i, res; ethr_tid tid; res = ethr_mutex_init(&tlmt_mtx1); ASSERT(res == 0); res = ethr_mutex_init(&tlmt_mtx2); ASSERT(res == 0); res = ethr_cond_init(&tlmt_cnd2); ASSERT(res == 0); tlmt_mtx1_locked = 0; tlmt_mtx1_do_unlock = 0; res = ethr_thr_create(&tid, tlmt_thread, NULL, NULL); ASSERT(res == 0); ethr_mutex_lock(&tlmt_mtx2); while (!tlmt_mtx1_locked) { res = ethr_cond_wait(&tlmt_cnd2, &tlmt_mtx2); ASSERT(res == 0 || res == EINTR); } ethr_mutex_unlock(&tlmt_mtx2); for (i = 0; i < 10; i++) { res = ethr_mutex_trylock(&tlmt_mtx1); ASSERT(res == EBUSY); } ethr_mutex_lock(&tlmt_mtx2); tlmt_mtx1_do_unlock = 1; ethr_cond_signal(&tlmt_cnd2); while (tlmt_mtx1_locked) { res = ethr_cond_wait(&tlmt_cnd2, &tlmt_mtx2); ASSERT(res == 0 || res == EINTR); } ethr_mutex_unlock(&tlmt_mtx2); res = ethr_mutex_trylock(&tlmt_mtx1); ASSERT(res == 0); ethr_mutex_unlock(&tlmt_mtx1); res = ethr_thr_join(tid, NULL); ASSERT(res == 0); res = ethr_mutex_destroy(&tlmt_mtx1); ASSERT(res == 0); res = ethr_mutex_destroy(&tlmt_mtx2); ASSERT(res == 0); res = ethr_cond_destroy(&tlmt_cnd2); ASSERT(res == 0); } /* * The cond wait test case. * * Tests ethr_cond_wait with ethr_cond_signal and ethr_cond_broadcast. */ static ethr_mutex cwt_mutex; static ethr_cond cwt_cond; static int cwt_counter; void * cwt_thread(void *unused) { int res; ethr_mutex_lock(&cwt_mutex); do { res = ethr_cond_wait(&cwt_cond, &cwt_mutex); } while (res == EINTR); ASSERT(res == 0); cwt_counter++; ethr_mutex_unlock(&cwt_mutex); return NULL; } static void cond_wait_test(void) { ethr_tid tid1, tid2; int res; res = ethr_mutex_init(&cwt_mutex); ASSERT(res == 0); res = ethr_cond_init(&cwt_cond); ASSERT(res == 0); /* Wake with signal */ cwt_counter = 0; res = ethr_thr_create(&tid1, cwt_thread, NULL, NULL); ASSERT(res == 0); res = ethr_thr_create(&tid2, cwt_thread, NULL, NULL); ASSERT(res == 0); do_sleep(1); /* Make sure threads waits on cond var */ ethr_mutex_lock(&cwt_mutex); ethr_cond_signal(&cwt_cond); /* Wake one thread */ do_sleep(1); /* Make sure awakened thread waits on mutex */ ASSERT(cwt_counter == 0); ethr_mutex_unlock(&cwt_mutex); do_sleep(1); /* Let awakened thread proceed */ ethr_mutex_lock(&cwt_mutex); ASSERT(cwt_counter == 1); ethr_cond_signal(&cwt_cond); /* Wake the other thread */ do_sleep(1); /* Make sure awakened thread waits on mutex */ ASSERT(cwt_counter == 1); ethr_mutex_unlock(&cwt_mutex); do_sleep(1); /* Let awakened thread proceed */ ethr_mutex_lock(&cwt_mutex); ASSERT(cwt_counter == 2); ethr_mutex_unlock(&cwt_mutex); res = ethr_thr_join(tid1, NULL); ASSERT(res == 0); res = ethr_thr_join(tid2, NULL); ASSERT(res == 0); /* Wake with broadcast */ cwt_counter = 0; res = ethr_thr_create(&tid1, cwt_thread, NULL, NULL); ASSERT(res == 0); res = ethr_thr_create(&tid2, cwt_thread, NULL, NULL); ASSERT(res == 0); do_sleep(1); /* Make sure threads waits on cond var */ ethr_mutex_lock(&cwt_mutex); ethr_cond_broadcast(&cwt_cond); /* Wake the threads */ do_sleep(1); /* Make sure awakened threads wait on mutex */ ASSERT(cwt_counter == 0); ethr_mutex_unlock(&cwt_mutex); do_sleep(1); /* Let awakened threads proceed */ ethr_mutex_lock(&cwt_mutex); ASSERT(cwt_counter == 2); ethr_mutex_unlock(&cwt_mutex); res = ethr_thr_join(tid1, NULL); ASSERT(res == 0); res = ethr_thr_join(tid2, NULL); ASSERT(res == 0); res = ethr_mutex_destroy(&cwt_mutex); ASSERT(res == 0); res = ethr_cond_destroy(&cwt_cond); ASSERT(res == 0); } /* * The broadcast test case. * * Tests that a ethr_cond_broadcast really wakes up all waiting threads. */ #define BCT_THREADS 64 #define BCT_NO_OF_WAITS 100 static int bct_woken = 0; static int bct_waiting = 0; static int bct_done = 0; static ethr_mutex bct_mutex; static ethr_cond bct_cond; static ethr_cond bct_cntrl_cond; static void * bct_thread(void *unused) { int res; ethr_mutex_lock(&bct_mutex); while (!bct_done) { bct_waiting++; if (bct_waiting == BCT_THREADS) ethr_cond_signal(&bct_cntrl_cond); do { res = ethr_cond_wait(&bct_cond, &bct_mutex); } while (res == EINTR); ASSERT(res == 0); bct_woken++; if (bct_woken == BCT_THREADS) ethr_cond_signal(&bct_cntrl_cond); } ethr_mutex_unlock(&bct_mutex); return NULL; } static void broadcast_test(void) { int res, i; ethr_tid tid[BCT_THREADS]; res = ethr_mutex_init(&bct_mutex); ASSERT(res == 0); res = ethr_cond_init(&bct_cntrl_cond); ASSERT(res == 0); res = ethr_cond_init(&bct_cond); ASSERT(res == 0); for (i = 0; i < BCT_THREADS; i++) { res = ethr_thr_create(&tid[i], bct_thread, NULL, NULL); ASSERT(res == 0); } ethr_mutex_lock(&bct_mutex); for (i = 0; i < BCT_NO_OF_WAITS; i++) { while (bct_waiting != BCT_THREADS) { res = ethr_cond_wait(&bct_cntrl_cond, &bct_mutex); ASSERT(res == 0 || res == EINTR); } bct_waiting = 0; bct_woken = 0; /* Wake all threads */ ethr_cond_broadcast(&bct_cond); while (bct_woken != BCT_THREADS) { res = ethr_cond_wait(&bct_cntrl_cond, &bct_mutex); ASSERT(res == 0 || res == EINTR); } } bct_done = 1; /* Wake all threads */ ethr_cond_broadcast(&bct_cond); ethr_mutex_unlock(&bct_mutex); for (i = 0; i < BCT_THREADS; i++) { res = ethr_thr_join(tid[i], NULL); ASSERT(res == 0); } res = ethr_mutex_destroy(&bct_mutex); ASSERT(res == 0); res = ethr_cond_destroy(&bct_cntrl_cond); ASSERT(res == 0); res = ethr_cond_destroy(&bct_cond); ASSERT(res == 0); } /* * The detached thread test case. * * Tests detached threads. */ #define DT_THREADS (50*1024) #define DT_BATCH_SIZE 64 static ethr_mutex dt_mutex; static ethr_cond dt_cond; static int dt_count; static int dt_limit; static void * dt_thread(void *unused) { ethr_mutex_lock(&dt_mutex); dt_count++; if (dt_count >= dt_limit) ethr_cond_signal(&dt_cond); ethr_mutex_unlock(&dt_mutex); return NULL; } static void detached_thread_test(void) { ethr_thr_opts thr_opts = ETHR_THR_OPTS_DEFAULT_INITER; ethr_tid tid[DT_BATCH_SIZE]; int i, j, res; res = ethr_mutex_init(&dt_mutex); ASSERT(res == 0); res = ethr_cond_init(&dt_cond); ASSERT(res == 0); thr_opts.detached = 1; dt_count = 0; dt_limit = 0; for (i = 0; i < DT_THREADS/DT_BATCH_SIZE; i++) { dt_limit += DT_BATCH_SIZE; for (j = 0; j < DT_BATCH_SIZE; j++) { res = ethr_thr_create(&tid[j], dt_thread, NULL, &thr_opts); ASSERT(res == 0); } ethr_mutex_lock(&dt_mutex); while (dt_count < dt_limit) { res = ethr_cond_wait(&dt_cond, &dt_mutex); ASSERT(res == 0 || res == EINTR); } ethr_mutex_unlock(&dt_mutex); print_line("dt_count = %d", dt_count); } do_sleep(1); } /* * The max threads test case. * * Tests */ #define MTT_TIMES 10 #define MTT_HARD_LIMIT (80000) static int mtt_terminate; static ethr_mutex mtt_mutex; static ethr_cond mtt_cond; static char mtt_string[22*MTT_TIMES]; /* 22 is enough for ", %d" */ void *mtt_thread(void *unused) { int res; ethr_mutex_lock(&mtt_mutex); while (!mtt_terminate) { res = ethr_cond_wait(&mtt_cond, &mtt_mutex); ASSERT(res == 0 || res == EINTR); } ethr_mutex_unlock(&mtt_mutex); return NULL; } static int mtt_create_join_threads(void) { int no_tids = 100, ix = 0, res = 0, no_threads; ethr_tid *tids; mtt_terminate = 0; tids = (ethr_tid *) malloc(sizeof(ethr_tid)*no_tids); ASSERT(tids); print_line("Beginning to create threads"); while (1) { if (ix >= no_tids) { no_tids += 100; if (no_tids > MTT_HARD_LIMIT) { print_line("Hit the hard limit on number of threads (%d)!", MTT_HARD_LIMIT); break; } tids = (ethr_tid *) realloc((void *)tids, sizeof(ethr_tid)*no_tids); ASSERT(tids); } res = ethr_thr_create(&tids[ix], mtt_thread, NULL, NULL); if (res != 0) { break; } ix++; } no_threads = ix; print_line("%d = ethr_thr_create()", res); print_line("Number of created threads: %d", no_threads); ethr_mutex_lock(&mtt_mutex); mtt_terminate = 1; ethr_cond_broadcast(&mtt_cond); ethr_mutex_unlock(&mtt_mutex); while (ix) { res = ethr_thr_join(tids[--ix], NULL); ASSERT(res == 0); } print_line("All created threads terminated"); free((void *) tids); return no_threads; } static void max_threads_test(void) { int no_threads[MTT_TIMES], i, up, down, eq, res; char *str; res = ethr_mutex_init(&mtt_mutex); ASSERT(res == 0); res = ethr_cond_init(&mtt_cond); ASSERT(res == 0); for (i = 0; i < MTT_TIMES; i++) { no_threads[i] = mtt_create_join_threads(); } str = &mtt_string[0]; eq = up = down = 0; for (i = 0; i < MTT_TIMES; i++) { if (i == 0) { str += sprintf(str, "%d", no_threads[i]); continue; } str += sprintf(str, ", %d", no_threads[i]); if (no_threads[i] < no_threads[i-1]) down++; else if (no_threads[i] > no_threads[i-1]) up++; else eq++; } print_line("Max created threads: %s", mtt_string); /* We fail if the no of threads we are able to create constantly decrease */ ASSERT(!down || up || eq); succeed("Max created threads: %s", mtt_string); } /* * The tsd test case. * * Tests thread specific data. */ #define TT_THREADS 10 static ethr_tsd_key tt_key; static void * tt_thread(void *arg) { int res = ethr_tsd_set(tt_key, arg); ASSERT(res == 0); return ethr_tsd_get(tt_key); } static void tsd_test(void) { void *tres; int i, res; ethr_tid tid[TT_THREADS]; int values[TT_THREADS]; res = ethr_tsd_key_create(&tt_key,"tsd_test"); ASSERT(res == 0); for (i = 1; i < TT_THREADS; i++) { res = ethr_thr_create(&tid[i], tt_thread, (void *) &values[i], NULL); ASSERT(res == 0); } tres = tt_thread((void *) &values[0]); ASSERT(tres == (void *) &values[0]); for (i = 1; i < TT_THREADS; i++) { res = ethr_thr_join(tid[i], &tres); ASSERT(res == 0); ASSERT(tres == (void *) &values[i]); } res = ethr_tsd_key_delete(tt_key); ASSERT(res == 0); } /* * The spinlock test case. * * Tests spinlocks. */ static ethr_spinlock_t st_spinlock; static int st_data; void * st_thread(void *unused) { print_line("Aux thread tries to lock spinlock"); ethr_spin_lock(&st_spinlock); print_line("Aux thread locked spinlock"); ASSERT(st_data == 0); st_data = 1; print_line("Aux thread wrote"); print_line("Aux thread goes to sleep for 1 second"); do_sleep(1); print_line("Aux thread woke up"); ASSERT(st_data == 1); ethr_spin_unlock(&st_spinlock); print_line("Aux thread unlocked spinlock"); return NULL; } static void spinlock_test(void) { int res; ethr_tid tid; print_line("Trying to initialize spinlock"); res = ethr_spinlock_init(&st_spinlock); ASSERT(res == 0); print_line("Initialized spinlock"); st_data = 0; print_line("Main thread tries to lock spinlock"); ethr_spin_lock(&st_spinlock); print_line("Main thread locked spinlock"); ASSERT(st_data == 0); print_line("Main thread about to create aux thread"); res = ethr_thr_create(&tid, st_thread, NULL, NULL); ASSERT(res == 0); print_line("Main thread created aux thread"); print_line("Main thread goes to sleep for 1 second"); do_sleep(1); print_line("Main thread woke up"); ASSERT(st_data == 0); ethr_spin_unlock(&st_spinlock); print_line("Main thread unlocked spinlock"); print_line("Main thread goes to sleep for 1 second"); do_sleep(1); print_line("Main thread woke up"); print_line("Main thread tries to lock spinlock"); ethr_spin_lock(&st_spinlock); print_line("Main thread locked spinlock"); ASSERT(st_data == 1); print_line("Main thread goes to sleep for 1 second"); do_sleep(1); print_line("Main thread woke up"); ASSERT(st_data == 1); ethr_spin_unlock(&st_spinlock); print_line("Main thread unlocked spinlock"); res = ethr_thr_join(tid, NULL); ASSERT(res == 0); print_line("Main thread joined aux thread"); res = ethr_spinlock_destroy(&st_spinlock); ASSERT(res == 0); print_line("Main thread destroyed spinlock"); } /* * The rwspinlock test case. * * Tests rwspinlocks. */ static ethr_rwlock_t rwst_rwspinlock; static int rwst_data; void * rwst_thread(void *unused) { int data; print_line("Aux thread tries to read lock rwspinlock"); ethr_read_lock(&rwst_rwspinlock); print_line("Aux thread read locked rwspinlock"); ASSERT(rwst_data == 4711); print_line("Aux thread tries to read unlock rwspinlock"); ethr_read_unlock(&rwst_rwspinlock); print_line("Aux thread read unlocked rwspinlock"); print_line("Aux thread tries to write lock rwspinlock"); ethr_write_lock(&rwst_rwspinlock); print_line("Aux thread write locked rwspinlock"); data = ++rwst_data; print_line("Aux thread wrote"); print_line("Aux thread goes to sleep for 1 second"); do_sleep(1); print_line("Aux thread woke up"); ASSERT(rwst_data == data); ++rwst_data; print_line("Aux thread tries to write unlock rwspinlock"); ethr_write_unlock(&rwst_rwspinlock); print_line("Aux thread write unlocked rwspinlock"); return NULL; } static void rwspinlock_test(void) { int data; int res; ethr_tid tid; print_line("Trying to initialize rwspinlock"); res = ethr_rwlock_init(&rwst_rwspinlock); ASSERT(res == 0); print_line("Initialized rwspinlock"); rwst_data = 4711; print_line("Main thread tries to read lock rwspinlock"); ethr_read_lock(&rwst_rwspinlock); print_line("Main thread read locked rwspinlock"); ASSERT(rwst_data == 4711); print_line("Main thread about to create aux thread"); res = ethr_thr_create(&tid, rwst_thread, NULL, NULL); ASSERT(res == 0); print_line("Main thread created aux thread"); print_line("Main thread goes to sleep for 1 second"); do_sleep(1); print_line("Main thread woke up"); ASSERT(rwst_data == 4711); print_line("Main thread tries to read unlock rwspinlock"); ethr_read_unlock(&rwst_rwspinlock); print_line("Main thread read unlocked rwspinlock"); print_line("Main thread tries to write lock rwspinlock"); ethr_write_lock(&rwst_rwspinlock); print_line("Main thread write locked rwspinlock"); data = ++rwst_data; print_line("Main thread goes to sleep for 1 second"); do_sleep(1); print_line("Main thread woke up"); ASSERT(rwst_data == data); ++rwst_data; print_line("Main thread tries to write unlock rwspinlock"); ethr_write_unlock(&rwst_rwspinlock); print_line("Main thread write unlocked rwspinlock"); res = ethr_thr_join(tid, NULL); ASSERT(res == 0); print_line("Main thread joined aux thread"); res = ethr_rwlock_destroy(&rwst_rwspinlock); ASSERT(res == 0); print_line("Main thread destroyed rwspinlock"); } /* * The rwmutex test case. * * Tests rwmutexes. */ static ethr_rwmutex rwmt_rwmutex; static int rwmt_data; void * rwmt_thread(void *unused) { int data; print_line("Aux thread tries to read lock rwmutex"); ethr_rwmutex_rlock(&rwmt_rwmutex); print_line("Aux thread read locked rwmutex"); ASSERT(rwmt_data == 4711); print_line("Aux thread tries to read unlock rwmutex"); ethr_rwmutex_runlock(&rwmt_rwmutex); print_line("Aux thread read unlocked rwmutex"); print_line("Aux thread tries to write lock rwmutex"); ethr_rwmutex_rwlock(&rwmt_rwmutex); print_line("Aux thread write locked rwmutex"); data = ++rwmt_data; print_line("Aux thread wrote"); print_line("Aux thread goes to sleep for 1 second"); do_sleep(1); print_line("Aux thread woke up"); ASSERT(rwmt_data == data); ++rwmt_data; print_line("Aux thread tries to write unlock rwmutex"); ethr_rwmutex_rwunlock(&rwmt_rwmutex); print_line("Aux thread write unlocked rwmutex"); return NULL; } static void rwmutex_test(void) { int data; int res; ethr_tid tid; print_line("Trying to initialize rwmutex"); res = ethr_rwmutex_init(&rwmt_rwmutex); ASSERT(res == 0); print_line("Initialized rwmutex"); rwmt_data = 4711; print_line("Main thread tries to read lock rwmutex"); ethr_rwmutex_rlock(&rwmt_rwmutex); print_line("Main thread read locked rwmutex"); ASSERT(rwmt_data == 4711); print_line("Main thread about to create aux thread"); res = ethr_thr_create(&tid, rwmt_thread, NULL, NULL); ASSERT(res == 0); print_line("Main thread created aux thread"); print_line("Main thread goes to sleep for 1 second"); do_sleep(1); print_line("Main thread woke up"); ASSERT(rwmt_data == 4711); print_line("Main thread tries to read unlock rwmutex"); ethr_rwmutex_runlock(&rwmt_rwmutex); print_line("Main thread read unlocked rwmutex"); print_line("Main thread tries to write lock rwmutex"); ethr_rwmutex_rwlock(&rwmt_rwmutex); print_line("Main thread write locked rwmutex"); data = ++rwmt_data; print_line("Main thread goes to sleep for 1 second"); do_sleep(1); print_line("Main thread woke up"); ASSERT(rwmt_data == data); ++rwmt_data; print_line("Main thread tries to write unlock rwmutex"); ethr_rwmutex_rwunlock(&rwmt_rwmutex); print_line("Main thread write unlocked rwmutex"); res = ethr_thr_join(tid, NULL); ASSERT(res == 0); print_line("Main thread joined aux thread"); res = ethr_rwmutex_destroy(&rwmt_rwmutex); ASSERT(res == 0); print_line("Main thread destroyed rwmutex"); } /* * The atomic test case. * * Tests atomics. */ #define AT_AINT32_MAX 0x7fffffff #define AT_AINT32_MIN 0x80000000 #define AT_THREADS 4 #define AT_ITER 10000 long at_set_val, at_rm_val, at_max_val; static ethr_atomic_t at_ready; static ethr_atomic_t at_go; static ethr_atomic_t at_done; static ethr_atomic_t at_data; #define AT_TEST_INIT(T, A, B) \ do { \ ethr_ ## A ## _init ## B(&A, 17); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 17); \ } while (0) #define AT_TEST_SET(T, A, B) \ do { \ ethr_ ## A ## _set ## B(&A, 4711); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 4711); \ } while (0) #define AT_TEST_XCHG(T, A, B) \ do { \ ethr_ ## A ## _set ## B(&A, 4711); \ ASSERT(ethr_ ## A ## _xchg ## B(&A, 17) == 4711); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 17); \ } while (0) #define AT_TEST_CMPXCHG(T, A, B) \ do { \ ethr_ ## A ## _set ## B(&A, 4711); \ ASSERT(ethr_ ## A ## _cmpxchg ## B(&A, 17, 33) == 4711); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 4711); \ ASSERT(ethr_ ## A ## _cmpxchg ## B(&A, 17, 4711) == 4711); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 17); \ } while (0) #define AT_TEST_ADD_READ(T, A, B) \ do { \ T var_ = AT_AINT32_MAX; \ var_ += 4711; \ ethr_ ## A ## _set ## B(&A, AT_AINT32_MAX); \ ASSERT(ethr_ ## A ## _add_read ## B(&A, 4711) == var_); \ ASSERT(ethr_ ## A ## _read ## B(&A) == var_); \ var_ = AT_AINT32_MIN; \ var_ -= 4711; \ ethr_ ## A ## _set ## B(&A, AT_AINT32_MIN); \ ASSERT(ethr_ ## A ## _add_read ## B(&A, -4711) == var_); \ ASSERT(ethr_ ## A ## _read ## B(&A) == var_); \ ethr_ ## A ## _set ## B(&A, 4711); \ ASSERT(ethr_ ## A ## _add_read ## B(&A, 10) == 4721); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 4721); \ } while (0) #define AT_TEST_ADD(T, A, B) \ do { \ T var_ = AT_AINT32_MAX; \ var_ += 4711; \ ethr_ ## A ## _set ## B(&A, AT_AINT32_MAX); \ ethr_ ## A ## _add ## B(&A, 4711); \ ASSERT(ethr_ ## A ## _read ## B(&A) == var_); \ var_ = AT_AINT32_MIN; \ var_ -= 4711; \ ethr_ ## A ## _set ## B(&A, AT_AINT32_MIN); \ ethr_ ## A ## _add ## B(&A, -4711); \ ASSERT(ethr_ ## A ## _read ## B(&A) == var_); \ ethr_ ## A ## _set ## B(&A, 11); \ ethr_ ## A ## _add ## B(&A, 4700); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 4711); \ } while (0) #define AT_TEST_INC_READ(T, A, B) \ do { \ T var_ = AT_AINT32_MAX; \ var_++; \ ethr_ ## A ## _set ## B(&A, AT_AINT32_MAX); \ ASSERT(ethr_ ## A ## _inc_read ## B(&A) == var_); \ ASSERT(ethr_ ## A ## _read ## B(&A) == var_); \ ethr_ ## A ## _set ## B(&A, 4710); \ ASSERT(ethr_ ## A ## _inc_read ## B(&A) == 4711); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 4711); \ } while (0) #define AT_TEST_DEC_READ(T, A, B) \ do { \ T var_ = AT_AINT32_MIN; \ var_--; \ ethr_ ## A ## _set ## B(&A, AT_AINT32_MIN); \ ASSERT(ethr_ ## A ## _dec_read ## B(&A) == var_); \ ASSERT(ethr_ ## A ## _read ## B(&A) == var_); \ ethr_ ## A ## _set ## B(&A, 17); \ ASSERT(ethr_ ## A ## _dec_read ## B(&A) == 16); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 16); \ } while (0) #define AT_TEST_INC(T, A, B) \ do { \ T var_ = AT_AINT32_MAX; \ var_++; \ ethr_ ## A ## _set ## B(&A, AT_AINT32_MAX); \ ethr_ ## A ## _inc ## B(&A); \ ASSERT(ethr_ ## A ## _read ## B(&A) == var_); \ ethr_ ## A ## _set ## B(&A, 4710); \ ethr_ ## A ## _inc ## B(&A); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 4711); \ } while (0) #define AT_TEST_DEC(T, A, B) \ do { \ T var_ = AT_AINT32_MIN; \ var_--; \ ethr_ ## A ## _set ## B(&A, AT_AINT32_MIN); \ ethr_ ## A ## _dec ## B(&A); \ ASSERT(ethr_ ## A ## _read ## B(&A) == var_); \ ethr_ ## A ## _set ## B(&A, 17); \ ethr_ ## A ## _dec ## B(&A); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 16); \ } while (0) #define AT_TEST_READ_BAND(T, A, B) \ do { \ ethr_ ## A ## _set ## B(&A, 0x13131313); \ ASSERT(ethr_ ## A ## _read_band ## B(&A, 0x31313131) == 0x13131313); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 0x11111111); \ } while (0) #define AT_TEST_READ_BOR(T, A, B) \ do { \ ethr_ ## A ## _set ## B(&A, 0x11111111); \ ASSERT(ethr_ ## A ## _read_bor ## B(&A, 0x23232323) == 0x11111111); \ ASSERT(ethr_ ## A ## _read ## B(&A) == 0x33333333); \ } while (0) ethr_atomic32_t atomic32; ethr_atomic_t atomic; ethr_dw_atomic_t dw_atomic; static void atomic_basic_test(void) { /* * Verify that each op does what it is expected * to do for at least one input. */ print_line("AT_AINT32_MAX=%d",AT_AINT32_MAX); print_line("AT_AINT32_MIN=%d",AT_AINT32_MIN); AT_TEST_INIT(ethr_sint32_t, atomic32, ); AT_TEST_SET(ethr_sint32_t, atomic32, ); AT_TEST_XCHG(ethr_sint32_t, atomic32, ); AT_TEST_CMPXCHG(ethr_sint32_t, atomic32, ); AT_TEST_ADD_READ(ethr_sint32_t, atomic32, ); AT_TEST_ADD(ethr_sint32_t, atomic32, ); AT_TEST_INC_READ(ethr_sint32_t, atomic32, ); AT_TEST_DEC_READ(ethr_sint32_t, atomic32, ); AT_TEST_INC(ethr_sint32_t, atomic32, ); AT_TEST_DEC(ethr_sint32_t, atomic32, ); AT_TEST_READ_BAND(ethr_sint32_t, atomic32, ); AT_TEST_READ_BOR(ethr_sint32_t, atomic32, ); AT_TEST_INIT(ethr_sint32_t, atomic32, _acqb); AT_TEST_SET(ethr_sint32_t, atomic32, _acqb); AT_TEST_XCHG(ethr_sint32_t, atomic32, _acqb); AT_TEST_CMPXCHG(ethr_sint32_t, atomic32, _acqb); AT_TEST_ADD_READ(ethr_sint32_t, atomic32, _acqb); AT_TEST_ADD(ethr_sint32_t, atomic32, _acqb); AT_TEST_INC_READ(ethr_sint32_t, atomic32, _acqb); AT_TEST_DEC_READ(ethr_sint32_t, atomic32, _acqb); AT_TEST_INC(ethr_sint32_t, atomic32, _acqb); AT_TEST_DEC(ethr_sint32_t, atomic32, _acqb); AT_TEST_READ_BAND(ethr_sint32_t, atomic32, _acqb); AT_TEST_READ_BOR(ethr_sint32_t, atomic32, _acqb); AT_TEST_INIT(ethr_sint32_t, atomic32, _relb); AT_TEST_SET(ethr_sint32_t, atomic32, _relb); AT_TEST_XCHG(ethr_sint32_t, atomic32, _relb); AT_TEST_CMPXCHG(ethr_sint32_t, atomic32, _relb); AT_TEST_ADD_READ(ethr_sint32_t, atomic32, _relb); AT_TEST_ADD(ethr_sint32_t, atomic32, _relb); AT_TEST_INC_READ(ethr_sint32_t, atomic32, _relb); AT_TEST_DEC_READ(ethr_sint32_t, atomic32, _relb); AT_TEST_INC(ethr_sint32_t, atomic32, _relb); AT_TEST_DEC(ethr_sint32_t, atomic32, _relb); AT_TEST_READ_BAND(ethr_sint32_t, atomic32, _relb); AT_TEST_READ_BOR(ethr_sint32_t, atomic32, _relb); AT_TEST_INIT(ethr_sint32_t, atomic32, _rb); AT_TEST_SET(ethr_sint32_t, atomic32, _rb); AT_TEST_XCHG(ethr_sint32_t, atomic32, _rb); AT_TEST_CMPXCHG(ethr_sint32_t, atomic32, _rb); AT_TEST_ADD_READ(ethr_sint32_t, atomic32, _rb); AT_TEST_ADD(ethr_sint32_t, atomic32, _rb); AT_TEST_INC_READ(ethr_sint32_t, atomic32, _rb); AT_TEST_DEC_READ(ethr_sint32_t, atomic32, _rb); AT_TEST_INC(ethr_sint32_t, atomic32, _rb); AT_TEST_DEC(ethr_sint32_t, atomic32, _rb); AT_TEST_READ_BAND(ethr_sint32_t, atomic32, _rb); AT_TEST_READ_BOR(ethr_sint32_t, atomic32, _rb); AT_TEST_INIT(ethr_sint32_t, atomic32, _wb); AT_TEST_SET(ethr_sint32_t, atomic32, _wb); AT_TEST_XCHG(ethr_sint32_t, atomic32, _wb); AT_TEST_CMPXCHG(ethr_sint32_t, atomic32, _wb); AT_TEST_ADD_READ(ethr_sint32_t, atomic32, _wb); AT_TEST_ADD(ethr_sint32_t, atomic32, _wb); AT_TEST_INC_READ(ethr_sint32_t, atomic32, _wb); AT_TEST_DEC_READ(ethr_sint32_t, atomic32, _wb); AT_TEST_INC(ethr_sint32_t, atomic32, _wb); AT_TEST_DEC(ethr_sint32_t, atomic32, _wb); AT_TEST_READ_BAND(ethr_sint32_t, atomic32, _wb); AT_TEST_READ_BOR(ethr_sint32_t, atomic32, _wb); AT_TEST_INIT(ethr_sint32_t, atomic32, _mb); AT_TEST_SET(ethr_sint32_t, atomic32, _mb); AT_TEST_XCHG(ethr_sint32_t, atomic32, _mb); AT_TEST_CMPXCHG(ethr_sint32_t, atomic32, _mb); AT_TEST_ADD_READ(ethr_sint32_t, atomic32, _mb); AT_TEST_ADD(ethr_sint32_t, atomic32, _mb); AT_TEST_INC_READ(ethr_sint32_t, atomic32, _mb); AT_TEST_DEC_READ(ethr_sint32_t, atomic32, _mb); AT_TEST_INC(ethr_sint32_t, atomic32, _mb); AT_TEST_DEC(ethr_sint32_t, atomic32, _mb); AT_TEST_READ_BAND(ethr_sint32_t, atomic32, _mb); AT_TEST_READ_BOR(ethr_sint32_t, atomic32, _mb); AT_TEST_INIT(ethr_sint_t, atomic, ); AT_TEST_SET(ethr_sint_t, atomic, ); AT_TEST_XCHG(ethr_sint_t, atomic, ); AT_TEST_CMPXCHG(ethr_sint_t, atomic, ); AT_TEST_ADD_READ(ethr_sint_t, atomic, ); AT_TEST_ADD(ethr_sint_t, atomic, ); AT_TEST_INC_READ(ethr_sint_t, atomic, ); AT_TEST_DEC_READ(ethr_sint_t, atomic, ); AT_TEST_INC(ethr_sint_t, atomic, ); AT_TEST_DEC(ethr_sint_t, atomic, ); AT_TEST_READ_BAND(ethr_sint_t, atomic, ); AT_TEST_READ_BOR(ethr_sint_t, atomic, ); AT_TEST_INIT(ethr_sint_t, atomic, _acqb); AT_TEST_SET(ethr_sint_t, atomic, _acqb); AT_TEST_XCHG(ethr_sint_t, atomic, _acqb); AT_TEST_CMPXCHG(ethr_sint_t, atomic, _acqb); AT_TEST_ADD_READ(ethr_sint_t, atomic, _acqb); AT_TEST_ADD(ethr_sint_t, atomic, _acqb); AT_TEST_INC_READ(ethr_sint_t, atomic, _acqb); AT_TEST_DEC_READ(ethr_sint_t, atomic, _acqb); AT_TEST_INC(ethr_sint_t, atomic, _acqb); AT_TEST_DEC(ethr_sint_t, atomic, _acqb); AT_TEST_READ_BAND(ethr_sint_t, atomic, _acqb); AT_TEST_READ_BOR(ethr_sint_t, atomic, _acqb); AT_TEST_INIT(ethr_sint_t, atomic, _relb); AT_TEST_SET(ethr_sint_t, atomic, _relb); AT_TEST_XCHG(ethr_sint_t, atomic, _relb); AT_TEST_CMPXCHG(ethr_sint_t, atomic, _relb); AT_TEST_ADD_READ(ethr_sint_t, atomic, _relb); AT_TEST_ADD(ethr_sint_t, atomic, _relb); AT_TEST_INC_READ(ethr_sint_t, atomic, _relb); AT_TEST_DEC_READ(ethr_sint_t, atomic, _relb); AT_TEST_INC(ethr_sint_t, atomic, _relb); AT_TEST_DEC(ethr_sint_t, atomic, _relb); AT_TEST_READ_BAND(ethr_sint_t, atomic, _relb); AT_TEST_READ_BOR(ethr_sint_t, atomic, _relb); AT_TEST_INIT(ethr_sint_t, atomic, _rb); AT_TEST_SET(ethr_sint_t, atomic, _rb); AT_TEST_XCHG(ethr_sint_t, atomic, _rb); AT_TEST_CMPXCHG(ethr_sint_t, atomic, _rb); AT_TEST_ADD_READ(ethr_sint_t, atomic, _rb); AT_TEST_ADD(ethr_sint_t, atomic, _rb); AT_TEST_INC_READ(ethr_sint_t, atomic, _rb); AT_TEST_DEC_READ(ethr_sint_t, atomic, _rb); AT_TEST_INC(ethr_sint_t, atomic, _rb); AT_TEST_DEC(ethr_sint_t, atomic, _rb); AT_TEST_READ_BAND(ethr_sint_t, atomic, _rb); AT_TEST_READ_BOR(ethr_sint_t, atomic, _rb); AT_TEST_INIT(ethr_sint_t, atomic, _wb); AT_TEST_SET(ethr_sint_t, atomic, _wb); AT_TEST_XCHG(ethr_sint_t, atomic, _wb); AT_TEST_CMPXCHG(ethr_sint_t, atomic, _wb); AT_TEST_ADD_READ(ethr_sint_t, atomic, _wb); AT_TEST_ADD(ethr_sint_t, atomic, _wb); AT_TEST_INC_READ(ethr_sint_t, atomic, _wb); AT_TEST_DEC_READ(ethr_sint_t, atomic, _wb); AT_TEST_INC(ethr_sint_t, atomic, _wb); AT_TEST_DEC(ethr_sint_t, atomic, _wb); AT_TEST_READ_BAND(ethr_sint_t, atomic, _wb); AT_TEST_READ_BOR(ethr_sint_t, atomic, _wb); AT_TEST_INIT(ethr_sint_t, atomic, _mb); AT_TEST_SET(ethr_sint_t, atomic, _mb); AT_TEST_XCHG(ethr_sint_t, atomic, _mb); AT_TEST_CMPXCHG(ethr_sint_t, atomic, _mb); AT_TEST_ADD_READ(ethr_sint_t, atomic, _mb); AT_TEST_ADD(ethr_sint_t, atomic, _mb); AT_TEST_INC_READ(ethr_sint_t, atomic, _mb); AT_TEST_DEC_READ(ethr_sint_t, atomic, _mb); AT_TEST_INC(ethr_sint_t, atomic, _mb); AT_TEST_DEC(ethr_sint_t, atomic, _mb); AT_TEST_READ_BAND(ethr_sint_t, atomic, _mb); AT_TEST_READ_BOR(ethr_sint_t, atomic, _mb); /* Double word */ { ethr_dw_sint_t dw0, dw1; dw0.sint[0] = 4711; dw0.sint[1] = 4712; /* init */ ethr_dw_atomic_init(&dw_atomic, &dw0); ethr_dw_atomic_read(&dw_atomic, &dw1); ETHR_ASSERT(dw1.sint[0] == 4711); ETHR_ASSERT(dw1.sint[1] == 4712); /* set */ dw0.sint[0] = 42; dw0.sint[1] = ~((ethr_sint_t) 0); ethr_dw_atomic_set(&dw_atomic, &dw0); ethr_dw_atomic_read(&dw_atomic, &dw1); ASSERT(dw1.sint[0] == 42); ASSERT(dw1.sint[1] == ~((ethr_sint_t) 0)); /* cmpxchg */ dw0.sint[0] = 17; dw0.sint[1] = 18; dw1.sint[0] = 19; dw1.sint[1] = 20; ASSERT(!ethr_dw_atomic_cmpxchg(&dw_atomic, &dw1, &dw0)); ethr_dw_atomic_read(&dw_atomic, &dw0); ASSERT(dw0.sint[0] == 42); ASSERT(dw0.sint[1] == ~((ethr_sint_t) 0)); ASSERT(ethr_dw_atomic_cmpxchg(&dw_atomic, &dw1, &dw0)); ethr_dw_atomic_read(&dw_atomic, &dw0); ASSERT(dw0.sint[0] == 19); ASSERT(dw0.sint[1] == 20); } } #define AT_DW_MIN 12 #define AT_DW_MAX 42 #define AT_DW_THREADS (AT_DW_MAX - AT_DW_MIN + 1) #define AT_DW_LOOPS 200000 #define AT_DW_R_LOOPS 10 ethr_dw_atomic_t at_dw_atomic; void at_dw_valid(ethr_dw_sint_t *dw) { int i; char c; char *cp; ASSERT(dw->sint[0] == dw->sint[1]); cp = (char *) &dw->sint[0]; c = cp[0]; ASSERT(AT_DW_MIN <= c && c <= AT_DW_MAX); for (i = 0; i < sizeof(ethr_sint_t); i++) ASSERT(c == cp[i]); } void * at_dw_thr(void *vval) { int l, r; ethr_sint_t val = (ethr_sint_t) vval; ethr_dw_sint_t dw; ethr_dw_sint_t my_dw; my_dw.sint[0] = val; my_dw.sint[1] = val; ethr_dw_atomic_set(&at_dw_atomic, &my_dw); for (l = 0; l < AT_DW_LOOPS; l++) { for (r = 0; r < AT_DW_R_LOOPS; r++) { ethr_dw_atomic_read(&at_dw_atomic, &dw); at_dw_valid(&dw); } ethr_dw_atomic_set(&at_dw_atomic, &my_dw); for (r = 0; r < AT_DW_R_LOOPS; r++) { ethr_dw_atomic_read(&at_dw_atomic, &dw); at_dw_valid(&dw); } dw.sint[0] = 0; dw.sint[1] = 0; while (1) { if (ethr_dw_atomic_cmpxchg(&at_dw_atomic, &my_dw, &dw)) break; } } } static void dw_atomic_massage_test(void) { int i, res; ethr_tid tid[AT_DW_THREADS]; ethr_thr_opts thr_opts = ETHR_THR_OPTS_DEFAULT_INITER; ethr_dw_sint_t dw; dw.sint[0] = dw.sint[1] = 0; ethr_dw_atomic_init(&at_dw_atomic, &dw); for (i = AT_DW_MIN; i <= AT_DW_MAX; i++) { ethr_sint_t val; memset(&val, i, sizeof(ethr_sint_t)); res = ethr_thr_create(&tid[i-AT_DW_MIN], at_dw_thr, (void *) val, &thr_opts); ASSERT(res == 0); } for (i = AT_DW_MIN; i <= AT_DW_MAX; i++) { res = ethr_thr_join(tid[i-AT_DW_MIN], NULL); ASSERT(res == 0); } } void * at_thread(void *unused) { int i; long val, go; val = ethr_atomic_inc_read(&at_ready); ASSERT(val > 0); ASSERT(val <= AT_THREADS); do { go = ethr_atomic_read(&at_go); } while (!go); for (i = 0; i < AT_ITER; i++) { val = ethr_atomic_read_bor(&at_data, at_set_val); ASSERT(val >= (i == 0 ? 0 : at_set_val) + (long) 4711); ASSERT(val <= at_max_val); val = ethr_atomic_read_band(&at_data, ~at_rm_val); ASSERT(val >= at_set_val + (long) 4711); ASSERT(val <= at_max_val); val = ethr_atomic_read(&at_data); ASSERT(val >= at_set_val + (long) 4711); ASSERT(val <= at_max_val); val = ethr_atomic_inc_read(&at_data); ASSERT(val > at_set_val + (long) 4711); ASSERT(val <= at_max_val); val = ethr_atomic_dec_read(&at_data); ASSERT(val >= at_set_val + (long) 4711); ASSERT(val <= at_max_val); ethr_atomic_inc(&at_data); ethr_atomic_dec(&at_data); val = ethr_atomic_add_read(&at_data, (long) 4711); ASSERT(val >= at_set_val + (long) 2*4711); ASSERT(val <= at_max_val); ethr_atomic_add(&at_data, (long) -4711); ASSERT(val >= at_set_val + (long) 4711); ASSERT(val <= at_max_val); } ethr_atomic_inc(&at_done); return NULL; } static void atomic_test(void) { long data_init, data_final, val; int res, i; ethr_tid tid[AT_THREADS]; ethr_thr_opts thr_opts = ETHR_THR_OPTS_DEFAULT_INITER; atomic_basic_test(); #if ETHR_SIZEOF_PTR > 4 at_rm_val = ((long) 1) << 57; at_set_val = ((long) 1) << 60; #else at_rm_val = ((long) 1) << 27; at_set_val = ((long) 1) << 30; #endif at_max_val = at_set_val + at_rm_val + ((long) AT_THREADS + 1) * 4711; data_init = at_rm_val + (long) 4711; data_final = at_set_val + (long) 4711; thr_opts.detached = 1; print_line("Initializing"); ethr_atomic_init(&at_ready, 0); ethr_atomic_init(&at_go, 0); ethr_atomic_init(&at_done, data_init); ethr_atomic_init(&at_data, data_init); val = ethr_atomic_read(&at_data); ASSERT(val == data_init); ethr_atomic_set(&at_done, 0); val = ethr_atomic_read(&at_done); ASSERT(val == 0); print_line("Creating threads"); for (i = 0; i < AT_THREADS; i++) { res = ethr_thr_create(&tid[i], at_thread, NULL, &thr_opts); ASSERT(res == 0); } print_line("Waiting for threads to ready up"); do { val = ethr_atomic_read(&at_ready); ASSERT(val >= 0); ASSERT(val <= AT_THREADS); } while (val != AT_THREADS); print_line("Letting threads loose"); val = ethr_atomic_xchg(&at_go, 17); ASSERT(val == 0); val = ethr_atomic_read(&at_go); ASSERT(val == 17); print_line("Waiting for threads to finish"); do { val = ethr_atomic_read(&at_done); ASSERT(val >= 0); ASSERT(val <= AT_THREADS); } while (val != AT_THREADS); print_line("Checking result"); val = ethr_atomic_read(&at_data); ASSERT(res == 0); ASSERT(val == data_final); print_line("Result ok"); } #endif /* #ifndef ETHR_NO_THREAD_LIB */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * The dispatcher * \* */ int main(int argc, char *argv[]) { if (argc < 2) fail("To few arguments for test case"); #ifndef ETHR_NO_THREAD_LIB { char *testcase; send_my_pid(); testcase = argv[1]; if (ethr_init(NULL) != 0 || ethr_late_init(NULL) != 0) fail("Failed to initialize the ethread library"); if (strcmp(testcase, "create_join_thread") == 0) create_join_thread_test(); else if (strcmp(testcase, "equal_tids") == 0) equal_tids_test(); else if (strcmp(testcase, "mutex") == 0) mutex_test(); else if (strcmp(testcase, "try_lock_mutex") == 0) try_lock_mutex_test(); else if (strcmp(testcase, "cond_wait") == 0) cond_wait_test(); else if (strcmp(testcase, "broadcast") == 0) broadcast_test(); else if (strcmp(testcase, "detached_thread") == 0) detached_thread_test(); else if (strcmp(testcase, "max_threads") == 0) max_threads_test(); else if (strcmp(testcase, "tsd") == 0) tsd_test(); else if (strcmp(testcase, "spinlock") == 0) spinlock_test(); else if (strcmp(testcase, "rwspinlock") == 0) rwspinlock_test(); else if (strcmp(testcase, "rwmutex") == 0) rwmutex_test(); else if (strcmp(testcase, "atomic") == 0) atomic_test(); else if (strcmp(testcase, "dw_atomic_massage") == 0) dw_atomic_massage_test(); else skip("Test case \"%s\" not implemented yet", testcase); succeed(NULL); } #else /* #ifndef ETHR_NO_THREAD_LIB */ skip("No ethread library to test"); #endif /* #ifndef ETHR_NO_THREAD_LIB */ return 0; }