Nucleus
Barry Object locking e8e484f (3 years, 3 months ago)
diff --git a/object/lock.c b/object/lock.c
new file mode 100644
index 0000000..e4c77c9
--- /dev/null
+++ b/object/lock.c
@@ -0,0 +1,61 @@
+/*
+ * This file implements spinlocks. It makes heavy use of GCC's atomic built-ins
+ * for syncronisation. The spinlocks have some simple mechanisms for preventing
+ * deadlocks. Each spinlock knowns which CPU/task is holding it, and can allow
+ * that CPU/task to acquire it multiple times and safely release it.
+ */
+
+#include <nucleus/object.h>
+#include <nucleus/task.h>
+#include <nucleus/panic.h>
+
+/* Check if already holding */
+static int
+holding(Spinlock *lock)
+{
+ if (current)
+ return (lock->locked && lock->owner == current);
+ return (lock->locked && lock->cpu == (CPUID + 1));
+}
+
+/* Initialise a lock */
+void
+init_lock(Spinlock *lock)
+{
+ lock->locked = 0;
+ lock->usage = 0;
+}
+
+/* Acquire a lock */
+void
+acquire(Spinlock *lock)
+{
+ /*
+ * Reference count the lock so it can be safely acquired by the same
+ * holder multiple times. This stops a lock from deadlocking itself.
+ */
+ __atomic_add_fetch(&lock->usage, 1, __ATOMIC_RELAXED);
+ if (holding(lock))
+ return;
+
+ while (__atomic_test_and_set(&lock->locked, __ATOMIC_ACQUIRE))
+ asm volatile("pause");
+
+ if (current)
+ lock->owner = current;
+ else
+ lock->cpu = CPUID + 1;
+}
+
+/* Release a lock */
+void
+release(Spinlock *lock)
+{
+ ASSERT(holding(lock));
+ if (__atomic_sub_fetch(&lock->usage, 1, __ATOMIC_RELAXED))
+ return;
+ __atomic_clear(&lock->locked, __ATOMIC_RELEASE);
+
+ lock->owner = NULL;
+ lock->cpu = 0;
+}
diff --git a/object/manager.c b/object/manager.c
index 301b6ca..a330d15 100644
--- a/object/manager.c
+++ b/object/manager.c
@@ -8,13 +8,16 @@
#include <nucleus/object.h>
+void acquire(Spinlock *lock);
+void release(Spinlock *lock);
+
/* Obtain a reference to an object */
void *
get(void *addr)
{
Object *obj = addr;
- obj->type->usage++;
- obj->usage++;
+ __atomic_add_fetch(&obj->type->usage, 1, __ATOMIC_RELAXED);
+ __atomic_add_fetch(&obj->usage, 1, __ATOMIC_RELAXED);
return addr;
}
@@ -23,10 +26,10 @@ void
put(void *addr)
{
Object *obj = addr;
- obj->type->usage--;
- if (--obj->usage)
+ __atomic_sub_fetch(&obj->type->usage, 1, __ATOMIC_RELAXED);
+ if (__atomic_sub_fetch(&obj->usage, 1, __ATOMIC_RELAXED))
return;
- obj->type->count--;
+ __atomic_sub_fetch(&obj->type->count, 1, __ATOMIC_RELAXED);
obj->type->delete(obj);
}
@@ -36,6 +39,22 @@ new(ObjectType *type)
{
Object *obj = type->new();
obj->type = type;
- type->count++;
+ __atomic_add_fetch(&type->count, 1, __ATOMIC_RELAXED);
return get(obj);
}
+
+/* Lock an object */
+void
+lock(void *addr)
+{
+ Object *obj = addr;
+ acquire(&obj->lock);
+}
+
+/* Unlock an object */
+void
+unlock(void *addr)
+{
+ Object *obj = addr;
+ release(&obj->lock);
+}