Changeset 29b8138 in mainline for uspace/app/rcutest/rcutest.c


Ignore:
Timestamp:
2012-11-23T19:58:34Z (12 years ago)
Author:
Adam Hraska <adam.hraska+hos@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
a879d73
Parents:
a2f42e5
Message:

urcu: Added sanity tests.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/rcutest/rcutest.c

    ra2f42e5 r29b8138  
    4242#include <thread.h>
    4343#include <assert.h>
    44 
     44#include <async.h>
    4545#include <fibril.h>
     46#include <compiler/barrier.h>
    4647
    4748#include <rcu.h>
    4849
    4950
    50 static void invoke_rcu(void)
    51 {
    52         rcu_read_lock();
     51#define USECS_PER_SEC (1000 * 1000)
     52#define USECS_PER_MS  1000
     53
     54
     55typedef struct test_desc {
     56        /* Aggregate test that runs other tests already in the table test_desc. */
     57        bool aggregate;
     58        enum {
     59                T_OTHER,
     60                T_SANITY,
     61                T_STRESS
     62        } type;
     63        bool (*func)(void);
     64        const char *name;
     65        const char *desc;
     66} test_desc_t;
     67
     68
     69static bool run_all_tests(void);
     70static bool run_sanity_tests(void);
     71static bool run_stress_tests(void);
     72
     73static bool wait_for_one_reader(void);
     74static bool basic_sanity_check(void);
     75static bool dont_wait_for_new_reader(void);
     76
     77
     78static test_desc_t test_desc[] = {
     79        {
     80                .aggregate = true,
     81                .type = T_OTHER,
     82                .func = run_all_tests,
     83                .name = "*",
     84                .desc = "Runs all tests.",
     85        },
     86        {
     87                .aggregate = true,
     88                .type = T_SANITY,
     89                .func = run_sanity_tests,
     90                .name = "sanity-tests",
     91                .desc = "Runs all RCU sanity tests.",
     92        },
     93        {
     94                .aggregate = true,
     95                .type = T_STRESS,
     96                .func = run_stress_tests,
     97                .name = "stress-tests",
     98                .desc = "Runs all RCU stress tests.",
     99        },
     100
     101        {
     102                .aggregate = false,
     103                .type = T_SANITY,
     104                .func = basic_sanity_check,
     105                .name = "basic-sanity",
     106                .desc = "Locks/unlocks and syncs in a single fibril, no contention.",
     107        },
     108        {
     109                .aggregate = false,
     110                .type = T_SANITY,
     111                .func = wait_for_one_reader,
     112                .name = "wait-for-one",
     113                .desc = "Syncs with one 2 secs sleeping reader.",
     114        },
     115        {
     116                .aggregate = false,
     117                .type = T_SANITY,
     118                .func = dont_wait_for_new_reader,
     119                .name = "ignore-new-r",
     120                .desc = "Syncs with preexisting reader but ignores new reader.",
     121        },
     122        {
     123                .aggregate = false,
     124                .type = T_OTHER,
     125                .func = NULL,
     126                .name = "(null)",
     127                .desc = "",
     128        },
     129};
     130
     131static const size_t test_desc_cnt = sizeof(test_desc) / sizeof(test_desc[0]);
     132
     133/*--------------------------------------------------------------------*/
     134
     135typedef int (*fibril_func_t)(void *);
     136
     137static bool create_fibril(int (*func)(void*), void *arg)
     138{
     139        fid_t fid = fibril_create(func, arg);
     140       
     141        if (0 == fid) {
     142                printf("Failed to create a fibril!\n");
     143                return false;
     144        }
     145       
     146        fibril_add_ready(fid);
     147        return true;
     148}
     149
     150/*--------------------------------------------------------------------*/
     151
     152static bool run_tests(bool (*include_filter)(test_desc_t *))
     153{
     154        size_t failed_cnt = 0;
     155        size_t ok_cnt = 0;
     156       
     157        for (size_t i = 0; i < test_desc_cnt; ++i) {
     158                test_desc_t *t = &test_desc[i];
     159               
     160                if (t->func && !t->aggregate && include_filter(t)) {
     161                        printf("Running \'%s\'...\n", t->name);
     162                        bool ok = test_desc[i].func();
     163                       
     164                        if (ok) {
     165                                ++ok_cnt;
     166                                printf("Passed: \'%s\'\n", t->name);
     167                        } else {
     168                                ++failed_cnt;
     169                                printf("FAILED: \'%s\'\n", t->name);
     170                        }
     171                }
     172        }
     173       
     174        printf("\n");
     175
     176        printf("%zu tests passed\n", ok_cnt);
     177
     178        if (failed_cnt) {
     179                printf("%zu tests failed\n", failed_cnt);
     180        }
     181       
     182        return 0 == failed_cnt;
     183}
     184
     185/*--------------------------------------------------------------------*/
     186
     187static bool all_tests_include_filter(test_desc_t *desc)
     188{
     189        return true;
     190}
     191
     192/* Runs all available tests tests one-by-one. */
     193static bool run_all_tests(void)
     194{
     195        printf("Running all tests...\n");
     196        return run_tests(all_tests_include_filter);
     197}
     198
     199/*--------------------------------------------------------------------*/
     200
     201static bool stress_tests_include_filter(test_desc_t *desc)
     202{
     203        return desc->type == T_STRESS;
     204}
     205
     206/* Runs all available stress tests one-by-one. */
     207static bool run_stress_tests(void)
     208{
     209        printf("Running stress tests...\n");
     210        return run_tests(stress_tests_include_filter);
     211}
     212
     213/*--------------------------------------------------------------------*/
     214
     215static bool sanity_tests_include_filter(test_desc_t *desc)
     216{
     217        return desc->type == T_SANITY;
     218}
     219
     220/* Runs all available sanity tests one-by-one. */
     221static bool run_sanity_tests(void)
     222{
     223        printf("Running sanity tests...\n");
     224        return run_tests(sanity_tests_include_filter);
     225}
     226
     227/*--------------------------------------------------------------------*/
     228
     229/* Locks/unlocks rcu and synchronizes without contention in a single fibril. */
     230static bool basic_sanity_check(void)
     231{
    53232        rcu_read_lock();
    54233        /* nop */
    55234        rcu_read_unlock();
     235
     236        rcu_read_lock();
     237        /* nop */
    56238        rcu_read_unlock();
    57239       
    58240        rcu_synchronize();
    59        
    60         printf("Woohoo, did not crash after invoking rcu functions.\n");
    61 }
     241
     242        /* Nested lock with yield(). */
     243        rcu_read_lock();
     244        fibril_yield();
     245        rcu_read_lock();
     246        fibril_yield();
     247        rcu_read_unlock();
     248        fibril_yield();
     249        rcu_read_unlock();
     250       
     251        fibril_yield();
     252        rcu_synchronize();
     253        rcu_synchronize();
     254       
     255        rcu_read_lock();
     256        /* nop */
     257        if (!rcu_read_locked())
     258                return false;
     259
     260        rcu_read_unlock();
     261       
     262        return !rcu_read_locked();
     263}
     264
     265typedef struct one_reader_info {
     266        bool entered_cs;
     267        bool exited_cs;
     268        size_t done_sleeps_cnt;
     269        bool synching;
     270        bool synched;
     271} one_reader_info_t;
     272
     273
     274static int sleeping_reader(one_reader_info_t *arg)
     275{
     276        rcu_register_fibril();
     277       
     278        printf("lock{");
     279        rcu_read_lock();
     280        arg->entered_cs = true;
     281
     282        printf("r-sleep{");
     283        /* 2 sec */
     284        async_usleep(2 * USECS_PER_SEC);
     285        ++arg->done_sleeps_cnt;
     286        printf("}");
     287       
     288        arg->exited_cs = true;
     289        rcu_read_unlock();
     290        printf("}");
     291       
     292        rcu_deregister_fibril();
     293        return 0;
     294}
     295
     296static bool wait_for_one_reader(void)
     297{
     298        one_reader_info_t info = { 0 };
     299       
     300        if (!create_fibril((fibril_func_t) sleeping_reader, &info))
     301                return false;
     302       
     303        /* 1 sec, waits for the reader to enter its critical section and sleep. */
     304        async_usleep(1 * USECS_PER_SEC);
     305       
     306        if (!info.entered_cs || info.exited_cs) {
     307                printf("Error: reader is unexpectedly outside of critical section.\n");
     308                return false;
     309        }
     310       
     311        info.synching = true;
     312        printf("sync[");
     313        rcu_synchronize();
     314        printf("]\n");
     315        info.synched = true;
     316
     317        /* Load info.exited_cs */
     318        memory_barrier();
     319       
     320        if (!info.exited_cs) {
     321                printf("Error: rcu_sync() returned before the reader exited its CS.\n");
     322                /*
     323                 * Sleep some more so we don't free info on stack while the reader
     324                 * is using it.
     325                 */
     326                /* 1.5 sec */
     327                async_usleep(1500 * 1000);
     328                return false;
     329        } else {
     330                return true;
     331        }
     332}
     333
     334/*--------------------------------------------------------------------*/
     335
     336#define WAIT_STEP_US  1000 * USECS_PER_MS
     337
     338typedef struct two_reader_info {
     339        bool new_entered_cs;
     340        bool new_exited_cs;
     341        bool old_entered_cs;
     342        bool old_exited_cs;
     343        bool synching;
     344        bool synched;
     345        size_t failed;
     346} two_reader_info_t;
     347
     348
     349static int preexisting_reader(two_reader_info_t *arg)
     350{
     351        rcu_register_fibril();
     352       
     353        printf("old-lock{");
     354        rcu_read_lock();
     355        arg->old_entered_cs = true;
     356       
     357        printf("wait-for-sync{");
     358        /* Wait for rcu_sync() to start waiting for us. */
     359        while (!arg->synching) {
     360                async_usleep(WAIT_STEP_US);
     361        }
     362        printf(" }");
     363       
     364        /* A new reader starts while rcu_sync() is in progress. */
     365       
     366        printf("wait-for-new-R{");
     367        /* Wait for the new reader to enter its reader section. */
     368        while (!arg->new_entered_cs) {
     369                async_usleep(WAIT_STEP_US);
     370        }
     371        printf(" }");
     372       
     373        arg->old_exited_cs = true;
     374       
     375        assert(!arg->new_exited_cs);
     376       
     377        if (arg->synched) {
     378                arg->failed = 1;
     379                printf("Error: rcu_sync() did not wait for preexisting reader.\n");
     380        }
     381       
     382        rcu_read_unlock();
     383        printf(" }");
     384       
     385        rcu_deregister_fibril();
     386        return 0;
     387}
     388
     389static int new_reader(two_reader_info_t *arg)
     390{
     391        rcu_register_fibril();
     392       
     393        /* Wait until rcu_sync() starts. */
     394        while (!arg->synching) {
     395                async_usleep(WAIT_STEP_US);
     396        }
     397       
     398        /*
     399         * synching is set when rcu_sync() is about to be entered so wait
     400         * some more to make sure it really does start executing.
     401         */
     402        async_usleep(WAIT_STEP_US);
     403       
     404        printf("new-lock(");
     405        rcu_read_lock();
     406        arg->new_entered_cs = true;
     407
     408        /* Wait for rcu_sync() exit, ie stop waiting for the preexisting reader. */
     409        while (!arg->synched) {
     410                async_usleep(WAIT_STEP_US);
     411        }
     412       
     413        arg->new_exited_cs = true;
     414        /* Write new_exited_cs before exiting reader section. */
     415        memory_barrier();
     416       
     417        /*
     418         * Preexisting reader should have exited by now, so rcu_synchronize()
     419         * must have returned.
     420         */
     421        if (!arg->old_exited_cs) {
     422                arg->failed = 1;
     423                printf("Error: preexisting reader should have exited by now!\n");
     424        }
     425       
     426        rcu_read_unlock();
     427        printf(")");
     428
     429        rcu_deregister_fibril();
     430        return 0;
     431}
     432
     433static bool dont_wait_for_new_reader(void)
     434{
     435        two_reader_info_t info = { 0 };
     436       
     437        if (!create_fibril((fibril_func_t) preexisting_reader, &info))
     438                return false;
     439
     440        if (!create_fibril((fibril_func_t) new_reader, &info))
     441                return false;
     442       
     443        /* Waits for the preexisting_reader to enter its CS.*/
     444        while (!info.old_entered_cs) {
     445                async_usleep(WAIT_STEP_US);
     446        }
     447       
     448        assert(!info.old_exited_cs);
     449        assert(!info.new_entered_cs);
     450        assert(!info.new_exited_cs);
     451       
     452        printf("sync[");
     453        info.synching = true;
     454        rcu_synchronize();
     455        printf(" ]");
     456       
     457        /* Load info.exited_cs */
     458        memory_barrier();
     459       
     460        if (!info.old_exited_cs) {
     461                printf("Error: rcu_sync() returned before preexisting reader exited.\n");
     462                info.failed = 1;
     463        }
     464       
     465        bool new_outside_cs = !info.new_entered_cs || info.new_exited_cs;
     466       
     467        /* Test if new reader is waiting in CS before setting synched. */
     468        compiler_barrier();
     469        info.synched = true;
     470               
     471        if (new_outside_cs) {
     472                printf("Error: new reader CS held up rcu_sync(). (4)\n");
     473                info.failed = 1;
     474        } else {
     475                /* Wait for the new reader. */
     476                rcu_synchronize();
     477               
     478                if (!info.new_exited_cs) {
     479                        printf("Error: 2nd rcu_sync() returned before new reader exited.\n");
     480                        info.failed = 1;
     481                }
     482               
     483                printf("\n");
     484        }
     485       
     486        if (info.failed) {
     487                /*
     488                 * Sleep some more so we don't free info on stack while readers
     489                 * are using it.
     490                 */
     491                async_usleep(WAIT_STEP_US);
     492        }
     493       
     494        return 0 == info.failed;
     495}
     496
     497#undef WAIT_STEP_US
     498/*--------------------------------------------------------------------*/
     499/*--------------------------------------------------------------------*/
     500static test_desc_t *find_test(const char *name)
     501{
     502        /* First match for test name. */
     503        for (size_t k = 0; k < test_desc_cnt; ++k) {
     504                test_desc_t *t = &test_desc[k];
     505               
     506                if (t->func && 0 == str_cmp(t->name, name))
     507                        return t;
     508        }
     509       
     510        /* Try to match the test number. */
     511        uint32_t test_num = 0;
     512       
     513        if (EOK == str_uint32_t(name, NULL, 0, true, &test_num)) {
     514                if (test_num < test_desc_cnt && test_desc[test_num].func) {
     515                        printf("[%u]\n", test_num);
     516                        return &test_desc[test_num];
     517                }
     518        }
     519       
     520        return NULL;
     521}
     522
     523static void list_tests(void)
     524{
     525        printf("Available tests: \n");
     526       
     527        for (size_t i = 0; i < test_desc_cnt; ++i) {
     528                test_desc_t *t = &test_desc[i];
     529               
     530                if (!t->func)
     531                        continue;
     532               
     533                const char *type = "";
     534               
     535                if (t->type == T_SANITY)
     536                        type = " (sanity)";
     537                if (t->type == T_STRESS)
     538                        type = " (stress)";
     539
     540                printf("%zu: %s ..%s %s\n", i, t->name, type, t->desc);
     541        }
     542}
     543
    62544
    63545static void print_usage(void)
    64546{
    65         printf("Usage: rcutest \n");
     547        printf("Usage: rcutest [test_name|test_number]\n");
     548        list_tests();
     549       
     550        printf("\nExample usage:\n");
     551        printf("\trcutest *\n");
     552        printf("\trcutest sanity-tests\n");
    66553}
    67554
     
    69556int main(int argc, char **argv)
    70557{
    71         if (argc >= 2) {
     558        rcu_register_fibril();
     559       
     560        if (argc != 2) {
    72561                print_usage();
    73                 return 1;
    74         }
    75        
    76         rcu_register_fibril();
    77        
    78         invoke_rcu();
    79         /* todo: impl */
    80        
    81         rcu_deregister_fibril();
    82        
    83         return 0;
     562               
     563                rcu_deregister_fibril();
     564                return 2;
     565        }
     566       
     567        test_desc_t *t = find_test(argv[1]);
     568       
     569        if (t) {
     570                printf("Running '%s'...\n", t->name);
     571                bool ok = t->func();
     572               
     573                printf("%s: '%s'\n", ok ? "Passed" : "FAILED", t->name);
     574               
     575                rcu_deregister_fibril();
     576                return ok ? 0 : 1;
     577        } else {
     578                printf("Non-existent test name.\n");
     579                list_tests();
     580               
     581                rcu_deregister_fibril();
     582                return 3;
     583        }
    84584}
    85585
Note: See TracChangeset for help on using the changeset viewer.