
Audio sources & decoding are pretty much done and working well. Playback is not really implemented yet, I'm just hacking in a way to output an audio source. - Seriously microsoft wtf
160 lines
No EOL
4.9 KiB
C
160 lines
No EOL
4.9 KiB
C
|
|
typedef struct Spinlock Spinlock;
|
|
typedef struct Mutex Mutex;
|
|
typedef struct Binary_Semaphore Binary_Semaphore;
|
|
|
|
// These are probably your best friend for sync-free multi-processing.
|
|
inline bool compare_and_swap_8(uint8_t *a, uint8_t b, uint8_t old);
|
|
inline bool compare_and_swap_16(uint16_t *a, uint16_t b, uint16_t old);
|
|
inline bool compare_and_swap_32(uint32_t *a, uint32_t b, uint32_t old);
|
|
inline bool compare_and_swap_64(uint64_t *a, uint64_t b, uint64_t old);
|
|
inline bool compare_and_swap_bool(bool *a, bool b, bool old);
|
|
|
|
///
|
|
// Spinlock "primitive"
|
|
// Like a mutex but it eats up the entire core while waiting.
|
|
// Beneficial if contention is low or sync speed is important
|
|
void spinlock_init(Spinlock *l);
|
|
void spinlock_acquire_or_wait(Spinlock* l);
|
|
// This returns true if successfully acquired or false if timeout reached.
|
|
bool spinlock_acquire_or_wait_timeout(Spinlock* l, f64 timeout_seconds);
|
|
void spinlock_release(Spinlock* l);
|
|
|
|
///
|
|
// High-level mutex primitive (short spinlock then OS mutex lock)
|
|
// Just spins for a few (configurable) microseconds with a spinlock,
|
|
// and if acquiring fails it falls back to a OS mutex.
|
|
void mutex_init(Mutex *m);
|
|
void mutex_destroy(Mutex *m);
|
|
void mutex_acquire_or_wait(Mutex *m);
|
|
void mutex_release(Mutex *m);
|
|
|
|
///
|
|
// Binary semaphore
|
|
void binary_semaphore_init(Binary_Semaphore *sem, bool initial_state);
|
|
void binary_semaphore_destroy(Binary_Semaphore *sem);
|
|
void binary_semaphore_wait(Binary_Semaphore *sem);
|
|
void binary_semaphore_signal(Binary_Semaphore *sem);
|
|
|
|
typedef struct Spinlock {
|
|
bool locked;
|
|
} Spinlock;
|
|
|
|
void spinlock_init(Spinlock *l) {
|
|
memset(l, 0, sizeof(*l));
|
|
}
|
|
void spinlock_acquire_or_wait(Spinlock* l) {
|
|
while (true) {
|
|
bool expected = false;
|
|
if (compare_and_swap_bool(&l->locked, true, expected)) {
|
|
MEMORY_BARRIER;
|
|
return;
|
|
}
|
|
while (l->locked) {
|
|
// spinny boi
|
|
}
|
|
}
|
|
}
|
|
// Returns true on aquired, false if timeout seconds reached
|
|
bool spinlock_acquire_or_wait_timeout(Spinlock* l, f64 timeout_seconds) {
|
|
f64 start = os_get_current_time_in_seconds();
|
|
while (true) {
|
|
bool expected = false;
|
|
if (compare_and_swap_bool(&l->locked, true, expected)) {
|
|
MEMORY_BARRIER;
|
|
return true;
|
|
}
|
|
while (l->locked) {
|
|
// spinny boi
|
|
if ((os_get_current_time_in_seconds()-start) >= timeout_seconds) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
void spinlock_release(Spinlock* l) {
|
|
bool expected = true;
|
|
bool success = compare_and_swap_bool(&l->locked, false, expected);
|
|
assert(success, "This thread should have acquired the spinlock but compare_and_swap failed");
|
|
MEMORY_BARRIER;
|
|
}
|
|
|
|
|
|
///
|
|
// High-level mutex primitive (short spinlock then OS mutex lock)
|
|
#define MUTEX_DEFAULT_SPIN_TIME_MICROSECONDS 100
|
|
typedef struct Mutex {
|
|
Spinlock spinlock;
|
|
f64 spin_time_microseconds;
|
|
Mutex_Handle os_handle;
|
|
volatile bool spinlock_acquired;
|
|
volatile u64 acquiring_thread;
|
|
} Mutex;
|
|
void mutex_init(Mutex *m) {
|
|
spinlock_init(&m->spinlock);
|
|
m->spin_time_microseconds = MUTEX_DEFAULT_SPIN_TIME_MICROSECONDS;
|
|
m->os_handle = os_make_mutex();
|
|
m->spinlock_acquired = false;
|
|
m->acquiring_thread = 0;
|
|
}
|
|
void mutex_destroy(Mutex *m) {
|
|
os_destroy_mutex(m->os_handle);
|
|
}
|
|
void mutex_acquire_or_wait(Mutex *m) {
|
|
if (spinlock_acquire_or_wait_timeout(&m->spinlock, m->spin_time_microseconds / 1000000.0)) {
|
|
assert(!m->spinlock_acquired, "Internal sync error in Mutex");
|
|
m->spinlock_acquired = true;
|
|
MEMORY_BARRIER;
|
|
}
|
|
os_lock_mutex(m->os_handle);
|
|
|
|
assert(!m->acquiring_thread, "Internal sync error in Mutex: Multiple threads acquired");
|
|
m->acquiring_thread = context.thread_id;
|
|
MEMORY_BARRIER;
|
|
}
|
|
void mutex_release(Mutex *m) {
|
|
assert(m->acquiring_thread != 0, "Tried to release a mutex which is not acquired");
|
|
assert(m->acquiring_thread == context.thread_id, "Non-owning thread tried to release mutex");
|
|
m->acquiring_thread = 0;
|
|
MEMORY_BARRIER;
|
|
bool was_spinlock_acquired = m->spinlock_acquired;
|
|
m->spinlock_acquired = false;
|
|
MEMORY_BARRIER;
|
|
os_unlock_mutex(m->os_handle);
|
|
MEMORY_BARRIER;
|
|
if (was_spinlock_acquired) {
|
|
spinlock_release(&m->spinlock);
|
|
MEMORY_BARRIER;
|
|
}
|
|
MEMORY_BARRIER;
|
|
}
|
|
|
|
typedef struct Binary_Semaphore {
|
|
bool signaled;
|
|
Mutex mutex;
|
|
} Binary_Semaphore;
|
|
|
|
void binary_semaphore_init(Binary_Semaphore *sem, bool initial_state) {
|
|
sem->signaled = initial_state;
|
|
mutex_init(&sem->mutex);
|
|
}
|
|
|
|
void binary_semaphore_destroy(Binary_Semaphore *sem) {
|
|
mutex_destroy(&sem->mutex);
|
|
}
|
|
|
|
void binary_semaphore_wait(Binary_Semaphore *sem) {
|
|
mutex_acquire_or_wait(&sem->mutex);
|
|
while (!sem->signaled) {
|
|
mutex_release(&sem->mutex);
|
|
os_yield_thread();
|
|
mutex_acquire_or_wait(&sem->mutex);
|
|
}
|
|
sem->signaled = false;
|
|
mutex_release(&sem->mutex);
|
|
}
|
|
|
|
void binary_semaphore_signal(Binary_Semaphore *sem) {
|
|
mutex_acquire_or_wait(&sem->mutex);
|
|
sem->signaled = true;
|
|
mutex_release(&sem->mutex);
|
|
} |