diff --git a/mutex.cc b/mutex.cc
index 639ce37a47f9bd383e6de205e75a1ba4a93230be..89f02c939b23720db440beff8f31be62e845c84b 100644
--- a/mutex.cc
+++ b/mutex.cc
@@ -2,16 +2,34 @@
 
 void mutex::lock()
 {
-    // dummy - no threads yet
+    // FIXME: use atomics
+    if (!_locked) {
+        _locked = true;
+        return;
+    } else {
+        auto me = sched::thread::current();
+        _waiters.push_back(me);
+        sched::thread::wait_until([=] {
+            return !_locked && _waiters.front() == me;
+        });
+        _waiters.pop_front();
+    }
 }
 
 bool mutex::try_lock()
 {
-    // dummy - no threads yet
-    return true;
+    if (_locked) {
+        return false;
+    } else {
+        _locked = true;
+        return true;
+    }
 }
 
 void mutex::unlock()
 {
-    // dummy - no threads yet
+    _locked = false;
+    if (!_waiters.empty()) {
+        _waiters.front()->wake();
+    }
 }
diff --git a/mutex.hh b/mutex.hh
index 4b3a1ab9d08949956ba450382cb41433bef19763..e74eed244f53b14a6e8f1e10a093e2ff4d7b51e3 100644
--- a/mutex.hh
+++ b/mutex.hh
@@ -2,12 +2,17 @@
 #define MUTEX_HH
 
 #include <mutex>
+#include <list>
+#include "sched.hh"
 
 class mutex {
 public:
     void lock();
     bool try_lock();
     void unlock();
+private:
+    bool _locked;
+    std::list<sched::thread*> _waiters;
 };
 
 template <class Lock, class Func>