mutex spinning

This commit is contained in:
jacob 2025-07-06 17:46:10 -05:00
parent 994ed1e1e1
commit a397458c72
4 changed files with 60 additions and 27 deletions

View File

@ -3,29 +3,38 @@
#include "sys.h" #include "sys.h"
#include "memory.h" #include "memory.h"
#define DEFAULT_MUTEX_SPIN 4000
/* ========================== * /* ========================== *
* Mutex * Mutex
* ========================== */ * ========================== */
struct snc_lock snc_lock_e(struct snc_mutex *m) struct snc_lock snc_lock_spin_e(struct snc_mutex *m, i32 spin)
{ {
__prof; __prof;
b32 locked = false; b32 locked = false;
while (!locked) { while (!locked) {
/* Spin lock */
i32 spin_cnt = 0;
i32 v = atomic_i32_fetch_test_set(&m->v, 0, (1 << 31)); i32 v = atomic_i32_fetch_test_set(&m->v, 0, (1 << 31));
if (v == 0) { do {
locked = true; if (v == 0) {
} else { locked = true;
/* Set pending */ } else {
if ((v & (1 << 30)) == 0) { /* Set pending */
i32 old = atomic_i32_fetch_test_set(&m->v, v | (1 << 30), v); if ((v & (1 << 30)) == 0) {
while (old != v && (old & (1 << 30)) == 0) { i32 old = atomic_i32_fetch_test_set(&m->v, v | (1 << 30), v);
while (old != v && (old & (1 << 30)) == 0) {
v = old;
old = atomic_i32_fetch_test_set(&m->v, v | (1 << 30), v);
}
v = old; v = old;
old = atomic_i32_fetch_test_set(&m->v, v | (1 << 30), v);
} }
v = old;
} }
/* Wait for change */ ++spin_cnt;
} while (spin_cnt < spin && !locked);
/* Wait if not successful */
if (!locked) {
sys_wait(&m->v, &v, 4); sys_wait(&m->v, &v, 4);
} }
} }
@ -35,19 +44,24 @@ struct snc_lock snc_lock_e(struct snc_mutex *m)
return lock; return lock;
} }
struct snc_lock snc_lock_s(struct snc_mutex *m) struct snc_lock snc_lock_spin_s(struct snc_mutex *m, i32 spin)
{ {
__prof; __prof;
b32 locked = false; b32 locked = false;
while (!locked) { while (!locked) {
/* Spin lock */
i32 spin_cnt = 0;
i32 v = atomic_i32_fetch(&m->v); i32 v = atomic_i32_fetch(&m->v);
while (!locked && (v & 0xC0000000) == 0) { do {
/* Increment shared lock count */ while (!locked && (v & 0xC0000000) == 0) {
i32 old = atomic_i32_fetch_test_set(&m->v, v, v + 1); /* Lock has no exclusive or pending exclusive lock, increment shared count */
if (v == old) { i32 old = atomic_i32_fetch_test_set(&m->v, v, v + 1);
locked = true; if (v == old) {
locked = true;
}
} }
} } while (spin_cnt < spin && !locked);
/* Wait if not successful */
if (!locked) { if (!locked) {
sys_wait(&m->v, &v, 4); sys_wait(&m->v, &v, 4);
} }
@ -57,6 +71,16 @@ struct snc_lock snc_lock_s(struct snc_mutex *m)
return lock; return lock;
} }
struct snc_lock snc_lock_e(struct snc_mutex *m)
{
return snc_lock_spin_e(m, DEFAULT_MUTEX_SPIN);
}
struct snc_lock snc_lock_s(struct snc_mutex *m)
{
return snc_lock_spin_s(m, DEFAULT_MUTEX_SPIN);
}
void snc_unlock(struct snc_lock *l) void snc_unlock(struct snc_lock *l)
{ {
__prof; __prof;

View File

@ -14,6 +14,8 @@ struct snc_mutex {
struct atomic_i32 v; struct atomic_i32 v;
}; };
struct snc_lock snc_lock_spin_e(struct snc_mutex *m, i32 spin);
struct snc_lock snc_lock_spin_s(struct snc_mutex *m, i32 spin);
struct snc_lock snc_lock_e(struct snc_mutex *m); struct snc_lock snc_lock_e(struct snc_mutex *m);
struct snc_lock snc_lock_s(struct snc_mutex *m); struct snc_lock snc_lock_s(struct snc_mutex *m);
void snc_unlock(struct snc_lock *lock); void snc_unlock(struct snc_lock *lock);

View File

@ -450,6 +450,7 @@ b32 sys_run_command(struct string cmd);
/* Futex-like wait & wake */ /* Futex-like wait & wake */
void sys_wait(void *addr, void *cmp, u32 size); void sys_wait(void *addr, void *cmp, u32 size);
void sys_wake_single(void *addr);
void sys_wake_all(void *addr); void sys_wake_all(void *addr);
/* ========================== * /* ========================== *

View File

@ -111,7 +111,6 @@ struct alignas(64) wait_list {
/* =================================================== */ /* =================================================== */
struct yielder *last_yielder; /* 8 bytes */ struct yielder *last_yielder; /* 8 bytes */
/* =================================================== */ /* =================================================== */
struct yielder *first_free_yielder; /* 8 bytes */
/* =================================================== */ /* =================================================== */
i32 num_yielders; /* 4 bytes */ i32 num_yielders; /* 4 bytes */
u8 _pad0[4]; /* 4 bytes (padding */ u8 _pad0[4]; /* 4 bytes (padding */
@ -133,10 +132,12 @@ struct alignas(64) wait_bin {
/* =================================================== */ /* =================================================== */
struct wait_list *first_free_wait_list; /* 8 bytes */ struct wait_list *first_free_wait_list; /* 8 bytes */
/* =================================================== */ /* =================================================== */
struct yielder *first_free_yielder;
/* =================================================== */
struct atomic_i32 lock; /* 4 bytes */ struct atomic_i32 lock; /* 4 bytes */
u8 _pad0[4]; /* 4 bytes (padding) */ u8 _pad0[4]; /* 4 bytes (padding) */
/* =================================================== */ /* =================================================== */
u8 _pad1[32]; /* 32 bytes (padding) */ u8 _pad1[24]; /* 24 bytes (padding) */
}; };
STATIC_ASSERT(sizeof(struct wait_bin) == 64); /* Assume wait_bin fits in one cache line (increase if necessary) */ STATIC_ASSERT(sizeof(struct wait_bin) == 64); /* Assume wait_bin fits in one cache line (increase if necessary) */
STATIC_ASSERT(alignof(struct wait_bin) == 64); /* Avoid false sharing */ STATIC_ASSERT(alignof(struct wait_bin) == 64); /* Avoid false sharing */
@ -413,9 +414,14 @@ void sys_wait(void *addr, void *cmp, u32 size)
#endif #endif
} }
void sys_wake_single(void *addr)
{
ASSERT(false);
(UNUSED)addr;
}
void sys_wake_all(void *addr) void sys_wake_all(void *addr)
{ {
#if 1
u64 wait_bin_index = (u64)addr % NUM_WAIT_BINS; u64 wait_bin_index = (u64)addr % NUM_WAIT_BINS;
struct wait_bin *bin = &G.wait_bins[wait_bin_index]; struct wait_bin *bin = &G.wait_bins[wait_bin_index];
@ -480,8 +486,8 @@ void sys_wake_all(void *addr)
} }
} }
/* Free yielders */ /* Free yielders */
wait_list->last_yielder->next = wait_list->first_free_yielder; wait_list->last_yielder->next = bin->first_free_yielder;
wait_list->first_free_yielder = wait_list->first_yielder; bin->first_free_yielder = wait_list->first_yielder;
wait_list->first_yielder = NULL; wait_list->first_yielder = NULL;
wait_list->last_yielder = NULL; wait_list->last_yielder = NULL;
wait_list->num_yielders = 0; wait_list->num_yielders = 0;
@ -490,7 +496,6 @@ void sys_wake_all(void *addr)
} }
} }
atomic_i32_fetch_set(&bin->lock, 0); atomic_i32_fetch_set(&bin->lock, 0);
#endif
/* Wake blocking waiters */ /* Wake blocking waiters */
WakeByAddressAll(addr); WakeByAddressAll(addr);
@ -714,6 +719,7 @@ INTERNAL SYS_THREAD_DEF(worker_entry, worker_ctx_arg)
(UNUSED)ctx; (UNUSED)ctx;
{ {
/* TODO: Heuristic pinning */
HANDLE thread_handle = GetCurrentThread(); HANDLE thread_handle = GetCurrentThread();
b32 success = false; b32 success = false;
(UNUSED)success; (UNUSED)success;
@ -879,9 +885,9 @@ INTERNAL SYS_THREAD_DEF(worker_entry, worker_ctx_arg)
/* Allocate new yielder */ /* Allocate new yielder */
struct yielder *yielder = NULL; struct yielder *yielder = NULL;
if (wait_list->first_free_yielder) { if (bin->first_free_yielder) {
yielder = wait_list->first_free_yielder; yielder = bin->first_free_yielder;
wait_list->first_free_yielder = yielder->next; bin->first_free_yielder = yielder->next;
} else { } else {
while (atomic_i32_fetch_test_set(&G.yielders_arena_lock, 0, 1) != 0) ix_pause(); while (atomic_i32_fetch_test_set(&G.yielders_arena_lock, 0, 1) != 0) ix_pause();
{ {