/* * ***************************************************************************** * * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018-2020 Gavin D. Howard and contributors. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. * * ***************************************************************************** */ #define _GNU_SOURCE #define _XOPEN_SOURCE (700) #include #include #include #include #include #include #include #include #include #include "time.c" #ifndef KERNEL #ifndef SHMEM #error Must define one of SHMEM or KERNEL #endif // SHMEM #else //KERNEL #ifdef SHMEM #error Must define only one of SHMEM or KERNEL #endif // SHMEM #endif // KERNEL #define LIMIT ((size_t) 100000000) #define CONSUMER(p) ((p) == 0) #define SIZE ((1 << 12) / sizeof(size_t)) #define MASK (SIZE - 1) #ifdef SHMEM typedef struct shmem { size_t* a; volatile sig_atomic_t* sent; volatile sig_atomic_t* read; } Shmem; Shmem s; #else // SHMEM int in; int out; typedef union size { size_t s; char a[sizeof(size_t)]; } Size; #endif // SHMEM void err(char* msg, int exit_code) { fprintf(stderr, "error: %s\n", msg); exit(exit_code); } #ifdef SHMEM Shmem setup_shmem(void) { Shmem s; // Our memory buffer will be readable and writable: int protection = PROT_READ | PROT_WRITE; // The buffer will be shared (meaning other processes can access it), but // anonymous (meaning third-party processes cannot obtain an address for // it), so only this process and its children will be able to use it: int visibility = MAP_SHARED | MAP_ANONYMOUS; // The remaining parameters to `mmap()` are not important for this use case, // but the manpage for `mmap` explains their purpose. s.a = mmap(NULL, 1 << 12, protection, visibility, -1, 0); if (s.a == NULL) err("mmap of array failed", 5); s.sent = mmap(NULL, sizeof(size_t), protection, visibility, -1, 0); if (s.sent == NULL) err("mmap of sent failed", 6); s.read = mmap(NULL, sizeof(size_t), protection, visibility, -1, 0); if (s.read == NULL) err("mmap of read failed", 7); return s; } size_t get_val(volatile sig_atomic_t* v) { sig_atomic_t val1, val2; do { val1 = *v; val2 = *v; } while (val1 != val2); return val1; } #endif // SHMEM size_t receive(sig_atomic_t* rd) { size_t idx; #ifdef KERNEL Size s; ssize_t r; r = read(in, s.a, sizeof(size_t)); /*fprintf(stderr, "r: %zd\n", r);*/ if (r != sizeof(size_t) && r != 0) err("could not read", 13); idx = s.s; #else // KERNEL sig_atomic_t val; do { val = get_val(s.sent); } while ((*rd & MASK) == (val & MASK)); idx = s.a[(*rd) & MASK]; *rd += 1; *s.read = *rd; #endif // KERNEL return idx; } void send(size_t idx, sig_atomic_t* st) { *st += 1; #ifdef KERNEL Size s; ssize_t w; s.s = idx; errno = 0; w = write(out, s.a, sizeof(size_t)); if (w != sizeof(size_t)) err("could not write", 14); #else // KERNEL sig_atomic_t val; do { val = get_val(s.read); } while ((*st & MASK) == (val & MASK)); s.a[(*st - 1) & MASK] = idx; *s.sent = *st; #endif // KERNEL } int main(int argc, char* argv[]) { size_t i, sum = 0; sig_atomic_t processed = 0; cpu_set_t set; pid_t pid; int pipefds[2]; TimeSpec start, end, diff; int status; nice(-20); ts_time(&start); #ifdef SHMEM s = setup_shmem(); #else signal(SIGPIPE, SIG_IGN); if (pipe2(pipefds, 0) < 0) { err("couldn't create pipe", 8); } #endif // SHMEM pid = fork(); if (pid < 0) { err("fork failed", 1); } #ifdef KERNEL if (pid == 0) { close(pipefds[1]); in = pipefds[0]; } else { close(pipefds[0]); out = pipefds[1]; } #endif // KERNEL #ifdef SHMEM CPU_ZERO(&set); if (CONSUMER(pid)) CPU_SET(1, &set); else CPU_SET(0, &set); if (sched_setaffinity(0, sizeof(cpu_set_t), &set) < 0) { err("could not set cpu affinity", 2); } #endif // SHMEM if (CONSUMER(pid)) { for (i = 0; i <= LIMIT; ++i) { sum += receive(&processed); } if (sum != ((LIMIT * (LIMIT + 1)) / 2)) err("wrong sum", 15); #ifndef NDEBUG #ifdef KERNEL printf("KERNEL Sum: %zu\n", sum); #else // KERNEL printf("SHMEM Sum: %zu\n", sum); #endif // KERNEL #endif // NDEBUG } else { for (i = 0; i <= LIMIT; ++i) { send(i, &processed); } } if (!CONSUMER(pid)) { waitpid(pid, &status, 0); if (WEXITSTATUS(status) != 0) err("child exited bad", 11); ts_time(&end); diff = ts_diff(end, start); #ifdef KERNEL printf("KERNEL Time: "); #else // KERNEL printf("SHMEM Time: "); #endif ts_print(diff, LIMIT); printf("\n"); } #ifdef KERNEL if (CONSUMER(pid)) close(in); else close(out); #endif // KERNEL return 0; }