summaryrefslogtreecommitdiff
path: root/kern/mutex_i.h
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2013-04-14 18:29:12 +0200
committerRichard Braun <rbraun@sceen.net>2013-04-14 18:29:12 +0200
commit04e1818f60ad8f90ea502f2f6c6ea61e6e61644c (patch)
tree51979cf0fdea679b868b044e386e92473295bbc2 /kern/mutex_i.h
parent909c423347085774a3fc7f8021ce765465cc92c8 (diff)
kern/{condition,mutex}: refactor common code
The condition module intrusively uses mutexes. Augment the interface of the mutex module so that mutexes and conditions share common code. As a side effect, the implementation should have gained in clarity.
Diffstat (limited to 'kern/mutex_i.h')
-rw-r--r--kern/mutex_i.h115
1 files changed, 115 insertions, 0 deletions
diff --git a/kern/mutex_i.h b/kern/mutex_i.h
new file mode 100644
index 00000000..2fa5cb42
--- /dev/null
+++ b/kern/mutex_i.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2013 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _KERN_MUTEX_I_H
+#define _KERN_MUTEX_I_H
+
+#include <kern/assert.h>
+#include <kern/list.h>
+#include <kern/spinlock.h>
+#include <kern/thread.h>
+#include <machine/atomic.h>
+
+#define MUTEX_UNLOCKED 0
+#define MUTEX_LOCKED 1
+#define MUTEX_CONTENDED 2
+
+struct mutex_waiter {
+ struct list node;
+ struct thread *thread;
+};
+
+struct mutex {
+ unsigned long state;
+ struct spinlock lock;
+ struct list waiters;
+};
+
+void mutex_lock_slow(struct mutex *mutex);
+
+void mutex_unlock_slow(struct mutex *mutex);
+
+static inline unsigned long
+mutex_tryacquire(struct mutex *mutex)
+{
+ return atomic_cas(&mutex->state, MUTEX_UNLOCKED, MUTEX_LOCKED);
+}
+
+static inline unsigned long
+mutex_tryacquire_slow(struct mutex *mutex)
+{
+ return atomic_swap(&mutex->state, MUTEX_CONTENDED);
+}
+
+static inline unsigned long
+mutex_release(struct mutex *mutex)
+{
+ unsigned long state;
+
+ state = atomic_swap(&mutex->state, MUTEX_UNLOCKED);
+ assert((state == MUTEX_LOCKED) || (state == MUTEX_CONTENDED));
+ return state;
+}
+
+static inline void
+mutex_queue(struct mutex *mutex, struct mutex_waiter *waiter)
+{
+ list_insert_tail(&mutex->waiters, &waiter->node);
+}
+
+static inline void
+mutex_queue_list(struct mutex *mutex, struct list *waiters)
+{
+ list_concat(&mutex->waiters, waiters);
+}
+
+static inline void
+mutex_wait(struct mutex *mutex, struct mutex_waiter *waiter)
+{
+ unsigned long state;
+
+ do {
+ thread_sleep(&mutex->lock);
+ state = mutex_tryacquire_slow(mutex);
+ } while (state != MUTEX_UNLOCKED);
+
+ list_remove(&waiter->node);
+}
+
+static inline void
+mutex_signal(struct mutex *mutex)
+{
+ struct mutex_waiter *waiter;
+
+ if (!list_empty(&mutex->waiters)) {
+ waiter = list_first_entry(&mutex->waiters, struct mutex_waiter, node);
+ thread_wakeup(waiter->thread);
+ }
+}
+
+static inline void
+mutex_trydowngrade(struct mutex *mutex)
+{
+ if (list_empty(&mutex->waiters)) {
+ unsigned long state;
+
+ state = atomic_swap(&mutex->state, MUTEX_LOCKED);
+ assert(state == MUTEX_CONTENDED);
+ }
+}
+
+#endif /* _KERN_MUTEX_I_H */