#include "sync.h"
#if INTERFACE
#include <stdint.h>
#include <stddef.h>
typedef volatile uint32_t spin_t;
struct monitor_t {
interrupt_monitor_t lock[1];
thread_t * volatile owner;
volatile int count;
};
struct rwlock_t {
monitor_t lock[1];
int readcount;
thread_t * volatile writer;
};
struct interrupt_monitor_t {
spin_t spin;
thread_t * volatile owner;
thread_t * volatile waiting;
timer_event_t timer[1];
};
#define INIT_ONCE() \
do { \
static int inited = 0; \
if (inited) { \
return; \
} \
inited = 1; \
} while(0)
typedef monitor_t mutex_t;
#define AUTOLOCK_CONCAT(a, b) a ## b
#define AUTOLOCK_VAR(line) AUTOLOCK_CONCAT(s,line)
#define SPIN_AUTOLOCK(lock) spin_t AUTOLOCK_VAR(__LINE__) = 0; while((AUTOLOCK_VAR(__LINE__) =spin_autolock(lock, AUTOLOCK_VAR(__LINE__) )))
#if 0
#define MUTEX_AUTOLOCK(lock) int AUTOLOCK_VAR(__LINE__) = 0; while((AUTOLOCK_VAR(__LINE__) =mutex_autolock(lock, AUTOLOCK_VAR(__LINE__) )))
#endif
#define MUTEX_AUTOLOCK(lock) MONITOR_AUTOLOCK(lock)
#define MONITOR_AUTOLOCK(lock) int AUTOLOCK_VAR(__LINE__) = 0; while((AUTOLOCK_VAR(__LINE__) =monitor_autolock(lock, AUTOLOCK_VAR(__LINE__) )))
#define INTERRUPT_MONITOR_AUTOLOCK(lock) int AUTOLOCK_VAR(__LINE__) = 0; while((AUTOLOCK_VAR(__LINE__) =interrupt_monitor_autolock(lock, AUTOLOCK_VAR(__LINE__) )))
#define READER_AUTOLOCK(lock) int AUTOLOCK_VAR(__LINE__) = 0; while((AUTOLOCK_VAR(__LINE__) =rwlock_autolock(lock, AUTOLOCK_VAR(__LINE__), 0)))
#define WRITER_AUTOLOCK(lock) int AUTOLOCK_VAR(__LINE__) = 0; while((AUTOLOCK_VAR(__LINE__) =rwlock_autolock(lock, AUTOLOCK_VAR(__LINE__), 1)))
#endif
exception_def TimeoutException = { "TimeoutException", &Exception };
#if 0
static void mutex_mark(void * p)
{
mutex_t * lock = p;
slab_gc_mark(lock->owner);
slab_gc_mark(lock->waiting);
}
static slab_type_t mutexes[1] = {SLAB_TYPE(sizeof(mutex_t), mutex_mark, 0)};
#endif
#if 0
static void monitor_mark(void * p)
{
monitor_t * lock = p;
slab_gc_mark(lock->lock->waiting);
slab_gc_mark(lock->owner);
}
#endif
static slab_type_t monitors[1] = {SLAB_TYPE(sizeof(monitor_t), 0, 0)};
static GCROOT map_t * locktable;
static mutex_t locktablelock;
static int contended;
int spin_trylock(spin_t * l)
{
return arch_spin_trylock(l);
}
void spin_unlock(spin_t * l)
{
arch_spin_unlock(l);
}
void spin_lock(spin_t * l)
{
arch_spin_lock(l);
}
#if 0
int mutex_autolock(mutex_t * lock, int state)
{
if (state) {
mutex_unlock(lock);
state = 0;
} else {
mutex_lock(lock);
state = 1;
}
return state;
}
#endif
int spin_autolock(spin_t * lock, int state)
{
if (state) {
spin_unlock(lock);
state = 0;
} else {
spin_lock(lock);
state = 1;
}
return state;
}
int monitor_autolock(monitor_t * lock, int state)
{
if (state) {
monitor_leave(lock);
state = 0;
} else {
monitor_enter(lock);
state = 1;
}
return state;
}
int interrupt_monitor_autolock(interrupt_monitor_t * lock, int state)
{
if (state) {
interrupt_monitor_leave(lock);
state = 0;
} else {
interrupt_monitor_enter(lock);
state = 1;
}
return state;
}
int rwlock_autolock(rwlock_t * lock, int state, int write)
{
if (state) {
rwlock_unlock(lock);
state = 0;
} else {
if (write) {
rwlock_write(lock);
} else {
rwlock_read(lock);
}
state = 1;
}
return state;
}
#if 0
static void thread_lock_signal(mutex_t * lock)
{
thread_t * resume = lock->waiting;
if (resume) {
LIST_DELETE(lock->waiting, resume);
thread_resume(resume);
}
}
static void thread_lock_wait(mutex_t * lock)
{
lock->waiting = thread_queue(lock->waiting, 0, THREAD_SLEEPING);
/* thread_lock_signal(lock); */
spin_unlock(&lock->spin);
thread_schedule();
spin_lock(&lock->spin);
}
mutex_t * mutex_create()
{
return slab_calloc(mutexes);
}
#endif
void mutex_lock(mutex_t * lock)
{
monitor_enter(lock);
}
void mutex_unlock(mutex_t * lock)
{
monitor_leave(lock);
}
monitor_t * monitor_create()
{
return slab_calloc(monitors);
}
void monitor_enter(monitor_t * monitor)
{
INTERRUPT_MONITOR_AUTOLOCK(monitor->lock) {
thread_t * thread = arch_get_thread();
while(monitor->owner && thread != monitor->owner) {
/* Locked by someone else */
interrupt_monitor_wait(monitor->lock);
}
monitor->owner = thread;
monitor->count++;
}
assert(monitor->owner == arch_get_thread());
}
void monitor_leave(monitor_t * monitor)
{
assert(monitor->owner == arch_get_thread());
INTERRUPT_MONITOR_AUTOLOCK(monitor->lock) {
monitor->count--;
if (0 == monitor->count) {
monitor->owner = 0;
interrupt_monitor_signal(monitor->lock);
}
}
}
void monitor_wait_timeout(monitor_t * monitor, timerspec_t timeout)
{
assert(monitor->owner == arch_get_thread());
INTERRUPT_MONITOR_AUTOLOCK(monitor->lock) {
int count = monitor->count;
monitor->count = 0;
monitor->owner = 0;
interrupt_monitor_wait_timeout(monitor->lock, timeout);
monitor->owner = arch_get_thread();
monitor->count = count;
}
}
void monitor_wait(monitor_t * monitor)
{
monitor_wait_timeout(monitor, 0);
}
void monitor_signal(monitor_t * monitor)
{
assert(monitor->owner == arch_get_thread());
INTERRUPT_MONITOR_AUTOLOCK(monitor->lock) {
interrupt_monitor_signal(monitor->lock);
}
}
void monitor_broadcast(monitor_t * monitor)
{
assert(monitor->owner == arch_get_thread());
INTERRUPT_MONITOR_AUTOLOCK(monitor->lock) {
interrupt_monitor_broadcast(monitor->lock);
}
}
static int interrupt_monitor_deadlock_visit_monitor(map_t * visited, interrupt_monitor_t * monitor);
static int interrupt_monitor_deadlock_visit_thread(map_t * visited, thread_t * thread)
{
if (thread == map_putpp(visited, thread, thread)) {
return 1;
}
return interrupt_monitor_deadlock_visit_monitor(visited, thread->waitingfor);
}
static int interrupt_monitor_deadlock_visit_monitor(map_t * visited, interrupt_monitor_t * monitor)
{
if (monitor == map_putpp(visited, monitor, monitor)) {
return 1;
}
thread_t * thread = monitor->waiting;
while(thread) {
if (interrupt_monitor_deadlock_visit_thread(visited, thread)) {
return 1;
}
LIST_NEXT(monitor->waiting, thread);
}
return interrupt_monitor_deadlock_visit_thread(visited, monitor->owner);
}
void interrupt_monitor_enter(interrupt_monitor_t * monitor)
{
interrupt_monitor_t * waitingfor = arch_get_thread()->waitingfor;
arch_get_thread()->waitingfor = monitor;
#if 0
spin_lock(&monitor->spin);
#else
int attempts = 0;
while(0 == spin_trylock(&monitor->spin)) {
attempts++;
if (0 == (attempts & 0xffff)) {
/* Try to detect deadlock */
map_t * visited = treap_new(NULL);
if (interrupt_monitor_deadlock_visit_thread(visited, arch_get_thread())) {
thread_yield();
}
}
}
#endif
arch_get_thread()->waitingfor = waitingfor;
monitor->owner = arch_get_thread();
assert(monitor->owner == arch_get_thread());
}
void interrupt_monitor_leave(interrupt_monitor_t * monitor)
{
assert(monitor->owner == arch_get_thread());
monitor->owner = 0;
spin_unlock(&monitor->spin);
}
typedef struct interrupt_monitor_wait_timeout_t {
interrupt_monitor_t * monitor;
thread_t * thread;
} interrupt_monitor_wait_timeout_t;
static void interrupt_monitor_wait_timeout_thread(void * p)
{
interrupt_monitor_wait_timeout_t * timeout = p;
INTERRUPT_MONITOR_AUTOLOCK(timeout->monitor) {
thread_t * thread = timeout->monitor->waiting;
while(thread) {
if (thread == timeout->thread) {
LIST_DELETE(timeout->monitor->waiting, thread);
thread_interrupt(thread);
thread_resume(thread);
thread = 0;
} else {
LIST_NEXT(timeout->monitor->waiting, thread);
}
}
}
}
void interrupt_monitor_wait_timeout(interrupt_monitor_t * monitor, timerspec_t timeout)
{
assert(monitor->owner == arch_get_thread());
interrupt_monitor_wait_timeout_t timeout_thread = { monitor, arch_get_thread() };
timer_event_t * timer;
if (timeout) {
timer_set(monitor->timer, timeout, interrupt_monitor_wait_timeout_thread, &timeout_thread);
}
monitor->waiting = thread_queue(monitor->waiting, 0, THREAD_SLEEPING);
interrupt_monitor_leave(monitor);
thread_schedule();
/* Check for timeout */
if (timeout) {
timer_delete(monitor->timer);
if (thread_interrupted()) {
KTHROW(TimeoutException, "Timeout");
}
}
interrupt_monitor_enter(monitor);
}
void interrupt_monitor_wait(interrupt_monitor_t * monitor)
{
assert(monitor->owner == arch_get_thread());
interrupt_monitor_wait_timeout(monitor, 0);
}
void interrupt_monitor_signal(interrupt_monitor_t * monitor)
{
assert(monitor->owner == arch_get_thread());
thread_t * resume = monitor->waiting;
if (resume) {
LIST_DELETE(monitor->waiting, resume);
thread_resume(resume);
}
}
void interrupt_monitor_broadcast(interrupt_monitor_t * monitor)
{
while(monitor->waiting) {
interrupt_monitor_signal(monitor);
}
}
static interrupt_monitor_t locks[32] = {0};
static void interrupt_monitor_irq_trigger(int irq)
{
INTERRUPT_MONITOR_AUTOLOCK(locks+irq) {
interrupt_monitor_broadcast(locks+irq);
}
}
#if 0
static monitor_t * thread_monitor_get(void * p)
{
mutex_lock(&locktablelock);
if (0 == locktable) {
locktable = tree_new(0, TREE_SPLAY);
}
monitor_t * lock = map_getpp(locktable, p);
if (0 == lock) {
lock = slab_calloc(monitors);
map_putpp(locktable, p, lock);
}
mutex_unlock(&locktablelock);
return lock;
}
static int thread_trylock_internal(mutex_t * lock)
{
if (0 == lock->count) {
/* Not in use, mark it as used and locked */
lock->count = 1;
lock->owner = arch_get_thread();
spin_unlock(&lock->spin);
return 1;
} else if (lock->owner == arch_get_thread()) {
/* Recursive lock, increase count */
lock->count++;
spin_unlock(&lock->spin);
return 1;
}
return 0;
}
#endif
void rwlock_read(rwlock_t * lock)
{
MONITOR_AUTOLOCK(lock->lock) {
if (arch_get_thread() == lock->writer) {
/* We already have a write lock, give it up */
lock->writer = 0;
} else {
while(lock->writer) {
monitor_wait(lock->lock);
}
}
lock->readcount++;
}
}
void rwlock_write(rwlock_t * lock)
{
thread_t * thread = arch_get_thread();
MONITOR_AUTOLOCK(lock->lock) {
/* Not already a reader, wait until no writer or readers */
while(lock->readcount || lock->writer) {
monitor_wait(lock->lock);
}
lock->writer = thread;
}
}
void rwlock_escalate(rwlock_t * lock)
{
thread_t * thread = arch_get_thread();
MONITOR_AUTOLOCK(lock->lock) {
while(lock->readcount > 1 || lock->writer) {
monitor_wait(lock->lock);
}
lock->readcount = 0;
lock->writer = thread;
}
}
void rwlock_unlock(rwlock_t * lock)
{
thread_t * thread = arch_get_thread();
MONITOR_AUTOLOCK(lock->lock) {
if (thread == lock->writer) {
lock->writer = 0;
} else {
lock->readcount--;
}
monitor_broadcast(lock->lock);
}
}
#if 0
int thread_tryplock(void * p)
{
mutex_t * lock = thread_monitor_get(p);
return thread_trylock_internal(lock);
}
void thread_lock(void * p)
{
monitor_t * lock = thread_monitor_get(p);
monitor_enter(lock);
}
void thread_unlock(void *p)
{
monitor_t * lock = thread_monitor_get(p);
monitor_leave(lock);
}
void thread_signal(void *p)
{
monitor_t * lock = thread_monitor_get(p);
monitor_signal(lock);
}
void thread_broadcast(void *p)
{
monitor_t * lock = thread_monitor_get(p);
monitor_broadcast(lock);
}
void thread_wait(void *p)
{
monitor_t * lock = thread_monitor_get(p);
monitor_wait(lock);
}
#endif
static void sync_deadlock_test()
{
static interrupt_monitor_t test[2] = {0};
thread_t * thread = thread_fork();
if (thread) {
INTERRUPT_MONITOR_AUTOLOCK(test) {
interrupt_monitor_wait(test);
INTERRUPT_MONITOR_AUTOLOCK(test+1) {
}
}
} else {
INTERRUPT_MONITOR_AUTOLOCK(test+1) {
INTERRUPT_MONITOR_AUTOLOCK(test) {
interrupt_monitor_signal(test);
thread_yield();
}
}
thread_exit(NULL);
}
thread_join(thread);
}
static void monitor_test()
{
static monitor_t test[1];
static int running = 1;
int num = 1;
thread_t * thread = thread_fork();
if (thread) {
for(int i=0; i<1000000; i++) {
MONITOR_AUTOLOCK(test) {
monitor_signal(test);
}
thread_yield();
}
MONITOR_AUTOLOCK(test) {
running = 0;
monitor_signal(test);
}
thread_yield();
thread_join(thread);
} else {
MONITOR_AUTOLOCK(test) {
while(running) {
monitor_wait(test);
}
}
}
}
void sync_test()
{
sync_deadlock_test();
monitor_test();
}