mirror of
https://github.com/darlinghq/darling-xnu.git
synced 2025-02-17 03:37:43 +00:00
203 lines
4.8 KiB
C
203 lines
4.8 KiB
C
// vim:noexpandtab
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/ulock.h>
|
|
|
|
#include "turnstile_multihop_types.h"
|
|
|
|
typedef _Atomic(u32) lock_t;
|
|
|
|
__inline static void
|
|
yield(void)
|
|
{
|
|
#if !defined(__x86_64__) && !defined(__i386__)
|
|
__asm volatile ("yield");
|
|
#else
|
|
__asm volatile ("pause");
|
|
#endif
|
|
}
|
|
|
|
__inline static void
|
|
wfe(void)
|
|
{
|
|
#if !defined(__x86_64__) && !defined(__i386__)
|
|
__asm volatile ("wfe");
|
|
#else
|
|
__asm volatile ("pause");
|
|
#endif
|
|
}
|
|
|
|
__inline static void
|
|
wfi(void)
|
|
{
|
|
#if !defined(__x86_64__) && !defined(__i386__)
|
|
__asm volatile ("wfi");
|
|
#else
|
|
__asm volatile ("pause");
|
|
#endif
|
|
}
|
|
|
|
__inline static void
|
|
sev(void)
|
|
{
|
|
#if !defined(__x86_64__) && !defined(__i386__)
|
|
__asm volatile ("sev");
|
|
#endif
|
|
}
|
|
|
|
#include <os/tsd.h>
|
|
|
|
#ifndef __TSD_MACH_THREAD_SELF
|
|
#define __TSD_MACH_THREAD_SELF 3
|
|
#endif
|
|
|
|
__inline static mach_port_name_t
|
|
_os_get_self(void)
|
|
{
|
|
mach_port_name_t self = (mach_port_name_t)(uintptr_t)(void *)_os_tsd_get_direct(__TSD_MACH_THREAD_SELF);
|
|
return self;
|
|
}
|
|
|
|
#define ULL_WAITERS 1U
|
|
|
|
static uint32_t lock_no_wait[4] = { 0, 0, 0, 0};
|
|
static uint32_t lock_wait[4] = { 0, 0, 0, 0};
|
|
|
|
static mach_port_name_t main_thread_name = 0;
|
|
|
|
__inline static void
|
|
ull_lock(lock_t *lock, int id, uint opcode, uint flags)
|
|
{
|
|
u32 thread_id = _os_get_self() & ~0x3u;
|
|
u32 ull_locked = (opcode == UL_UNFAIR_LOCK) ? thread_id : 4u;
|
|
u32 mach_id = _os_get_self() >> 2;
|
|
u32 prev;
|
|
bool succeeded = false;
|
|
bool waiters = false;
|
|
bool called_wait = false;
|
|
u32 count = 0;
|
|
|
|
do {
|
|
count++;
|
|
if ((count % 100000) == 0) {
|
|
printf("[%d,%d]%s>top of loop count=%d\n", id, mach_id, __FUNCTION__, count);
|
|
}
|
|
u32 new = waiters ? (ULL_WAITERS | ull_locked) : ull_locked;
|
|
prev = 0;
|
|
__c11_atomic_compare_exchange_strong(lock, &prev, new, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
|
|
if (prev == 0) {
|
|
/* Was unlocked, now locked */
|
|
succeeded = true;
|
|
break;
|
|
}
|
|
|
|
u32 value = prev;
|
|
if (!(value & ULL_WAITERS)) {
|
|
new = value | ULL_WAITERS;
|
|
__c11_atomic_compare_exchange_strong(lock, &prev, new, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
|
|
if (prev == value) {
|
|
/* succeeded in setting ULL_WAITERS */
|
|
value = new;
|
|
} else if (prev & ULL_WAITERS) {
|
|
/* Didn't succeed, but someone else already set ULL_WAITERS */
|
|
value = prev;
|
|
} else {
|
|
/* Something changed under us, so try again */
|
|
if (count % 100000 == 0) {
|
|
printf("[%d,%d]%s>Something changed under us, prev=%d\n", id, mach_id, __FUNCTION__, prev);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
/* Locked with waiters indication, so block */
|
|
int ret = __ulock_wait(flags | opcode, lock, value, 0);
|
|
called_wait = true;
|
|
if (ret < 0) {
|
|
if (flags & ULF_NO_ERRNO) {
|
|
errno = -ret;
|
|
}
|
|
if (errno == EFAULT) {
|
|
continue;
|
|
}
|
|
printf("[%d,%d]%s>ull_wait() error: %s\n", id, mach_id, __FUNCTION__, strerror(errno));
|
|
exit(1);
|
|
}
|
|
waiters = (ret > 0);
|
|
|
|
if (count % 100000 == 0) {
|
|
printf("[%d,%d]%s>bottom of loop prev=%d\n", id, mach_id, __FUNCTION__, prev);
|
|
}
|
|
} while (!succeeded);
|
|
|
|
if (called_wait) {
|
|
lock_wait[id]++;
|
|
} else {
|
|
lock_no_wait[id]++;
|
|
}
|
|
}
|
|
|
|
static uint32_t unlock_no_waiters[4] = { 0, 0, 0, 0};
|
|
static uint32_t unlock_waiters[4] = { 0, 0, 0, 0 };
|
|
static uint32_t unlock_waiters_gone[4] = { 0, 0, 0, 0 };
|
|
static uint32_t unlock_waiters_wake_thread[4] = { 0, 0, 0, 0 };
|
|
|
|
__inline static void
|
|
ull_unlock(lock_t *lock, int id, uint opcode, uint flags)
|
|
{
|
|
u32 thread_id = _os_get_self() & ~0x3u;
|
|
u32 ull_locked = (opcode == UL_UNFAIR_LOCK) ? thread_id : 4u;
|
|
u32 mach_id = _os_get_self() >> 2;
|
|
u32 prev = ull_locked;
|
|
__c11_atomic_compare_exchange_strong(lock, &prev, 0, __ATOMIC_RELEASE, __ATOMIC_RELAXED);
|
|
if (prev == ull_locked) {
|
|
unlock_no_waiters[id]++;
|
|
return;
|
|
}
|
|
|
|
if (prev == 0) {
|
|
printf("%s>already unlocked\n", __FUNCTION__);
|
|
exit(1);
|
|
}
|
|
|
|
if (prev == (ULL_WAITERS | ull_locked)) {
|
|
/* locked with waiters */
|
|
__c11_atomic_store(lock, 0, __ATOMIC_SEQ_CST);
|
|
|
|
if ((flags & ULF_WAKE_THREAD) && (_os_get_self() == main_thread_name)) {
|
|
flags &= ~(uint)ULF_WAKE_THREAD;
|
|
}
|
|
int ret = __ulock_wake((flags | opcode), lock, main_thread_name);
|
|
if ((ret < 0) && (flags & ULF_NO_ERRNO)) {
|
|
errno = -ret;
|
|
}
|
|
if ((flags & ULF_WAKE_THREAD) && (ret < 0) && (errno == EALREADY)) {
|
|
flags &= ~(uint)ULF_WAKE_THREAD;
|
|
ret = __ulock_wake((flags | opcode), lock, 0);
|
|
if ((ret < 0) && (flags & ULF_NO_ERRNO)) {
|
|
errno = -ret;
|
|
}
|
|
} else if ((flags & ULF_WAKE_THREAD) && (ret == 0)) {
|
|
unlock_waiters_wake_thread[id]++;
|
|
}
|
|
if (ret < 0) {
|
|
if (errno == ENOENT) {
|
|
unlock_waiters_gone[id]++;
|
|
} else {
|
|
printf("[%d,%d]%s>ull_wake() error: %s\n", id, mach_id, __FUNCTION__, strerror(errno));
|
|
exit(1);
|
|
}
|
|
}
|
|
unlock_waiters[id]++;
|
|
} else {
|
|
printf("%s>unexpected lock value %d\n", __FUNCTION__, prev);
|
|
exit(1);
|
|
}
|
|
}
|