Index: kernel/generic/include/proc/thread.h
===================================================================
--- kernel/generic/include/proc/thread.h	(revision 2f7d77c6250db2dbaf4fc40c073fd148af44fd39)
+++ kernel/generic/include/proc/thread.h	(revision e768aea1e1c1b71c4e7d59520251bebbdc779f46)
@@ -112,4 +112,12 @@
 	/** If true, the thread can be interrupted from sleep. */
 	bool sleep_interruptible;
+
+	/**
+	 * If true, and this thread's sleep returns without a wakeup
+	 * (timed out or interrupted), waitq ignores the next wakeup.
+	 * This is necessary for futex to be able to handle those conditions.
+	 */
+	bool sleep_composable;
+
 	/** Wait queue in which this thread sleeps. */
 	waitq_t *sleep_queue;
Index: kernel/generic/include/synch/futex.h
===================================================================
--- kernel/generic/include/synch/futex.h	(revision 2f7d77c6250db2dbaf4fc40c073fd148af44fd39)
+++ kernel/generic/include/synch/futex.h	(revision e768aea1e1c1b71c4e7d59520251bebbdc779f46)
@@ -53,5 +53,5 @@
 
 extern void futex_init(void);
-extern sys_errno_t sys_futex_sleep(uintptr_t);
+extern sys_errno_t sys_futex_sleep(uintptr_t, uintptr_t);
 extern sys_errno_t sys_futex_wakeup(uintptr_t);
 
Index: kernel/generic/include/synch/waitq.h
===================================================================
--- kernel/generic/include/synch/waitq.h	(revision 2f7d77c6250db2dbaf4fc40c073fd148af44fd39)
+++ kernel/generic/include/synch/waitq.h	(revision e768aea1e1c1b71c4e7d59520251bebbdc779f46)
@@ -62,4 +62,7 @@
 	int missed_wakeups;
 
+	/** Number of wakeups that need to be ignored due to futex timeout. */
+	int ignore_wakeups;
+
 	/** List of sleeping threads for which there was no missed_wakeup. */
 	list_t sleepers;
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision 2f7d77c6250db2dbaf4fc40c073fd148af44fd39)
+++ kernel/generic/src/proc/thread.c	(revision e768aea1e1c1b71c4e7d59520251bebbdc779f46)
@@ -383,4 +383,5 @@
 	timeout_initialize(&thread->sleep_timeout);
 	thread->sleep_interruptible = false;
+	thread->sleep_composable = false;
 	thread->sleep_queue = NULL;
 	thread->timeout_pending = false;
Index: kernel/generic/src/synch/futex.c
===================================================================
--- kernel/generic/src/synch/futex.c	(revision 2f7d77c6250db2dbaf4fc40c073fd148af44fd39)
+++ kernel/generic/src/synch/futex.c	(revision e768aea1e1c1b71c4e7d59520251bebbdc779f46)
@@ -398,7 +398,10 @@
 }
 
-/** Sleep in futex wait queue.
- *
- * @param uaddr		Userspace address of the futex counter.
+/** Sleep in futex wait queue with a timeout.
+ *  If the sleep times out or is interrupted, the next wakeup is ignored.
+ *  The userspace portion of the call must handle this condition.
+ *
+ * @param uaddr	 	Userspace address of the futex counter.
+ * @param timeout	Maximum number of useconds to sleep. 0 means no limit.
  *
  * @return		If there is no physical mapping for uaddr ENOENT is
@@ -406,5 +409,5 @@
  *                      waitq_sleep_timeout().
  */
-sys_errno_t sys_futex_sleep(uintptr_t uaddr)
+sys_errno_t sys_futex_sleep(uintptr_t uaddr, uintptr_t timeout)
 {
 	futex_t *futex = get_futex(uaddr);
@@ -417,6 +420,6 @@
 #endif
 
-	errno_t rc = waitq_sleep_timeout(
-	    &futex->wq, 0, SYNCH_FLAGS_INTERRUPTIBLE, NULL);
+	errno_t rc = waitq_sleep_timeout(&futex->wq, timeout,
+	    SYNCH_FLAGS_INTERRUPTIBLE | SYNCH_FLAGS_FUTEX, NULL);
 
 #ifdef CONFIG_UDEBUG
Index: kernel/generic/src/synch/waitq.c
===================================================================
--- kernel/generic/src/synch/waitq.c	(revision 2f7d77c6250db2dbaf4fc40c073fd148af44fd39)
+++ kernel/generic/src/synch/waitq.c	(revision e768aea1e1c1b71c4e7d59520251bebbdc779f46)
@@ -57,4 +57,5 @@
 #include <adt/list.h>
 #include <arch/cycle.h>
+#include <mem.h>
 
 static void waitq_sleep_timed_out(void *);
@@ -71,7 +72,7 @@
 void waitq_initialize(waitq_t *wq)
 {
+	memsetb(wq, sizeof(*wq), 0);
 	irq_spinlock_initialize(&wq->lock, "wq.lock");
 	list_initialize(&wq->sleepers);
-	wq->missed_wakeups = 0;
 }
 
@@ -114,4 +115,6 @@
 		thread->saved_context = thread->sleep_timeout_context;
 		do_wakeup = true;
+		if (thread->sleep_composable)
+			wq->ignore_wakeups++;
 		thread->sleep_queue = NULL;
 		irq_spinlock_unlock(&wq->lock, false);
@@ -176,4 +179,6 @@
 		list_remove(&thread->wq_link);
 		thread->saved_context = thread->sleep_interruption_context;
+		if (thread->sleep_composable)
+			wq->ignore_wakeups++;
 		do_wakeup = true;
 		thread->sleep_queue = NULL;
@@ -393,4 +398,6 @@
 	 */
 	irq_spinlock_lock(&THREAD->lock, false);
+
+	THREAD->sleep_composable = (flags & SYNCH_FLAGS_FUTEX);
 
 	if (flags & SYNCH_FLAGS_INTERRUPTIBLE) {
@@ -538,4 +545,12 @@
 	assert(irq_spinlock_locked(&wq->lock));
 
+	if (wq->ignore_wakeups > 0) {
+		if (mode == WAKEUP_FIRST) {
+			wq->ignore_wakeups--;
+			return;
+		}
+		wq->ignore_wakeups = 0;
+	}
+
 loop:
 	if (list_empty(&wq->sleepers)) {
