aboutsummaryrefslogblamecommitdiffstats
path: root/erts/test/ethread_SUITE_data/ethread_tests.c
blob: 7fc71d8047da70bfaec0c97acb5bbc2a99d37e02 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                   


                                                        




                                                                      
  



                                                                         
  











































































                                                                         
                                                           



                                 
      






































































































                                                                 

                                                 
















                                     

                            















                                                                 
                                



                                                    
                                  







                     



                                      















































                                                                                   
                                
                      

                                  















                                           
                           




                       
                                                 
                               












                                                        
                                 








                                            


                 



                                             
 


                                                  
                               














                                                         
                                 






                                                         
                               









                                                         
                                 









                                                







                                  


                            








                               

                                

                         
                                 





                                                     

                                  
 
                                
                         

                                  









                         






                                      





                                                         
                                





                                                     
                                  





                                             
                                

                            
                                 





                                                     
                                  



                                         
                                  












                                         




































































                                                                               

                            


                       
                        
 

            
                                
 



                                                    


                  
                                  




                
                    
 


                        



                                      
 



                          
                                                         
                     
                                                         



                                                          
                                
 
                                                      




                                                               
                                  


                                                   
                                


                             
                                                            




                                                               
                                  


                                                   
                                


                             
                                  











                                    
                                                         
                     
                                                         



                                                          
                                
 
                                                          




                                                               
                                  


                                                    
                                


                             
                                  











                                         













                                                                        


                                






                        
                                



                       

                                              




                                                        

                                              


     
                                  









                              






                                          





                                                               
                                











                                                              
                                       










                                                              
                                   
 
                                  
 
                                       





















                                             

                           





                       
                               





                                   
                                 










                                                          




                                     












                                                                       
                                   



                                                      
                                     















                                              

                            






                                                                   
                                





                                                    
                                  


































                                                                                
                                


                      
                                   
 
                                  
















                                                 
                                                    

              




                                      






























                                                                                


























































                                                                             
                                                    
                                 












                                                        
                                   



















                                                     
                                 














                                                         
                                   






                                                         
                                 









                                                         
                                   

























                                                 

                                                           
                                     




                                                             
                                       


                                                            
                                      












                                                              
                                        




















                                                            
                                     















                                                              
                                       


                                                             
                                      











                                                               
                                        

























                                                        

                                                        
                                      




                                                          
                                        


                                                         
                                       












                                                           
                                         




















                                                         
                                      















                                                           
                                        


                                                          
                                       











                                                            
                                         






























                                                     
          


                 
                                          



                              
                                      


                                   
                                                         


                                                               
                                                          


                                                
                                         


                                                
                                             


                                               
                                             


                                                
                                  
 
                                  
 
                                                          


                                                  
                                                



                                                
                              











                                                          
                       

                                      
     

                                      
      







                                                                         



                                          
 
                                     
                             

                                     









                                                                   
                                          




                                        
                                       
                     
                                   




                                                
                                         




                                  
                                     






                              














                                                                               



                           
 
                                                              









                                                             


                                                    
                             





                                                          









                                                     










                                                                   
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2004-2010. All Rights Reserved.
 *
 * The contents of this file are subject to the Erlang Public License,
 * Version 1.1, (the "License"); you may not use this file except in
 * compliance with the License. You should have received a copy of the
 * Erlang Public License along with this software. If not, it can be
 * retrieved online at http://www.erlang.org/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights 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 <stdio.h>
#include <stdarg.h>
#include <string.h>
#ifndef __WIN32__
#include <unistd.h>
#endif
#include <limits.h>

/*
 * 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);
}

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 time now test.
 *
 * Tests ethr_time_now by comparing time values with Erlang.
 */
#define TNT_MAX_TIME_DIFF 200000
#define TNT_MAX_TIME_VALUES 52

static void
time_now_test(void)
{
    int scanf_res, time_now_res, i, no_values, max_abs_diff;
    static ethr_timeval tv[TNT_MAX_TIME_VALUES];
    static int ms[TNT_MAX_TIME_VALUES];

    i = 0;
    do {
	ASSERT(i < TNT_MAX_TIME_VALUES);
	scanf_res = scanf("%d", &ms[i]);
	time_now_res = ethr_time_now(&tv[i]);
	ASSERT(scanf_res == 1);
	ASSERT(time_now_res == 0);
#if 0
	print_line("Got %d; %ld:%ld", ms[i], tv[i].tv_sec, tv[i].tv_nsec);
#endif
	i++;
    } while (ms[i-1] >= 0);

    no_values = i-1;

    ASSERT(ms[0] == 0);

    print_line("TNT_MAX_TIME_DIFF = %d (us)", TNT_MAX_TIME_DIFF);

    max_abs_diff = 0;

    for (i = 1; i < no_values; i++) {
	long diff;
	long tn_us;
	long e_us;

	tn_us = (tv[i].tv_sec - tv[0].tv_sec) * 1000000;
	tn_us += (tv[i].tv_nsec - tv[0].tv_nsec)/1000;

	e_us = ms[i]*1000;
	
	diff = e_us - tn_us;

	print_line("Erlang time = %ld us; ethr_time_now = %ld us; diff %ld us",
		   e_us, tn_us, diff);

	if (max_abs_diff < abs((int) diff)) {
	    max_abs_diff = abs((int) diff);
	}

	ASSERT(e_us - TNT_MAX_TIME_DIFF <= tn_us);
	ASSERT(tn_us <= e_us + TNT_MAX_TIME_DIFF);
    }

    print_line("Max absolute diff = %d us", max_abs_diff);
    succeed("Max absolute diff = %d us", max_abs_diff);
}

/*
 * 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

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;
	    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++;
    } while (res == 0);

    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);
    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_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;

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;

#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, "time_now") == 0)
	    time_now_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
	    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;
}