aboutsummaryrefslogblamecommitdiffstats
path: root/erts/test/ethread_SUITE_data/ethread_tests.c
blob: ed96ecdbd295c6c669bce345fba0b4bfe8aea145 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750

                   
  
                                                        
  




                                                                      
  



                                                                         
  



































































                                                                         
                   







                                      
                                                           



                                 
      






































































































                                                                 

                                                 
















                                     

                            















                                                                 
                                



                                                    
                                  







                     



                                      















































                                                                                   
                                
                      

                                  















                                           
                           




                       
                                                 
                               












                                                        
                                 








                                            


                 



                                             
 


                                                  
                               














                                                         
                                 






                                                         
                               









                                                         
                                 









                                                







                                  


                            








                               

                                

                         
                                 





                                                     

                                  
 
                                
                         

                                  









                         






                                      





                                                         
                                





                                                     
                                  





                                             
                                

                            
                                 





                                                     
                                  



                                         
                                  












                                         





                                                                      

                            


                       
                        
 

            
                                
 



                                                    


                  
                                  




                
                    
 


                        



                                      
 



                          
                                                         
                     
                                                         



                                                          
                                
 
                                                      




                                                               
                                  


                                                   
                                


                             
                                                            




                                                               
                                  


                                                   
                                


                             
                                  











                                    
                                                         
                     
                                                         



                                                          
                                
 
                                                          




                                                               
                                  


                                                    
                                


                             
                                  











                                         













                                                                        


                                






                        
                                



                       

                                              




                                                        

                                              


     
                                  









                              






                                          





                                                               
                                











                                                              
                                       










                                                              
                                   
 
                                  
 
                                       





















                                             

                           





                       
                               





                                   
                                 










                                                          




                                     












                                                                       
                                   



                                                      
                                     













                                              
                              

                         

                            






                                                                   
                                





                                                    
                                  




















                                                         




                                                                            



                                                                                
                       
                  
         
             
     





                                                            
                                


                      
                                   
 
                                  
















                                                 
                                                    

              




                                      






























                                                                                


























































                                                                             
                                                    
                                 












                                                        
                                   



















                                                     
                                 














                                                         
                                   






                                                         
                                 









                                                         
                                   

























                                                 

                                                           
                                     




                                                             
                                       


                                                            
                                      












                                                              
                                        




















                                                            
                                     















                                                              
                                       


                                                             
                                      











                                                               
                                        

























                                                        

                                                        
                                      




                                                          
                                        


                                                         
                                       












                                                           
                                         




















                                                         
                                      















                                                           
                                        


                                                          
                                       











                                                            
                                         

















                                                     


                                









                                       
































































































































































































































































































































































































































                                                                                     


                       
          
                 
 
                                          



                              
                                      


                                   
                                                         


                                                               
                                                          


                                                
                                         


                                                
                                             


                                               
                                             


                                                
                                  
 
                                  
 
                                                          


                                                  
                                                



                                                
                              


                







                                                          

                        
                       

                                      
     

                                      
      







                                                                         



                                          
 
                                     
                             

                                     









                                                                   
                                          




                                        
                                       
                     
                                   




                                                
                                         




                                  
                                     






                              














                                                                               



                           
 
                                                              









                                                             
                                                    
                             





                                                          









                                                     

                                                            










                                                                   
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2004-2011. 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);
    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);
    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)


static void
atomic_basic_test(void)
{
    /*
     * Verify that each op does what it is expected
     * to do for at least one input.
     */
    ethr_atomic32_t atomic32;
    ethr_atomic_t atomic;

    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_atomic_t dw_atomic;
	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;
}