Index: HelenOS.config
===================================================================
--- HelenOS.config	(revision 38e34273c4ac658e194702982ca3b3c5f85af939)
+++ HelenOS.config	(revision f6372be92e3eaec95762607dcf097844741b51b5)
@@ -385,4 +385,7 @@
 ! CONFIG_UBSAN_KERNEL (n/y)
 
+% Track owner for futexes in userspace.
+! CONFIG_DEBUG_FUTEX (y/n)
+
 % Deadlock detection support for spinlocks
 ! [CONFIG_DEBUG=y&CONFIG_SMP=y] CONFIG_DEBUG_SPINLOCK (y/n)
Index: uspace/lib/c/generic/fibril.c
===================================================================
--- uspace/lib/c/generic/fibril.c	(revision 38e34273c4ac658e194702982ca3b3c5f85af939)
+++ uspace/lib/c/generic/fibril.c	(revision f6372be92e3eaec95762607dcf097844741b51b5)
@@ -150,5 +150,5 @@
 	case FIBRIL_FROM_DEAD:
 		/* Make sure the async_futex is held. */
-		assert((atomic_signed_t) async_futex.val.count <= 0);
+		futex_assert_is_locked(&async_futex);
 
 		/* If we are going to manager and none exists, create it */
@@ -162,4 +162,7 @@
 		    fibril_t, link);
 
+		/* Bookkeeping. */
+		futex_give_to(&async_futex, dstf);
+
 		if (stype == FIBRIL_FROM_DEAD)
 			dstf->clean_after_me = srcf;
@@ -167,4 +170,6 @@
 	case FIBRIL_PREEMPT:
 	case FIBRIL_FROM_MANAGER:
+		futex_assert_is_not_locked(&async_futex);
+
 		if (list_empty(&ready_list)) {
 			futex_unlock(&fibril_futex);
@@ -196,4 +201,7 @@
 		break;
 	}
+
+	/* Bookkeeping. */
+	futex_give_to(&fibril_futex, dstf);
 
 	/* Swap to the next fibril. */
Index: uspace/lib/c/generic/futex.c
===================================================================
--- uspace/lib/c/generic/futex.c	(revision 38e34273c4ac658e194702982ca3b3c5f85af939)
+++ uspace/lib/c/generic/futex.c	(revision f6372be92e3eaec95762607dcf097844741b51b5)
@@ -34,5 +34,14 @@
 
 #include <futex.h>
+
+#include <assert.h>
 #include <atomic.h>
+#include <fibril.h>
+#include <io/kio.h>
+
+#include "private/fibril.h"
+
+//#define DPRINTF(...) kio_printf(__VA_ARGS__)
+#define DPRINTF(...) ((void)0)
 
 /** Initialize futex counter.
@@ -47,4 +56,88 @@
 }
 
+#ifdef CONFIG_DEBUG_FUTEX
+
+void __futex_assert_is_locked(futex_t *futex, const char *name)
+{
+	void *owner = __atomic_load_n(&futex->owner, __ATOMIC_RELAXED);
+	fibril_t *self = (fibril_t *) fibril_get_id();
+	if (owner != self) {
+		DPRINTF("Assertion failed: %s (%p) is not locked by fibril %p (instead locked by fibril %p).\n", name, futex, self, owner);
+	}
+	assert(owner == self);
+}
+
+void __futex_assert_is_not_locked(futex_t *futex, const char *name)
+{
+	void *owner = __atomic_load_n(&futex->owner, __ATOMIC_RELAXED);
+	fibril_t *self = (fibril_t *) fibril_get_id();
+	if (owner == self) {
+		DPRINTF("Assertion failed: %s (%p) is already locked by fibril %p.\n", name, futex, self);
+	}
+	assert(owner != self);
+}
+
+void __futex_lock(futex_t *futex, const char *name)
+{
+	/* We use relaxed atomics to avoid violating C11 memory model.
+	 * They should compile to regular load/stores, but simple assignments
+	 * would be UB by definition.
+	 */
+
+	fibril_t *self = (fibril_t *) fibril_get_id();
+	DPRINTF("Locking futex %s (%p) by fibril %p.\n", name, futex, self);
+	__futex_assert_is_not_locked(futex, name);
+	futex_down(futex);
+
+	void *prev_owner = __atomic_exchange_n(&futex->owner, self, __ATOMIC_RELAXED);
+	assert(prev_owner == NULL);
+
+	atomic_inc(&self->futex_locks);
+}
+
+void __futex_unlock(futex_t *futex, const char *name)
+{
+	fibril_t *self = (fibril_t *) fibril_get_id();
+	DPRINTF("Unlocking futex %s (%p) by fibril %p.\n", name, futex, self);
+	__futex_assert_is_locked(futex, name);
+	__atomic_store_n(&futex->owner, NULL, __ATOMIC_RELAXED);
+	atomic_dec(&self->futex_locks);
+	futex_up(futex);
+}
+
+bool __futex_trylock(futex_t *futex, const char *name)
+{
+	fibril_t *self = (fibril_t *) fibril_get_id();
+	bool success = futex_trydown(futex);
+	if (success) {
+		void *owner = __atomic_load_n(&futex->owner, __ATOMIC_RELAXED);
+		assert(owner == NULL);
+
+		__atomic_store_n(&futex->owner, self, __ATOMIC_RELAXED);
+
+		atomic_inc(&self->futex_locks);
+
+		DPRINTF("Trylock on futex %s (%p) by fibril %p succeeded.\n", name, futex, self);
+	} else {
+		DPRINTF("Trylock on futex %s (%p) by fibril %p failed.\n", name, futex, self);
+	}
+
+	return success;
+}
+
+void __futex_give_to(futex_t *futex, void *new_owner, const char *name)
+{
+	fibril_t *self = fibril_self();
+	fibril_t *no = new_owner;
+	DPRINTF("Passing futex %s (%p) from fibril %p to fibril %p.\n", name, futex, self, no);
+
+	__futex_assert_is_locked(futex, name);
+	atomic_dec(&self->futex_locks);
+	atomic_inc(&no->futex_locks);
+	__atomic_store_n(&futex->owner, new_owner, __ATOMIC_RELAXED);
+}
+
+#endif
+
 /** @}
  */
Index: uspace/lib/c/include/futex.h
===================================================================
--- uspace/lib/c/include/futex.h	(revision 38e34273c4ac658e194702982ca3b3c5f85af939)
+++ uspace/lib/c/include/futex.h	(revision f6372be92e3eaec95762607dcf097844741b51b5)
@@ -39,10 +39,36 @@
 #include <errno.h>
 #include <libc.h>
+#include <time.h>
 
 typedef struct futex {
 	atomic_t val;
+#ifdef CONFIG_DEBUG_FUTEX
+	_Atomic void *owner;
+#endif
 } futex_t;
 
 extern void futex_initialize(futex_t *futex, int value);
+
+#ifdef CONFIG_DEBUG_FUTEX
+
+#define FUTEX_INITIALIZE(val) {{ (val) }, NULL }
+#define FUTEX_INITIALIZER     FUTEX_INITIALIZE(1)
+
+void __futex_assert_is_locked(futex_t *, const char *);
+void __futex_assert_is_not_locked(futex_t *, const char *);
+void __futex_lock(futex_t *, const char *);
+void __futex_unlock(futex_t *, const char *);
+bool __futex_trylock(futex_t *, const char *);
+void __futex_give_to(futex_t *, void *, const char *);
+
+#define futex_lock(futex) __futex_lock((futex), #futex)
+#define futex_unlock(futex) __futex_unlock((futex), #futex)
+#define futex_trylock(futex) __futex_trylock((futex), #futex)
+
+#define futex_give_to(futex, new_owner) __futex_give_to((futex), (new_owner), #futex)
+#define futex_assert_is_locked(futex) __futex_assert_is_locked((futex), #futex)
+#define futex_assert_is_not_locked(futex) __futex_assert_is_not_locked((futex), #futex)
+
+#else
 
 #define FUTEX_INITIALIZE(val) {{ (val) }}
@@ -52,4 +78,10 @@
 #define futex_trylock(fut)  futex_trydown((fut))
 #define futex_unlock(fut)   (void) futex_up((fut))
+
+#define futex_give_to(fut, owner) ((void)0)
+#define futex_assert_is_locked(fut) assert((atomic_signed_t) (fut)->val.count <= 0)
+#define futex_assert_is_not_locked(fut) ((void)0)
+
+#endif
 
 /** Try to down the futex.
