diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-07-23 11:24:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-07-23 11:24:09 +0000 |
commit | e36b37583bebd229102f46c4ed7d2f6fad8697d4 (patch) | |
tree | 73937b6f051fcaaa1ccbdfbaa9f3a1f36bbedb9e /regressions/ck_epoch/validate/ck_epoch_section.c | |
parent | Initial commit. (diff) | |
download | ck-e36b37583bebd229102f46c4ed7d2f6fad8697d4.tar.xz ck-e36b37583bebd229102f46c4ed7d2f6fad8697d4.zip |
Adding upstream version 0.6.0.upstream/0.6.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | regressions/ck_epoch/validate/ck_epoch_section.c | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/regressions/ck_epoch/validate/ck_epoch_section.c b/regressions/ck_epoch/validate/ck_epoch_section.c new file mode 100644 index 0000000..12bcca1 --- /dev/null +++ b/regressions/ck_epoch/validate/ck_epoch_section.c @@ -0,0 +1,311 @@ +/* + * Copyright 2015 John Esmet. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> + +#include <ck_epoch.h> + +#include "../../common.h" + +static ck_epoch_t epc; +static ck_epoch_record_t record, record2; +static unsigned int cleanup_calls; + +static void +setup_test(void) +{ + + ck_epoch_init(&epc); + ck_epoch_register(&epc, &record); + ck_epoch_register(&epc, &record2); + cleanup_calls = 0; + + return; +} + +static void +teardown_test(void) +{ + + memset(&epc, 0, sizeof(ck_epoch_t)); + ck_epoch_unregister(&record); + memset(&record, 0, sizeof(ck_epoch_record_t)); + memset(&record2, 0, sizeof(ck_epoch_record_t)); + cleanup_calls = 0; + + return; +} + +static void +cleanup(ck_epoch_entry_t *e) +{ + (void) e; + + cleanup_calls++; + + return; +} + +static void +test_simple_read_section(void) +{ + ck_epoch_entry_t entry; + ck_epoch_section_t section; + + memset(&entry, 0, sizeof(ck_epoch_entry_t)); + setup_test(); + + ck_epoch_begin(&record, §ion); + ck_epoch_call(&record, &entry, cleanup); + assert(cleanup_calls == 0); + ck_epoch_end(&record, §ion); + ck_epoch_barrier(&record); + assert(cleanup_calls == 1); + + teardown_test(); + return; +} + +static void +test_nested_read_section(void) +{ + ck_epoch_entry_t entry1, entry2; + ck_epoch_section_t section1, section2; + + memset(&entry1, 0, sizeof(ck_epoch_entry_t)); + memset(&entry2, 0, sizeof(ck_epoch_entry_t)); + setup_test(); + + ck_epoch_begin(&record, §ion1); + ck_epoch_call(&record, &entry1, cleanup); + assert(cleanup_calls == 0); + + ck_epoch_begin(&record, §ion2); + ck_epoch_call(&record, &entry2, cleanup); + assert(cleanup_calls == 0); + + ck_epoch_end(&record, §ion2); + assert(cleanup_calls == 0); + + ck_epoch_end(&record, §ion1); + assert(cleanup_calls == 0); + + ck_epoch_barrier(&record); + assert(cleanup_calls == 2); + + teardown_test(); + return; +} + +struct obj { + ck_epoch_entry_t entry; + unsigned int destroyed; +}; + +static void * +barrier_work(void *arg) +{ + unsigned int *run; + + run = (unsigned int *)arg; + while (ck_pr_load_uint(run) != 0) { + /* + * Need to use record2, as record is local + * to the test thread. + */ + ck_epoch_barrier(&record2); + usleep(5 * 1000); + } + + return NULL; +} + +static void * +reader_work(void *arg) +{ + ck_epoch_record_t local_record; + ck_epoch_section_t section; + struct obj *o; + + ck_epoch_register(&epc, &local_record); + + o = (struct obj *)arg; + + /* + * Begin a read section. The calling thread has an open read section, + * so the object should not be destroyed for the lifetime of this + * thread. + */ + ck_epoch_begin(&local_record, §ion); + usleep((common_rand() % 100) * 1000); + assert(ck_pr_load_uint(&o->destroyed) == 0); + ck_epoch_end(&local_record, §ion); + + ck_epoch_unregister(&local_record); + + return NULL; +} + +static void +obj_destroy(ck_epoch_entry_t *e) +{ + struct obj *o; + + o = (struct obj *)e; + ck_pr_fas_uint(&o->destroyed, 1); + + return; +} + +static void +test_single_reader_with_barrier_thread(void) +{ + const int num_sections = 10; + struct obj o; + unsigned int run; + pthread_t thread; + ck_epoch_section_t sections[num_sections]; + int shuffled[num_sections]; + + run = 1; + memset(&o, 0, sizeof(struct obj)); + common_srand(time(NULL)); + setup_test(); + + if (pthread_create(&thread, NULL, barrier_work, &run) != 0) { + abort(); + } + + /* Start a bunch of sections. */ + for (int i = 0; i < num_sections; i++) { + ck_epoch_begin(&record, §ions[i]); + shuffled[i] = i; + if (i == num_sections / 2) { + usleep(1 * 1000); + } + } + + /* Generate a shuffle. */ + for (int i = num_sections - 1; i >= 0; i--) { + int k = common_rand() % (i + 1); + int tmp = shuffled[k]; + shuffled[k] = shuffled[i]; + shuffled[i] = tmp; + } + + ck_epoch_call(&record, &o.entry, obj_destroy); + + /* Close the sections in shuffle-order. */ + for (int i = 0; i < num_sections; i++) { + ck_epoch_end(&record, §ions[shuffled[i]]); + if (i != num_sections - 1) { + assert(ck_pr_load_uint(&o.destroyed) == 0); + usleep(3 * 1000); + } + } + + ck_pr_store_uint(&run, 0); + if (pthread_join(thread, NULL) != 0) { + abort(); + } + + ck_epoch_barrier(&record); + assert(ck_pr_load_uint(&o.destroyed) == 1); + + teardown_test(); + + return; +} + +static void +test_multiple_readers_with_barrier_thread(void) +{ + const int num_readers = 10; + struct obj o; + unsigned int run; + ck_epoch_section_t section; + pthread_t threads[num_readers + 1]; + + run = 1; + memset(&o, 0, sizeof(struct obj)); + memset(§ion, 0, sizeof(ck_epoch_section_t)); + common_srand(time(NULL)); + setup_test(); + + /* Create a thread to call barrier() while we create reader threads. + * Each barrier will attempt to move the global epoch forward so + * it will make the read section code coverage more interesting. */ + if (pthread_create(&threads[num_readers], NULL, + barrier_work, &run) != 0) { + abort(); + } + + ck_epoch_begin(&record, §ion); + ck_epoch_call(&record, &o.entry, obj_destroy); + + for (int i = 0; i < num_readers; i++) { + if (pthread_create(&threads[i], NULL, reader_work, &o) != 0) { + abort(); + } + } + + ck_epoch_end(&record, §ion); + + ck_pr_store_uint(&run, 0); + if (pthread_join(threads[num_readers], NULL) != 0) { + abort(); + } + + /* After the barrier, the object should be destroyed and readers + * should return. */ + for (int i = 0; i < num_readers; i++) { + if (pthread_join(threads[i], NULL) != 0) { + abort(); + } + } + + teardown_test(); + return; +} + +int +main(void) +{ + + test_simple_read_section(); + test_nested_read_section(); + test_single_reader_with_barrier_thread(); + test_multiple_readers_with_barrier_thread(); + + return 0; +} |