Make pthread mutexes more scalable

pthread_mutex_lock() now uses a better algorithm which goes much faster
in multithreaded environments that have lock contention. This comes at
the cost of adding some fixed-cost overhead to mutex invocations. That
doesn't matter for Cosmopolitan because our core libraries all encode
locking operations as NOP instructions when in single-threaded mode.
Overhead only applies starting the moment you first call clone().
This commit is contained in:
Justine Tunney 2022-09-05 13:06:34 -07:00
parent 7de2f229a7
commit 7ff0ea8c13
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
32 changed files with 410 additions and 112 deletions

View file

@ -20,6 +20,7 @@
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/futex.internal.h"
#include "libc/intrin/pthread.h"
/**
@ -28,9 +29,18 @@
* @return 0 on success or error number on failure
* @raises EPERM if in error check mode and not owned by caller
*/
int(pthread_mutex_unlock)(pthread_mutex_t *mutex) {
int me, owner;
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
int c, me, owner;
switch (mutex->attr) {
case PTHREAD_MUTEX_NORMAL:
// From Futexes Are Tricky Version 1.1 § Mutex, Take 3;
// Ulrich Drepper, Red Hat Incorporated, June 27, 2004.
if ((c = atomic_fetch_sub_explicit(&mutex->lock, 1,
memory_order_release)) != 1) {
atomic_store_explicit(&mutex->lock, 0, memory_order_release);
_futex_wake_private(&mutex->lock, 1);
}
return 0;
case PTHREAD_MUTEX_ERRORCHECK:
me = gettid();
owner = atomic_load_explicit(&mutex->lock, memory_order_relaxed);
@ -41,8 +51,6 @@ int(pthread_mutex_unlock)(pthread_mutex_t *mutex) {
// fallthrough
case PTHREAD_MUTEX_RECURSIVE:
if (--mutex->reent) return 0;
// fallthrough
case PTHREAD_MUTEX_NORMAL:
atomic_store_explicit(&mutex->lock, 0, memory_order_relaxed);
if (atomic_load_explicit(&mutex->waits, memory_order_relaxed) > 0) {
_pthread_mutex_wake(mutex);