aboutsummaryrefslogtreecommitdiff
path: root/libgo/runtime/thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/runtime/thread.c')
-rw-r--r--libgo/runtime/thread.c61
1 files changed, 48 insertions, 13 deletions
diff --git a/libgo/runtime/thread.c b/libgo/runtime/thread.c
index 5651217..99a0d68 100644
--- a/libgo/runtime/thread.c
+++ b/libgo/runtime/thread.c
@@ -7,32 +7,67 @@
void
runtime_initlock(Lock *l)
{
- if(pthread_mutex_init(&l->mutex, NULL) != 0)
- runtime_throw("pthread_mutex_init failed");
+ l->key = 0;
+ if(sem_init(&l->sem, 0, 0) != 0)
+ runtime_throw("sem_init failed");
+}
+
+static uint32
+runtime_xadd(uint32 volatile *val, int32 delta)
+{
+ uint32 oval, nval;
+
+ for(;;){
+ oval = *val;
+ nval = oval + delta;
+ if(runtime_cas(val, oval, nval))
+ return nval;
+ }
+}
+
+// noinline so that runtime_lock doesn't have to split the stack.
+static void runtime_lock_full(Lock *l) __attribute__ ((noinline));
+
+static void
+runtime_lock_full(Lock *l)
+{
+ if(sem_wait(&l->sem) != 0)
+ runtime_throw("sem_wait failed");
}
void
runtime_lock(Lock *l)
{
- if(pthread_mutex_lock(&l->mutex) != 0)
- runtime_throw("lock failed");
+ if(m->locks < 0)
+ runtime_throw("lock count");
+ m->locks++;
+
+ if(runtime_xadd(&l->key, 1) > 1) // someone else has it; wait
+ runtime_lock_full(l);
}
-void
-runtime_unlock(Lock *l)
+static void runtime_unlock_full(Lock *l) __attribute__ ((noinline));
+
+static void
+runtime_unlock_full(Lock *l)
{
- if(pthread_mutex_unlock(&l->mutex) != 0)
- runtime_throw("unlock failed");
+ if(sem_post(&l->sem) != 0)
+ runtime_throw("sem_post failed");
}
void
-runtime_destroylock(Lock *l)
+runtime_unlock(Lock *l)
{
- pthread_mutex_destroy(&l->mutex);
+ m->locks--;
+ if(m->locks < 0)
+ runtime_throw("lock count");
+
+ if(runtime_xadd(&l->key, -1) > 0) // someone else is waiting
+ runtime_unlock_full(l);
}
-bool
-runtime_trylock(Lock *l)
+void
+runtime_destroylock(Lock *l)
{
- return pthread_mutex_trylock(&l->mutex) == 0;
+ sem_destroy(&l->sem);
}