mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 15:38:22 +00:00
Test rwlock more
This commit is contained in:
parent
69402f4d78
commit
9cc1bd04b2
5 changed files with 230 additions and 39 deletions
|
@ -30,7 +30,8 @@ LIBC_TESTLIB_A_HDRS = \
|
|||
libc/testlib/moby.h \
|
||||
libc/testlib/subprocess.h \
|
||||
libc/testlib/testlib.h \
|
||||
libc/testlib/viewables.h
|
||||
libc/testlib/trace.h \
|
||||
libc/testlib/viewables.h \
|
||||
|
||||
LIBC_TESTLIB_A_SRCS_S = \
|
||||
libc/testlib/bench.S \
|
||||
|
@ -80,9 +81,10 @@ LIBC_TESTLIB_A_SRCS_C = \
|
|||
libc/testlib/testrunner.c \
|
||||
libc/testlib/thunks.c \
|
||||
libc/testlib/tmptest.c \
|
||||
libc/testlib/trace.c \
|
||||
libc/testlib/waitforexit.c \
|
||||
libc/testlib/waitforterm.c \
|
||||
libc/testlib/yield.c
|
||||
libc/testlib/yield.c \
|
||||
|
||||
LIBC_TESTLIB_A_SRCS = \
|
||||
$(LIBC_TESTLIB_A_SRCS_S) \
|
||||
|
|
151
libc/testlib/trace.c
Normal file
151
libc/testlib/trace.c
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2024 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "trace.h"
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <threads.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct TraceEvent {
|
||||
unsigned long long ts;
|
||||
int pid;
|
||||
int tid;
|
||||
const char* name;
|
||||
const char* cat;
|
||||
char ph;
|
||||
};
|
||||
|
||||
static int g_pid;
|
||||
static atomic_bool g_oom;
|
||||
static atomic_int g_count;
|
||||
static thread_local int g_id;
|
||||
static thread_local int g_ids;
|
||||
static thread_local int g_tid;
|
||||
static unsigned long g_start_rdtsc;
|
||||
static struct TraceEvent g_events[1000000];
|
||||
|
||||
static unsigned long rdtsc(void) {
|
||||
#ifdef __x86_64__
|
||||
unsigned ax, dx;
|
||||
__asm__ volatile("rdtsc" : "=a"(ax), "=d"(dx));
|
||||
return (unsigned long)dx << 32 | ax;
|
||||
#else
|
||||
unsigned long c;
|
||||
__asm__ volatile("mrs %0, cntvct_el0" : "=r"(c));
|
||||
return c * 48; // the fudge factor
|
||||
#endif
|
||||
}
|
||||
|
||||
static int cosmo_trace_oom(void) {
|
||||
if (atomic_load_explicit(&g_oom, memory_order_relaxed))
|
||||
return -1;
|
||||
if (atomic_exchange_explicit(&g_oom, true, memory_order_acq_rel))
|
||||
return -1;
|
||||
fprintf(stderr, "warning: ran out of trace event memory\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cosmo_trace_reserve(int count) {
|
||||
int id = atomic_load_explicit(&g_count, memory_order_relaxed);
|
||||
if (id + count > sizeof(g_events) / sizeof(*g_events))
|
||||
return cosmo_trace_oom();
|
||||
id = atomic_fetch_add_explicit(&g_count, count, memory_order_acq_rel);
|
||||
if (id + count > sizeof(g_events) / sizeof(*g_events))
|
||||
return cosmo_trace_oom();
|
||||
return id;
|
||||
}
|
||||
|
||||
static void cosmo_trace_event(int id, const char* name, const char* cat,
|
||||
char ph) {
|
||||
g_events[id].ts = rdtsc();
|
||||
g_events[id].pid = g_pid ? g_pid - 1 : getpid();
|
||||
g_events[id].tid = g_tid ? g_tid - 1 : gettid();
|
||||
g_events[id].name = name;
|
||||
g_events[id].cat = cat;
|
||||
g_events[id].ph = ph;
|
||||
}
|
||||
|
||||
void cosmo_trace_set_pid(int pid) {
|
||||
g_pid = pid + 1;
|
||||
}
|
||||
|
||||
void cosmo_trace_set_tid(int tid) {
|
||||
g_tid = tid + 1;
|
||||
}
|
||||
|
||||
void cosmo_trace_begin(const char* name) {
|
||||
if (g_ids < 2) {
|
||||
g_ids = 20;
|
||||
g_id = cosmo_trace_reserve(g_ids);
|
||||
if (g_id == -1) {
|
||||
g_ids = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
cosmo_trace_event(g_id++, name, "category", 'B');
|
||||
--g_ids;
|
||||
}
|
||||
|
||||
void cosmo_trace_end(const char* name) {
|
||||
if (g_ids < 1)
|
||||
return;
|
||||
cosmo_trace_event(g_id++, name, "category", 'E');
|
||||
--g_ids;
|
||||
}
|
||||
|
||||
static void cosmo_trace_save(const char* filename) {
|
||||
int count = atomic_load_explicit(&g_count, memory_order_relaxed);
|
||||
if (!count)
|
||||
return;
|
||||
fprintf(stderr, "saving trace to %s...\n", filename);
|
||||
FILE* file = fopen(filename, "w");
|
||||
if (!file) {
|
||||
perror(filename);
|
||||
return;
|
||||
}
|
||||
fprintf(file, "[\n");
|
||||
bool once = false;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!g_events[i].name)
|
||||
continue;
|
||||
if (!once) {
|
||||
once = true;
|
||||
} else {
|
||||
fputs(",\n", file);
|
||||
}
|
||||
fprintf(file,
|
||||
"{\"name\": \"%s\", \"cat\": \"%s\", \"ph\": \"%c\", "
|
||||
"\"ts\": %.3f, \"pid\": %d, \"tid\": %d}",
|
||||
g_events[i].name, g_events[i].cat, g_events[i].ph,
|
||||
(g_events[i].ts - g_start_rdtsc) / 3000., g_events[i].pid,
|
||||
g_events[i].tid);
|
||||
}
|
||||
fprintf(file, "\n]\n");
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void trace_startup(void) {
|
||||
g_start_rdtsc = rdtsc();
|
||||
}
|
||||
|
||||
__attribute__((__destructor__)) static void trace_shutdown(void) {
|
||||
cosmo_trace_save("trace.json"); // see chrome://tracing/
|
||||
}
|
11
libc/testlib/trace.h
Normal file
11
libc/testlib/trace.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_TESTLIB_TRACE_H_
|
||||
#define COSMOPOLITAN_LIBC_TESTLIB_TRACE_H_
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void cosmo_trace_set_pid(int);
|
||||
void cosmo_trace_set_tid(int);
|
||||
void cosmo_trace_begin(const char*);
|
||||
void cosmo_trace_end(const char*);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_TESTLIB_TRACE_H_ */
|
|
@ -1,11 +1,14 @@
|
|||
#define USE POSIX_RECURSIVE
|
||||
#define USE POSIX
|
||||
#define ITERATIONS 100000
|
||||
#define THREADS 30
|
||||
|
||||
#define SPIN 1
|
||||
#define FUTEX 2
|
||||
#define POSIX 3
|
||||
#define POSIX_RECURSIVE 4
|
||||
#define FUTEX_SHARED 3
|
||||
#define POSIX 4
|
||||
#define POSIX_RECURSIVE 5
|
||||
#define RWLOCK 6
|
||||
#define RWLOCK_SHARED 7
|
||||
|
||||
#ifdef __COSMOPOLITAN__
|
||||
#include <cosmo.h>
|
||||
|
@ -274,8 +277,11 @@ void lock(atomic_int *futex) {
|
|||
word = atomic_exchange_explicit(futex, 2, memory_order_acquire);
|
||||
while (word > 0) {
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
||||
#if USE == FUTEX
|
||||
nsync_futex_wait_(futex, 2, 0, 0, 0);
|
||||
#if USE == FUTEX || USE == FUTEX_SHARED
|
||||
cosmo_futex_wait(
|
||||
futex, 2,
|
||||
USE == FUTEX_SHARED ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE,
|
||||
0, 0);
|
||||
#else
|
||||
pthread_yield_np();
|
||||
#endif
|
||||
|
@ -288,8 +294,10 @@ void unlock(atomic_int *futex) {
|
|||
int word = atomic_fetch_sub_explicit(futex, 1, memory_order_release);
|
||||
if (word == 2) {
|
||||
atomic_store_explicit(futex, 0, memory_order_release);
|
||||
#if USE == FUTEX
|
||||
nsync_futex_wake_(futex, 1, 0);
|
||||
#if USE == FUTEX || USE == FUTEX_SHARED
|
||||
cosmo_futex_wake(
|
||||
futex, 1,
|
||||
USE == FUTEX_SHARED ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -297,6 +305,7 @@ void unlock(atomic_int *futex) {
|
|||
int g_chores;
|
||||
atomic_int g_lock;
|
||||
pthread_mutex_t g_locker;
|
||||
pthread_rwlock_t g_rwlocker;
|
||||
|
||||
void *worker(void *arg) {
|
||||
for (int i = 0; i < ITERATIONS; ++i) {
|
||||
|
@ -304,6 +313,10 @@ void *worker(void *arg) {
|
|||
pthread_mutex_lock(&g_locker);
|
||||
++g_chores;
|
||||
pthread_mutex_unlock(&g_locker);
|
||||
#elif USE == RWLOCK || USE == RWLOCK_SHARED
|
||||
pthread_rwlock_wrlock(&g_rwlocker);
|
||||
++g_chores;
|
||||
pthread_rwlock_unlock(&g_rwlocker);
|
||||
#else
|
||||
lock(&g_lock);
|
||||
++g_chores;
|
||||
|
@ -331,7 +344,6 @@ int main() {
|
|||
struct timeval start;
|
||||
gettimeofday(&start, 0);
|
||||
|
||||
pthread_mutex_t lock;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
#if USE == POSIX_RECURSIVE
|
||||
|
@ -342,6 +354,14 @@ int main() {
|
|||
pthread_mutex_init(&g_locker, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
|
||||
pthread_rwlockattr_t rwattr;
|
||||
pthread_rwlockattr_init(&rwattr);
|
||||
#if USE == RWLOCK_SHARED
|
||||
pthread_rwlockattr_setpshared(&rwattr, PTHREAD_PROCESS_SHARED);
|
||||
#endif
|
||||
pthread_rwlock_init(&g_rwlocker, &rwattr);
|
||||
pthread_rwlockattr_destroy(&rwattr);
|
||||
|
||||
pthread_t th[THREADS];
|
||||
for (int i = 0; i < THREADS; ++i)
|
||||
pthread_create(&th[i], 0, worker, 0);
|
||||
|
@ -360,7 +380,8 @@ int main() {
|
|||
tomicros(ru.ru_utime), //
|
||||
tomicros(ru.ru_stime));
|
||||
|
||||
pthread_mutex_destroy(&lock);
|
||||
pthread_rwlock_destroy(&g_rwlocker);
|
||||
pthread_mutex_destroy(&g_locker);
|
||||
|
||||
#ifdef __COSMOPOLITAN__
|
||||
CheckForMemoryLeaks();
|
||||
|
|
|
@ -18,34 +18,32 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdalign.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
#define READERS 8
|
||||
#define WRITERS 2
|
||||
#define READER_ITERATIONS 10000
|
||||
#define WRITER_ITERATIONS 1000
|
||||
#define READERS 8
|
||||
#define WRITERS 2
|
||||
#define ITERATIONS 1000
|
||||
|
||||
int writes;
|
||||
atomic_int reads;
|
||||
atomic_bool done;
|
||||
alignas(128) int foo;
|
||||
alignas(128) int bar;
|
||||
pthread_rwlock_t lock;
|
||||
pthread_rwlockattr_t attr;
|
||||
pthread_barrier_t barrier;
|
||||
|
||||
FIXTURE(pthread_rwlock, private) {
|
||||
reads = 0;
|
||||
writes = 0;
|
||||
ASSERT_EQ(0, pthread_rwlockattr_init(&attr));
|
||||
ASSERT_EQ(0, pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE));
|
||||
ASSERT_EQ(0, pthread_rwlock_init(&lock, &attr));
|
||||
ASSERT_EQ(0, pthread_rwlockattr_destroy(&attr));
|
||||
void delay(int k) {
|
||||
int n = rand() % k;
|
||||
for (volatile int i = 0; i < n; ++i) {
|
||||
}
|
||||
}
|
||||
|
||||
FIXTURE(pthread_rwlock, pshared) {
|
||||
reads = 0;
|
||||
writes = 0;
|
||||
void SetUp(void) {
|
||||
ASSERT_EQ(0, pthread_rwlockattr_init(&attr));
|
||||
ASSERT_EQ(0, pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED));
|
||||
ASSERT_EQ(0, pthread_rwlock_init(&lock, &attr));
|
||||
|
@ -58,23 +56,33 @@ void TearDown(void) {
|
|||
|
||||
void *Reader(void *arg) {
|
||||
pthread_barrier_wait(&barrier);
|
||||
for (int i = 0; i < READER_ITERATIONS; ++i) {
|
||||
while (!atomic_load_explicit(&done, memory_order_relaxed)) {
|
||||
ASSERT_EQ(0, pthread_rwlock_rdlock(&lock));
|
||||
++reads;
|
||||
// cosmo_trace_begin("reader");
|
||||
int x = foo;
|
||||
usleep(1); // delay(100000);
|
||||
int y = bar;
|
||||
ASSERT_EQ(x, y);
|
||||
// cosmo_trace_end("reader");
|
||||
ASSERT_EQ(0, pthread_rwlock_unlock(&lock));
|
||||
usleep(1); // delay(100000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *Writer(void *arg) {
|
||||
pthread_barrier_wait(&barrier);
|
||||
for (int i = 0; i < WRITER_ITERATIONS; ++i) {
|
||||
for (int i = 0; i < ITERATIONS; ++i) {
|
||||
ASSERT_EQ(0, pthread_rwlock_wrlock(&lock));
|
||||
++writes;
|
||||
// cosmo_trace_begin("writer");
|
||||
++foo;
|
||||
delay(100);
|
||||
++bar;
|
||||
// cosmo_trace_end("writer");
|
||||
ASSERT_EQ(0, pthread_rwlock_unlock(&lock));
|
||||
for (volatile int i = 0; i < 100; ++i)
|
||||
pthread_pause_np();
|
||||
delay(100);
|
||||
}
|
||||
done = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -82,14 +90,12 @@ TEST(pthread_rwlock_rdlock, test) {
|
|||
int i;
|
||||
pthread_t *t = gc(malloc(sizeof(pthread_t) * (READERS + WRITERS)));
|
||||
ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, READERS + WRITERS));
|
||||
for (i = 0; i < READERS + WRITERS; ++i) {
|
||||
for (i = 0; i < READERS + WRITERS; ++i)
|
||||
ASSERT_SYS(0, 0,
|
||||
pthread_create(t + i, 0, i < READERS ? Reader : Writer, 0));
|
||||
}
|
||||
for (i = 0; i < READERS + WRITERS; ++i) {
|
||||
for (i = 0; i < READERS + WRITERS; ++i)
|
||||
EXPECT_SYS(0, 0, pthread_join(t[i], 0));
|
||||
}
|
||||
EXPECT_EQ(READERS * READER_ITERATIONS, reads);
|
||||
EXPECT_EQ(WRITERS * WRITER_ITERATIONS, writes);
|
||||
EXPECT_EQ(WRITERS * ITERATIONS, foo);
|
||||
EXPECT_EQ(WRITERS * ITERATIONS, bar);
|
||||
ASSERT_EQ(0, pthread_barrier_destroy(&barrier));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue