/* * Copyright 2011-2015 Samy Al Bahra. * 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 #include #include #include #include #include #include "../../common.h" #ifndef STEPS #define STEPS 1000000 #endif struct example { unsigned int a; unsigned int b; unsigned int c; }; static struct example global CK_CC_CACHELINE; static ck_sequence_t seqlock CK_CC_CACHELINE = CK_SEQUENCE_INITIALIZER; static unsigned int barrier; static struct affinity affinerator; static void validate(struct example *copy) { if (copy->b != copy->a + 1000) { ck_error("ERROR: Failed regression: copy->b (%u != %u + %u / %u)\n", copy->b, copy->a, 1000, copy->a + 1000); } if (copy->c != copy->a + copy->b) { ck_error("ERROR: Failed regression: copy->c (%u != %u + %u / %u)\n", copy->c, copy->a, copy->b, copy->a + copy->b); } return; } static void * consumer(void *unused CK_CC_UNUSED) { struct example copy; uint32_t version; unsigned int retries = 0; unsigned int i; unused = NULL; if (aff_iterate(&affinerator)) { perror("ERROR: Could not affine thread"); exit(EXIT_FAILURE); } while (ck_pr_load_uint(&barrier) == 0); for (i = 0; i < STEPS; i++) { /* * Attempt a read of the data structure. If the structure * has been modified between ck_sequence_read_begin and * ck_sequence_read_retry then attempt another read since * the data may be in an inconsistent state. */ do { version = ck_sequence_read_begin(&seqlock); copy.a = ck_pr_load_uint(&global.a); copy.b = ck_pr_load_uint(&global.b); copy.c = ck_pr_load_uint(&global.c); retries++; } while (ck_sequence_read_retry(&seqlock, version) == true); validate(©); CK_SEQUENCE_READ(&seqlock, &version) { copy.a = ck_pr_load_uint(&global.a); copy.b = ck_pr_load_uint(&global.b); copy.c = ck_pr_load_uint(&global.c); retries++; } validate(©); } fprintf(stderr, "%u retries.\n", retries - STEPS); ck_pr_dec_uint(&barrier); return (NULL); } int main(int argc, char *argv[]) { pthread_t *threads; unsigned int counter = 0; bool first = true; int n_threads, i; if (argc != 3) { ck_error("Usage: ck_sequence \n"); } n_threads = atoi(argv[1]) - 1; if (n_threads <= 0) { ck_error("ERROR: Number of threads must be greater than 0\n"); } threads = malloc(sizeof(pthread_t) * n_threads); if (threads == NULL) { ck_error("ERROR: Could not allocate memory for threads\n"); } affinerator.delta = atoi(argv[2]); affinerator.request = 0; for (i = 0; i < n_threads; i++) { if (pthread_create(&threads[i], NULL, consumer, NULL)) { ck_error("ERROR: Failed to create thread %d\n", i); } } for (;;) { /* * Update the shared data in a non-blocking fashion. * If the data is modified by multiple writers then * ck_sequence_write_begin must be called after acquiring * the associated lock and ck_sequence_write_end must be * called before relinquishing the lock. */ ck_sequence_write_begin(&seqlock); global.a = counter++; global.b = global.a + 1000; global.c = global.b + global.a; ck_sequence_write_end(&seqlock); if (first == true) { ck_pr_store_uint(&barrier, n_threads); first = false; } counter++; if (ck_pr_load_uint(&barrier) == 0) break; ck_pr_stall(); } printf("%u updates made.\n", counter); return (0); }