Eigenstate: myrddin-dev mailing list

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 2/4] Fix futex timeouts and handle futex error codes.


---
 lib/sys/sys+freebsd-x64.myr                   |  9 ++-
 lib/sys/sys+linux-x64.myr                     |  6 +-
 lib/thread/condvar+freebsd.myr                | 15 ++--
 lib/thread/condvar+linux.myr                  | 27 +++----
 lib/thread/condvar+openbsd:6.2.myr            | 27 +++----
 lib/thread/condvar+osx.myr                    | 15 ++--
 lib/thread/condvar.myr                        | 36 ++++-----
 lib/thread/futex+freebsd.myr                  | 56 +++++++++-----
 lib/thread/futex+linux.myr                    | 59 ++++++++++++---
 lib/thread/futex+openbsd:6.2.myr              | 66 ++++++++++++++--
 lib/thread/futex+osx.myr                      | 75 ++++++++++++-------
 lib/thread/mutex+futex.myr                    | 11 ++-
 lib/thread/mutex+plan9.myr                    |  2 -
 lib/thread/mutex.myr                          |  2 -
 lib/thread/sem+futex.myr                      |  3 +-
 lib/thread/sem+plan9.myr                      |  1 -
 lib/thread/waitgrp+futex.myr                  |  3 +-
 support/syscall-gen/specials+freebsd-x64.frag |  1 +
 support/syscall-gen/specials+linux-x64.frag   |  4 +-
 support/syscall-gen/types+freebsd-x64.frag    |  7 ++
 support/syscall-gen/types+linux-x64.frag      |  2 +
 21 files changed, 268 insertions(+), 159 deletions(-)

diff --git a/lib/sys/sys+freebsd-x64.myr b/lib/sys/sys+freebsd-x64.myr
index e1cfb692..b89e99b9 100644
--- a/lib/sys/sys+freebsd-x64.myr
+++ b/lib/sys/sys+freebsd-x64.myr
@@ -582,10 +582,10 @@ pkg sys =
 	;;
 	
 	const Umtxabstime = 1
-	type _umtx_time = struct
-		_timeout	: timespec
-		_flags		: uint32
-		_clockid	: uint32
+	type umtx_time = struct
+		timeout	: timespec
+		flags	: uint32
+		clockid	: uint32
 	;;
 	
 	/* open options */
@@ -1192,6 +1192,7 @@ pkg sys =
 		const getsockopt	: (sock : fd, lev : sockproto, opt : sockopt, val : void#, len : size# -> int)
 		const munmap	: (addr:byte#, len:size -> int64)
 		const mmap	: (addr:byte#, len:size, prot:mprot, flags:mopt, fd:fd, off:off -> byte#)
+		const clockid		: (clk : clock -> uint64)
 		const clock_getres	: (clk : clock, ts : timespec# -> int32)
 		const clock_gettime	: (clk : clock, ts : timespec# -> int32)
 		const clock_settime	: (clk : clock, ts : timespec# -> int32)
diff --git a/lib/sys/sys+linux-x64.myr b/lib/sys/sys+linux-x64.myr
index 1cebb123..61bb8b2d 100644
--- a/lib/sys/sys+linux-x64.myr
+++ b/lib/sys/sys+linux-x64.myr
@@ -563,6 +563,8 @@ pkg sys =
 	const Futexpriv	: futexop = 128
 	const Futexclockrt	: futexop = 256
 	
+	const Futexbitsetmatchany : int32 = -1
+	
 	/* poll events : posix */
 	const Pollin	: pollevt = 0x001	/* There is data to read.  */
 	const Pollpri	: pollevt = 0x002	/* There is urgent data to read.  */
@@ -1273,7 +1275,7 @@ pkg sys =
 	const dup	: (fd : fd -> fd)
 	const dup2	: (src : fd, dst : fd -> fd)
 	const futex	: (uaddr : int32#, op : futexop, val : int32, \
-	timeout : timespec#, uaddr2 : int32#, val3 : int32 -> int64)
+		timeout : timespec#, uaddr2 : int32#, val3 : int32 -> int)
 	const semctl	:  (semid : int, semnum : int, cmd : int, arg : void# -> int)
 	const epollcreate	: (flg : epollflags	-> fd)	/* actually epoll_create1 */
 	const epollctl	: (epfd : fd, op : int, fd : fd, evt : epollevt# -> int)
@@ -1692,7 +1694,7 @@ pkg sys =
 	
 	/* threading */
 	const futex	= {uaddr, op, val, timeout, uaddr2, val3
-		-> syscall(Sysfutex, a(uaddr), a(op), a(val), a(timeout), a(uaddr2), a(val3))
+		-> (syscall(Sysfutex, a(uaddr), a(op), a(val), a(timeout), a(uaddr2), a(val3)) : int)
 	}
 	const semctl	= {semid, semnum, cmd, arg
 		-> (syscall(Syssemctl, a(semnum), a(cmd), a(arg)) : int)
diff --git a/lib/thread/condvar+freebsd.myr b/lib/thread/condvar+freebsd.myr
index 002757ae..7b2e77c3 100644
--- a/lib/thread/condvar+freebsd.myr
+++ b/lib/thread/condvar+freebsd.myr
@@ -1,7 +1,6 @@
 use std
 
 use "atomic"
-use "common"
 use "mutex"
 use "futex"
 
@@ -22,19 +21,17 @@ const mkcond = {mtx
 }
 
 const condwait = {cond
-	var seq
-	var mtx
+	var mtx = cond._mtx
+	var seq = xget(&cond._seq)
 
-	mtx = cond._mtx
-	seq = cond._seq
 	mtxunlock(mtx)
+	ftxwait(&cond._seq, seq, -1)
 
 	/*
-	FIXME?: `ftxwait` can be interrupted but `condwait` should always be
-	done in a loop anyway.
+	In the event of a broadcast, we aren't requeueing waiters from the
+	condvar's futex to the mutex's futex so we can just reacquire the mutex
+	normally.
 	*/
-	ftxwait(&cond._seq, seq, Zptr)
-
 	mtxlock(mtx)
 }
 
diff --git a/lib/thread/condvar+linux.myr b/lib/thread/condvar+linux.myr
index e1a9e100..8d3f417b 100644
--- a/lib/thread/condvar+linux.myr
+++ b/lib/thread/condvar+linux.myr
@@ -3,12 +3,13 @@ use sys
 
 use "atomic"
 use "common"
+use "futex"
 use "mutex"
 
 pkg thread =
 	type cond = struct
 		_mtx	: mutex#
-		_seq	: int32
+		_seq	: ftxtag
 	;;
 
 	const mkcond	: (mtx : mutex# -> cond)
@@ -22,30 +23,24 @@ const mkcond = {mtx
 }
 
 const condwait = {cond
-	var seq
-	var mtx
+	var mtx = cond._mtx
+	var seq = xget(&cond._seq)
 
-	mtx = cond._mtx
-	seq = cond._seq
 	mtxunlock(mtx)
+	ftxwait(&cond._seq, seq, -1)
 
 	/*
-	FIXME?: `futex` can be interrupted but `condwait` should always be done
-	in a loop anyway.
-	*/
-	sys.futex(&cond._seq, sys.Futexwait | sys.Futexpriv, seq, Zptr, Zptr, 0)
-
-	/*
-	We need to atomically set the mutex to contended. This allows us to
-	pass responsibility for waking up the potential other waiters on to the
-	unlocker of the mutex.
+	In the event of a broadcast, we need to atomically set the mutex to
+	contended. This allows us to pass responsibility for waking up the
+	potential other waiters from the requeue operation on to the unlocker
+	of the mutex.
 	*/
 	mtxcontended(mtx)
 }
 
 const condsignal = {cond : cond#
 	xadd(&cond._seq, 1)
-	sys.futex(&cond._seq, sys.Futexwake | sys.Futexpriv, 1, Zptr, Zptr, 0)
+	ftxwake(&cond._seq)
 }
 
 const condbroadcast = {cond : cond#
@@ -55,7 +50,7 @@ const condbroadcast = {cond : cond#
 	used for the number of threads to move, and is not ignored when
 	requeueing
 	*/
-	sys.futex(&cond._seq, sys.Futexrequeue | sys.Futexpriv,
+	sys.futex((&cond._seq : int32#), sys.Futexrequeue | sys.Futexpriv,
 		1, (0x7fffffff : sys.timespec#),
 		(&cond._mtx._state : int32#), 0)
 }
diff --git a/lib/thread/condvar+openbsd:6.2.myr b/lib/thread/condvar+openbsd:6.2.myr
index 29dbb35f..345a3b17 100644
--- a/lib/thread/condvar+openbsd:6.2.myr
+++ b/lib/thread/condvar+openbsd:6.2.myr
@@ -3,12 +3,13 @@ use sys
 
 use "atomic"
 use "common"
+use "futex"
 use "mutex"
 
 pkg thread =
 	type cond = struct
 		_mtx	: mutex#
-		_seq	: uint32
+		_seq	: ftxtag
 	;;
 
 	const mkcond	: (mtx : mutex# -> cond)
@@ -22,35 +23,29 @@ const mkcond = {mtx
 }
 
 const condwait = {cond
-	var seq : uint32
-	var mtx
+	var mtx = cond._mtx
+	var seq = xget(&cond._seq)
 
-	mtx = cond._mtx
-	seq = cond._seq
 	mtxunlock(mtx)
+	ftxwait(&cond._seq, seq, -1)
 
 	/*
-	FIXME?: `futex` can be interrupted but `condwait` should always be done
-	in a loop anyway.
-	*/
-	sys.futex(&cond._seq, sys.Futexwait, (seq : int), Zptr, Zptr)
-
-	/*
-	We need to atomically set the mutex to contended. This allows us to
-	pass responsibility for waking up the potential other waiters on to the
-	unlocker of the mutex.
+	In the event of a broadcast, we need to atomically set the mutex to
+	contended. This allows us to pass responsibility for waking up the
+	potential other waiters from the requeue operation on to the unlocker
+	of the mutex.
 	*/
 	mtxcontended(mtx)
 }
 
 const condsignal = {cond : cond#
 	xadd(&cond._seq, 1)
-	sys.futex(&cond._seq, sys.Futexwake, 1, Zptr, Zptr)
+	ftxwake(&cond._seq)
 }
 
 const condbroadcast = {cond : cond#
 	xadd(&cond._seq, 1)
-	sys.futex(&cond._seq, sys.Futexrequeue, 1,
+	sys.futex((&cond._seq : uint32#), sys.Futexrequeue, 1,
 		(0x7fffffff : sys.timespec#),
 		(&cond._mtx._state : uint32#))
 }
diff --git a/lib/thread/condvar+osx.myr b/lib/thread/condvar+osx.myr
index d74c321f..0f1b1ff4 100644
--- a/lib/thread/condvar+osx.myr
+++ b/lib/thread/condvar+osx.myr
@@ -1,7 +1,6 @@
 use std
 
 use "atomic"
-use "common"
 use "mutex"
 use "futex"
 
@@ -22,19 +21,17 @@ const mkcond = {mtx
 }
 
 const condwait = {cond
-	var seq
-	var mtx
+	var mtx = cond._mtx
+	var seq = xget(&cond._seq)
 
-	mtx = cond._mtx
-	seq = cond._seq
 	mtxunlock(mtx)
+	ftxwait(&cond._seq, seq, -1)
 
 	/*
-	FIXME?: `ftxwait` can be interrupted but `condwait` should always be
-	done in a loop anyway.
+	In the event of a broadcast, we aren't requeueing waiters from the
+	condvar's futex to the mutex's futex so we can just reacquire the mutex
+	normally.
 	*/
-	ftxwait(&cond._seq, seq, Zptr)
-
 	mtxlock(mtx)
 }
 
diff --git a/lib/thread/condvar.myr b/lib/thread/condvar.myr
index 4fde7073..f843f843 100644
--- a/lib/thread/condvar.myr
+++ b/lib/thread/condvar.myr
@@ -8,7 +8,8 @@ use "sem"
 pkg thread =
 	type cond = struct
 		_mtx	: mutex#
-		_waitq	: condwaiter#
+		_head	: condwaiter#
+		_tail	: condwaiter#
 		_lock	: mutex
 	;;
 
@@ -18,15 +19,8 @@ pkg thread =
 	const condbroadcast	: (cond : cond# -> void)
 ;;
 
-/*
-The waitqueue is a doubly-linked list because we'll need to remove waiters from
-anywhere in the list when we add timeout support.
-
-`cond._waitq.prev` is the tail of the queue.
-*/
 type condwaiter = struct
 	next : condwaiter#
-	prev : condwaiter#
 	sem : sem
 ;;
 
@@ -40,14 +34,9 @@ const condwait = {cond
 	var waiter = std.mk([.sem = mksem(0)])
 
 	mtxlock(lock)
-	match cond._waitq
-	| Zptr:
-		waiter.prev = waiter
-		cond._waitq = waiter
-	| q:
-		waiter.prev = q.prev
-		waiter.prev.next = waiter
-		q.prev = waiter
+	match cond._tail
+	| Zptr: cond._head = cond._tail = waiter
+	| tail: cond._tail = tail.next = waiter
 	;;
 
 	mtxunlock(lock)
@@ -61,13 +50,13 @@ const condsignal = {cond
 	var lock = &cond._lock
 
 	mtxlock(lock)
-	var head = cond._waitq
+	var head = cond._head
 	if head != Zptr
-		if head.next != Zptr
-			head.next.prev = head.prev
-		;;
-		cond._waitq = head.next
+		cond._head = head.next
 		sempost(&head.sem)
+		if cond._head == Zptr
+			cond._tail = Zptr
+		;;
 	;;
 	mtxunlock(lock)
 }
@@ -81,9 +70,10 @@ const condbroadcast = {cond
 	var head = Zptr
 
 	mtxlock(lock)
-	while (head = cond._waitq) != Zptr
-		cond._waitq = head.next
+	while (head = cond._head) != Zptr
+		cond._head = head.next
 		sempost(&head.sem)
 	;;
+	cond._tail = Zptr
 	mtxunlock(lock)
 }
diff --git a/lib/thread/futex+freebsd.myr b/lib/thread/futex+freebsd.myr
index ea5f28d8..c011bf94 100644
--- a/lib/thread/futex+freebsd.myr
+++ b/lib/thread/futex+freebsd.myr
@@ -1,3 +1,4 @@
+use std
 use sys
 
 use "atomic"
@@ -7,34 +8,53 @@ pkg thread =
 	type ftxtag = uint32
 	impl atomic ftxtag
 
-	const ftxwait : (uaddr : ftxtag#, val : ftxtag, timeout : sys.timespec# -> int)
-	const ftxwake : (uaddr : ftxtag# -> int)
-	const ftxwakeall : (uaddr : ftxtag# -> int)
+	const ftxwait : (uaddr : ftxtag#, val : ftxtag, tmout : std.time -> sys.errno)
+	const ftxwake : (uaddr : ftxtag# -> void)
+	const ftxwakeall : (uaddr : ftxtag# -> void)
 ;;
 
-const ftxwait = {uaddr, val, timeout
-	if timeout == Zptr
-		-> sys.umtx_op((uaddr : void#), sys.Umtxwaituintpriv, (val : uint64), Zptr, Zptr)
+const ftxwait = {uaddr, val, tmout
+	var ut : sys.umtx_time, utp, utsize, rc
+
+	if tmout < 0
+		utp = Zptr
+		utsize = Zptr
+	else
+		var t = (tmout : int64)
+		std.assert(sys.clock_gettime(`sys.Clockmonotonic, &ut.timeout) == 0,
+			"error: clock_gettime returned -1\n")
+		ut.timeout.nsec += (t % 1_000_000) * 1000
+		ut.timeout.sec += (ut.timeout.nsec / 1_000_000_000) + (t / 1_000_000)
+		ut.timeout.nsec %= 1_000_000_000
+		ut.flags = (sys.Umtxabstime : uint32)
+		ut.clockid = (sys.clockid(`sys.Clockmonotonic) : uint32)
+		utp = (&ut : void#)
+		utsize = (sizeof(sys.umtx_time) : void#)
+	;;
+
+	while (rc = (sys.umtx_op((uaddr : void#),
+			sys.Umtxwaituintpriv,
+			(val : uint64),
+			utsize,
+			utp) : sys.errno)) == sys.Eintr
 	;;
 
-	var ut : sys._umtx_time = [
-		._timeout = timeout#,
-		._flags = (sys.Umtxabstime : uint32),
-		._clockid = 1, /* CLOCK_MONOTONIC. Not exported from sys. */
-	]
-	-> sys.umtx_op((uaddr : void#),
-		sys.Umtxwaituintpriv,
-		(val : uint64),
-		(sizeof(sys._umtx_time) : void#),
-		(&ut : void#))
+	match rc
+	| 0: -> 0
+	| sys.Eagain: -> sys.Eagain
+	| sys.Etimedout: -> sys.Etimedout
+	| err:
+		std.fput(2, "error: umtx_op returned {}\n", err)
+		std.suicide()
+	;;
 }
 
 const ftxwake = {uaddr
-	-> sys.umtx_op((uaddr : void#), sys.Umtxwakepriv, 1, Zptr, Zptr)
+	sys.umtx_op((uaddr : void#), sys.Umtxwakepriv, 1, Zptr, Zptr)
 }
 
 const ftxwakeall = {uaddr
-	-> sys.umtx_op((uaddr : void#), sys.Umtxwakepriv, 0x7fffffff, Zptr, Zptr)
+	sys.umtx_op((uaddr : void#), sys.Umtxwakepriv, 0x7fffffff, Zptr, Zptr)
 }
 
 impl atomic ftxtag =
diff --git a/lib/thread/futex+linux.myr b/lib/thread/futex+linux.myr
index c5dbf062..c182644c 100644
--- a/lib/thread/futex+linux.myr
+++ b/lib/thread/futex+linux.myr
@@ -1,3 +1,4 @@
+use std
 use sys
 
 use "atomic"
@@ -7,26 +8,60 @@ pkg thread =
 	type ftxtag = uint32
 	impl atomic ftxtag
 
-	const ftxwait : (uaddr : ftxtag#, val : ftxtag, timeout : sys.timespec# -> int)
-	const ftxwake : (uaddr : ftxtag# -> int)
-	const ftxwakeall : (uaddr : ftxtag# -> int)
+	const ftxwait : (uaddr : ftxtag#, val : ftxtag, tmout : std.time -> sys.errno)
+	const ftxwake : (uaddr : ftxtag# -> void)
+	const ftxwakeall : (uaddr : ftxtag# -> void)
 ;;
 
-const ftxwait = {uaddr, val, timeout
-	-> (sys.futex((uaddr : int32#),
-		sys.Futexwait | sys.Futexpriv,
-		(val : int32),
-		timeout,
-		Zptr,
-		0) : int)
+const ftxwait = {uaddr, val, tmout
+	var rc
+
+	if tmout < 0
+		while (rc = (sys.futex((uaddr : int32#),
+				sys.Futexwait | sys.Futexpriv,
+				(val : int32),
+				Zptr,
+				Zptr,
+				0) : sys.errno)) == sys.Eintr
+		;;
+	else
+		var t = (tmout : int64)
+		var ts
+		std.assert(sys.clock_gettime(`sys.Clockmonotonic, &ts) == 0,
+			"error: clock_gettime returned -1\n")
+		ts.nsec += (t % 1_000_000) * 1000
+		ts.sec += (ts.nsec / 1_000_000_000) + (t / 1_000_000)
+		ts.nsec %= 1_000_000_000
+
+		/*
+		Futexwaitbitset + Futexbitsetmatchany causes the timeout to be
+		treated as absolute rather than relative.
+		*/
+		while (rc = (sys.futex((uaddr : int32#),
+				sys.Futexwaitbitset | sys.Futexpriv,
+				(val : int32),
+				&ts,
+				Zptr,
+				sys.Futexbitsetmatchany) : sys.errno)) == sys.Eintr
+		;;
+	;;
+
+	match rc
+	| 0: -> 0
+	| sys.Eagain: -> sys.Eagain
+	| sys.Etimedout: -> sys.Etimedout
+	| err:
+		std.fput(2, "error: futex returned {}\n", err)
+		std.suicide()
+	;;
 }
 
 const ftxwake = {uaddr
-	-> (sys.futex((uaddr : int32#), sys.Futexwake | sys.Futexpriv, 1, Zptr, Zptr, 0) : int)
+	sys.futex((uaddr : int32#), sys.Futexwake | sys.Futexpriv, 1, Zptr, Zptr, 0)
 }
 
 const ftxwakeall = {uaddr
-	-> (sys.futex((uaddr : int32#), sys.Futexwake | sys.Futexpriv, 0x7fffffff, Zptr, Zptr, 0) : int)
+	sys.futex((uaddr : int32#), sys.Futexwake | sys.Futexpriv, 0x7fffffff, Zptr, Zptr, 0)
 }
 
 impl atomic ftxtag =
diff --git a/lib/thread/futex+openbsd:6.2.myr b/lib/thread/futex+openbsd:6.2.myr
index e3c9c413..5778e288 100644
--- a/lib/thread/futex+openbsd:6.2.myr
+++ b/lib/thread/futex+openbsd:6.2.myr
@@ -1,3 +1,4 @@
+use std
 use sys
 
 use "atomic"
@@ -7,21 +8,72 @@ pkg thread =
 	type ftxtag = uint32
 	impl atomic ftxtag
 
-	const ftxwait : (uaddr : ftxtag#, val : ftxtag, timeout : sys.timespec# -> int)
-	const ftxwake : (uaddr : ftxtag# -> int)
-	const ftxwakeall : (uaddr : ftxtag# -> int)
+	const ftxwait : (uaddr : ftxtag#, val : ftxtag, tmout : std.time -> sys.errno)
+	const ftxwake : (uaddr : ftxtag# -> void)
+	const ftxwakeall : (uaddr : ftxtag# -> void)
 ;;
 
-const ftxwait = {uaddr, val, timeout
-	-> sys.futex((uaddr : uint32#), sys.Futexwait, (val : int), timeout, Zptr)
+const ftxwait = {uaddr, val, tmout
+	var rc
+
+	if tmout < 0
+		while (rc = (sys.futex((uaddr : uint32#),
+				sys.Futexwait,
+				(val : int),
+				Zptr,
+				Zptr) : sys.errno)) == sys.Eintr
+		;;
+	else
+		var t = (tmout : int64)
+		var start
+		std.assert(sys.clock_gettime(`sys.Clockmonotonic, &start) == 0,
+			"error: clock_gettime returned -1\n")
+		var ts = [
+			.sec = t / 1_000_000
+			.nsec = (t % 1_000_000) * 1000
+		]
+
+		while (rc = (sys.futex((uaddr : uint32#),
+				sys.Futexwait,
+				(val : int),
+				&ts,
+				Zptr) : sys.errno)) == sys.Eintr
+			var now
+			std.assert(sys.clock_gettime(`sys.Clockmonotonic, &now) == 0,
+				"error: clock_gettime returned -1\n")
+			var t1 = t - ((now.sec - start.sec) * 1_000_000)
+			var nsec = now.nsec - start.nsec
+			if nsec >= 0
+				t1 -= nsec / 1000
+			else
+				t1 -= (1_000_000_000 + nsec) / 1000
+			;;
+
+			if t1 > t
+				-> sys.Etimedout
+			;;
+			ts.sec = t1 / 1_000_000
+			ts.nsec = (t1 % 1_000_000) * 1000
+			t = t1
+		;;
+	;;
+
+	match rc
+	| 0: -> 0
+	| sys.Eagain: -> sys.Eagain
+	| sys.Etimedout: -> sys.Etimedout
+	| err:
+		std.fput(2, "error: futex returned {}\n", err)
+		std.suicide()
+	;;
 }
 
 const ftxwake = {uaddr
-	-> sys.futex((uaddr : uint32#), sys.Futexwake, 1, Zptr, Zptr)
+	sys.futex((uaddr : uint32#), sys.Futexwake, 1, Zptr, Zptr)
 }
 
 const ftxwakeall = {uaddr
-	-> sys.futex((uaddr : uint32#), sys.Futexwake, 0x7fffffff, Zptr, Zptr)
+	sys.futex((uaddr : uint32#), sys.Futexwake, 0x7fffffff, Zptr, Zptr)
 }
 
 impl atomic ftxtag =
diff --git a/lib/thread/futex+osx.myr b/lib/thread/futex+osx.myr
index 17478703..ea93fda7 100644
--- a/lib/thread/futex+osx.myr
+++ b/lib/thread/futex+osx.myr
@@ -8,49 +8,72 @@ pkg thread =
 	type ftxtag = uint64
 	impl atomic ftxtag
 
-	const ftxwait : (uaddr : ftxtag#, val : ftxtag, timeout : sys.timespec# -> int)
-	const ftxwake : (uaddr : ftxtag# -> int)
-	const ftxwakeall : (uaddr : ftxtag# -> int)
+	const ftxwait : (uaddr : ftxtag#, val : ftxtag, tmout : std.time -> sys.errno)
+	const ftxwake : (uaddr : ftxtag# -> void)
+	const ftxwakeall : (uaddr : ftxtag# -> void)
 ;;
 
 /*
  * The ulock_ functions are undocumented but the relevant source can be found at
  * https://github.com/apple/darwin-xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/bsd/kern/sys_ulock.c
  */
-const ftxwait = {uaddr, val, timeout
-	if timeout == Zptr
-		-> sys.ulock_wait(sys.Ulockcompareandwait, (uaddr : uint64#), (val : uint64), 0)
-	;;
+const ftxwait = {uaddr, val, tmout
+	var rc
 
-	var ts
-	var err = sys.clock_gettime(`sys.Clockmonotonic, &ts)
-	std.assert(err == 0, "error: clock_gettime returned {}\n", err)
-	if timeout.sec < ts.sec
-		-> (std.Etimedout : int)
-	;;
+	if tmout < 0
+		while (rc = (sys.ulock_wait(sys.Ulockcompareandwait,
+				(uaddr : uint64#),
+				(val : uint64),
+				0) : sys.errno)) == sys.Eintr
+		;;
+	else
+		var start, t
+		std.assert(tmout <= 0xffffffff, "error: maximum os x futex timeout exceeded\n")
+		std.assert(sys.clock_gettime(`sys.Clockmonotonic, &start) == 0,
+			"error: clock_gettime returned -1\n")
+		t = (tmout : uint32)
+
+		while (rc = (sys.ulock_wait(sys.Ulockcompareandwait,
+				(uaddr : uint64#),
+				(val : uint64),
+				t) : sys.errno)) == sys.Eintr
+			var now
+			std.assert(sys.clock_gettime(`sys.Clockmonotonic, &now) == 0,
+				"error: clock_gettime returned -1\n")
+			var t1 = t - (((now.sec - start.sec) * 1_000_000) : uint32)
+			var nsec = now.nsec - start.nsec
+			if nsec >= 0
+				t1 -= (nsec / 1000 : uint32)
+			else
+				t1 -= ((1_000_000_000 + nsec) / 1000 : uint32)
+			;;
 
-	var usec = 0
-	var sec = (timeout.sec - ts.sec) * 1000
-	std.assert(sec <= 0xffffffff, "error: maximum futex timeout exceeded\n")
-	usec = (sec : uint32)
-	if timeout.nsec > ts.nsec
-		var nsec = (timeout.nsec - ts.nsec) / 1000
-		std.assert(usec + nsec > usec, "error: maximum futex timeout exceeded\n")
-		usec += nsec
+			if t1 > t
+				-> sys.Etimedout
+			;;
+			t = t1
+		;;
 	;;
 
-	if usec == 0
-		-> (std.Etimedout : int)
+	match rc
+	| 0: -> 0
+	| sys.Eagain: -> sys.Eagain
+	| sys.Etimedout: -> sys.Etimedout
+	| err:
+		if err > 0
+			-> 0
+		;;
+		std.fput(2, "error: ulock_wait returned {}\n", err)
+		std.suicide()
 	;;
-	-> sys.ulock_wait(sys.Ulockcompareandwait, (uaddr : uint64#), (val : uint64), usec)
 }
 
 const ftxwake = {uaddr
-	-> sys.ulock_wake(sys.Ulockcompareandwait, (uaddr : uint64#), 0)
+	sys.ulock_wake(sys.Ulockcompareandwait, (uaddr : uint64#), 0)
 }
 
 const ftxwakeall = {uaddr
-	-> sys.ulock_wake(sys.Ulockcompareandwait | sys.Ulockulfwakeall, (uaddr : uint64#), 0)
+	sys.ulock_wake(sys.Ulockcompareandwait | sys.Ulockulfwakeall, (uaddr : uint64#), 0)
 }
 
 impl atomic ftxtag =
diff --git a/lib/thread/mutex+futex.myr b/lib/thread/mutex+futex.myr
index a6453fa9..50e8406d 100644
--- a/lib/thread/mutex+futex.myr
+++ b/lib/thread/mutex+futex.myr
@@ -1,5 +1,4 @@
 use "atomic"
-use "common"
 use "futex"
 
 pkg thread =
@@ -28,12 +27,12 @@ const mkmtx = {
 const mtxlock = {mtx
 	var c
 
-	/* 
+	/*
 	Uncontended case: we get an unlocked mutex, and we lock it.
 	*/
-        c = Locked
+	c = Locked
 	for var i = 0; i < nspin; i++
-		c = xcas(&mtx._state, Unlocked, Locked) 
+		c = xcas(&mtx._state, Unlocked, Locked)
 		if c == Unlocked
 			-> void
 		;;
@@ -49,7 +48,7 @@ const mtxlock = {mtx
 	;;
 
 	while c != Unlocked
-		ftxwait(&mtx._state, Contended, Zptr)
+		ftxwait(&mtx._state, Contended, -1)
 		c = xchg(&mtx._state, Contended)
 	;;
 }
@@ -74,6 +73,6 @@ const mtxunlock = {mtx
 
 const mtxcontended = {mtx
 	while xchg(&mtx._state, Contended) != Unlocked
-		ftxwait(&mtx._state, Contended, Zptr)
+		ftxwait(&mtx._state, Contended, -1)
 	;;
 }
diff --git a/lib/thread/mutex+plan9.myr b/lib/thread/mutex+plan9.myr
index e2e1207f..67c93bfb 100644
--- a/lib/thread/mutex+plan9.myr
+++ b/lib/thread/mutex+plan9.myr
@@ -1,9 +1,7 @@
 use std
 use sys
 
-
 use "atomic"
-use "common"
 
 pkg thread =
 	type mutex = struct
diff --git a/lib/thread/mutex.myr b/lib/thread/mutex.myr
index 5b905749..b37f2fb3 100644
--- a/lib/thread/mutex.myr
+++ b/lib/thread/mutex.myr
@@ -1,9 +1,7 @@
 use std
 use sys
 
-
 use "atomic"
-use "common"
 
 pkg thread =
 	type mutex = struct
diff --git a/lib/thread/sem+futex.myr b/lib/thread/sem+futex.myr
index d79bd41b..542586be 100644
--- a/lib/thread/sem+futex.myr
+++ b/lib/thread/sem+futex.myr
@@ -1,7 +1,6 @@
 use std
 
 use "atomic"
-use "common"
 use "futex"
 
 pkg thread =
@@ -28,7 +27,7 @@ const semwait = {s
 				-> void
 			;;
 		;;
-		ftxwait(&s._val, v, Zptr)
+		ftxwait(&s._val, v, -1)
 	;;
 }
 
diff --git a/lib/thread/sem+plan9.myr b/lib/thread/sem+plan9.myr
index 221a8056..449d7ceb 100644
--- a/lib/thread/sem+plan9.myr
+++ b/lib/thread/sem+plan9.myr
@@ -2,7 +2,6 @@ use std
 use sys
 
 use "atomic"
-use "common"
 
 pkg thread =
 	type sem = struct
diff --git a/lib/thread/waitgrp+futex.myr b/lib/thread/waitgrp+futex.myr
index 7fbd5eb6..701d8f04 100644
--- a/lib/thread/waitgrp+futex.myr
+++ b/lib/thread/waitgrp+futex.myr
@@ -1,7 +1,6 @@
 use std
 
 use "atomic"
-use "common"
 use "futex"
 
 pkg thread =
@@ -22,7 +21,7 @@ const wgwait = {w
 	var v = 0
 
 	while (v = xget(&w._val)) != 0
-		ftxwait(&w._val, v, Zptr)
+		ftxwait(&w._val, v, -1)
 	;;
 }
 
diff --git a/support/syscall-gen/specials+freebsd-x64.frag b/support/syscall-gen/specials+freebsd-x64.frag
index 82b3c31b..de5cfa4a 100644
--- a/support/syscall-gen/specials+freebsd-x64.frag
+++ b/support/syscall-gen/specials+freebsd-x64.frag
@@ -65,6 +65,7 @@
 	const mmap	: (addr:byte#, len:size, prot:mprot, flags:mopt, fd:fd, off:off -> byte#)
 
 	/* time - doublecheck if this is right */
+	const clockid		: (clk : clock -> uint64)
 	const clock_getres	: (clk : clock, ts : timespec# -> int32)
 	const clock_gettime	: (clk : clock, ts : timespec# -> int32)
 	const clock_settime	: (clk : clock, ts : timespec# -> int32)
diff --git a/support/syscall-gen/specials+linux-x64.frag b/support/syscall-gen/specials+linux-x64.frag
index de2ce273..6f8b8063 100644
--- a/support/syscall-gen/specials+linux-x64.frag
+++ b/support/syscall-gen/specials+linux-x64.frag
@@ -62,7 +62,7 @@ const dup2	: (src : fd, dst : fd -> fd)
 
 /* threading */
 const futex	: (uaddr : int32#, op : futexop, val : int32, \
-	timeout : timespec#, uaddr2 : int32#, val3 : int32 -> int64)
+	timeout : timespec#, uaddr2 : int32#, val3 : int32 -> int)
 const semctl	:  (semid : int, semnum : int, cmd : int, arg : void# -> int)
 
 /* polling */
@@ -205,7 +205,7 @@ const sigprocmask	= {sig, act, oact;	-> (syscall(Sysrt_sigprocmask, a(sig), a(ac
 
 /* threading */
 const futex	= {uaddr, op, val, timeout, uaddr2, val3
-	-> syscall(Sysfutex, a(uaddr), a(op), a(val), a(timeout), a(uaddr2), a(val3))
+	-> (syscall(Sysfutex, a(uaddr), a(op), a(val), a(timeout), a(uaddr2), a(val3)) : int)
 }
 const semctl	= {semid, semnum, cmd, arg
 	-> (syscall(Syssemctl, a(semnum), a(cmd), a(arg)) : int)
diff --git a/support/syscall-gen/types+freebsd-x64.frag b/support/syscall-gen/types+freebsd-x64.frag
index 9bae3054..f8990533 100644
--- a/support/syscall-gen/types+freebsd-x64.frag
+++ b/support/syscall-gen/types+freebsd-x64.frag
@@ -575,6 +575,13 @@ type uuid = struct
 	node				: uint8[_Uuidnodesz];
 ;;
 
+const Umtxabstime = 1
+type umtx_time = struct
+	timeout	: timespec
+	flags	: uint32
+	clockid	: uint32
+;;
+
 /* open options */
 const Ordonly  	: fdopt = 0x0
 const Owronly  	: fdopt = 0x1
diff --git a/support/syscall-gen/types+linux-x64.frag b/support/syscall-gen/types+linux-x64.frag
index 13d95aaa..9e90a5ac 100644
--- a/support/syscall-gen/types+linux-x64.frag
+++ b/support/syscall-gen/types+linux-x64.frag
@@ -556,6 +556,8 @@ const Futexcmprequeuepi	: futexop = 12
 const Futexpriv	: futexop = 128
 const Futexclockrt	: futexop = 256
 
+const Futexbitsetmatchany : int32 = -1
+
 /* poll events : posix */
 const Pollin	: pollevt = 0x001	/* There is data to read.  */
 const Pollpri	: pollevt = 0x002	/* There is urgent data to read.  */
-- 
2.18.0

 ---- On Tue, 14 Aug 2018 23:15:39 -0700 Ori Bernstein <ori@xxxxxxxxxxxxxx> wrote ---- 
 > Overall, seems sane, biggest issue is a missed wake that I let slip through earlier. 
 > Comments inline. 
 >  
 > On Sat, 11 Aug 2018 19:12:47 -0700, iriri <iri@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx> wrote: 
 >  
 > >  
 > > diff --git a/lib/sys/sys+linux-x64.myr b/lib/sys/sys+linux-x64.myr 
 > > index 1cebb123..83fdea53 100644 
 > > --- a/lib/sys/sys+linux-x64.myr 
 > > +++ b/lib/sys/sys+linux-x64.myr 
 > > @@ -562,6 +562,8 @@ pkg sys = 
 > >       
 > >      const Futexpriv    : futexop = 128 
 > >      const Futexclockrt    : futexop = 256 
 > > + 
 > > +    const Futexbitsetmatchany : int32 = -1 
 >        
 > Same note as previous patch. 
 >  
 > >      /* poll events : posix */ 
 > >      const Pollin    : pollevt = 0x001    /* There is data to read.  */ 
 > > @@ -1273,7 +1275,7 @@ pkg sys = 
 > >      const dup    : (fd : fd -> fd) 
 > >      const dup2    : (src : fd, dst : fd -> fd) 
 > >      const futex    : (uaddr : int32#, op : futexop, val : int32, \ 
 > > diff --git a/lib/thread/condvar+freebsd.myr b/lib/thread/condvar+freebsd.myr 
 > > index 002757ae..4e2b0c11 100644 
 > > --- a/lib/thread/condvar+freebsd.myr 
 > > +++ b/lib/thread/condvar+freebsd.myr 
 > > @@ -1,7 +1,6 @@ 
 > >  use std 
 > >   
 > >  use "atomic" 
 > > -use "common" 
 > >  use "mutex" 
 > >  use "futex" 
 > >   
 > > @@ -22,19 +21,11 @@ const mkcond = {mtx 
 > >  } 
 > >   
 > >  const condwait = {cond 
 > > -    var seq 
 > > -    var mtx 
 > > +    var mtx = cond._mtx 
 > > +    var seq = xget(&cond._seq) 
 > >   
 > > -    mtx = cond._mtx 
 > > -    seq = cond._seq 
 > >      mtxunlock(mtx) 
 > > - 
 > > -    /* 
 > > -    FIXME?: `ftxwait` can be interrupted but `condwait` should always be 
 > > -    done in a loop anyway. 
 > > -    */ 
 > > -    ftxwait(&cond._seq, seq, Zptr) 
 > > - 
 > > +    ftxwait(&cond._seq, seq, 0) 
 > >      mtxlock(mtx) 
 >  
 > Looking at this code, I realized it may have lost something 
 > in the translation from Linux cond vars. Isn't it possible 
 > that if we have multiple waiters on the condition variable, 
 > we could miss waking up a waiter, because we erronously decide 
 > a lock is uncontended? 
 >  
 > Consider this interleaving of operations: 
 >  
 >     t1        t2         t3 
 >   (st=U) |               |               | 
 >     lock +               |               | 
 > condwait +               |               | 
 >   (st=L) |               |               | 
 >   unlock +               |               | 
 >   (st=U) |               |               | 
 >          |          lock +               | 
 >          |        (st=L) |               | 
 >          |      condwait +               | 
 >          |        (st=L) |               | 
 >          |        unlock +               | 
 >          |        (st=U) |               | 
 >          |               |               | 
 >          |               |        signal + 
 >          |               |       ftxwake | 
 >     lock +               |               | 
 >   (st=L) |               |               | 
 >  dostuff |               |               | 
 >   unlock +               |               | 
 >   (st=U) |               |               | 
 >  
 > It seems to me that T2 will never get woken up unless 
 > we use the special mtxcontended() function in our futex 
 > based mutex implementation, which sets it to contended 
 > while locking the futexe. That forces the unlock to issue 
 > a wakeup, and wakes up the thread. 
 >  
 > This could probably use some more commenting. Possibly 
 > with the contents of the above message. 
 >  
 >  
 > > diff --git a/lib/thread/condvar+osx.myr b/lib/thread/condvar+osx.myr 
 > > index d74c321f..d18f4ccb 100644 
 > > --- a/lib/thread/condvar+osx.myr 
 > > +++ b/lib/thread/condvar+osx.myr 
 > > @@ -1,7 +1,6 @@ 
 > >  use std 
 > >   
 > >  use "atomic" 
 > > -use "common" 
 > >  use "mutex" 
 > >  use "futex" 
 > >   
 > > @@ -22,19 +21,11 @@ const mkcond = {mtx 
 > >  } 
 > >   
 > >  const condwait = {cond 
 > > -    var seq 
 > > -    var mtx 
 > > +    var mtx = cond._mtx 
 > > +    var seq = xget(&cond._seq) 
 > >   
 > > -    mtx = cond._mtx 
 > > -    seq = cond._seq 
 > >      mtxunlock(mtx) 
 > > - 
 > > -    /* 
 > > -    FIXME?: `ftxwait` can be interrupted but `condwait` should always be 
 > > -    done in a loop anyway. 
 > > -    */ 
 > > -    ftxwait(&cond._seq, seq, Zptr) 
 > > - 
 > > +    ftxwait(&cond._seq, seq, 0) 
 > >      mtxlock(mtx) 
 > >  } 
 >  
 > Same bug, seems like we can have a missed wakeup. Also, it really looks like 
 > all of the condvar implementations could be unified, at least for the waiting. 
 > Is it the waking that keeps them separate right now? 
 >  
 > >   
 > > diff --git a/lib/thread/futex+freebsd.myr b/lib/thread/futex+freebsd.myr 
 > > index ea5f28d8..a9ad01ed 100644 
 > > --- a/lib/thread/futex+freebsd.myr 
 > > +++ b/lib/thread/futex+freebsd.myr 
 > > @@ -1,3 +1,4 @@ 
 > > +use std 
 > >  use sys 
 > >   
 > >  use "atomic" 
 > > @@ -7,34 +8,51 @@ pkg thread = 
 > >      type ftxtag = uint32 
 > >      impl atomic ftxtag 
 > >   
 > > -    const ftxwait : (uaddr : ftxtag#, val : ftxtag, timeout : sys.timespec# -> int) 
 > > -    const ftxwake : (uaddr : ftxtag# -> int) 
 > > -    const ftxwakeall : (uaddr : ftxtag# -> int) 
 > > +    const ftxwait : (uaddr : ftxtag#, val : ftxtag, tmout : uint32 -> sys.errno) 
 > > +    const ftxwake : (uaddr : ftxtag# -> void) 
 > > +    const ftxwakeall : (uaddr : ftxtag# -> void) 
 > >  ;; 
 > >   
 > > -const ftxwait = {uaddr, val, timeout 
 > > -    if timeout == Zptr 
 > > -        -> sys.umtx_op((uaddr : void#), sys.Umtxwaituintpriv, (val : uint64), Zptr, Zptr) 
 > > +const ftxwait = {uaddr, val, tmout 
 > > +    var ut : sys._umtx_time, utp, utsize, rc 
 > > + 
 > > +    if tmout == 0 
 >  
 > Preference: use -1 for infinite timeout. 
 >  
 > > +        utp = Zptr 
 > > +        utsize = Zptr 
 > > +    else 
 > > +        var t = (tmout : int64) 
 > > +        std.assert(sys.clock_gettime(`sys.Clockmonotonic, &ut._timeout) == 0, 
 > > +            "error: clock_gettime returned -1\n") 
 > > +        ut._timeout.nsec += (t % 1_000_000) * 1000 
 > > +        ut._timeout.sec += (ut._timeout.nsec / 1_000_000_000) + (t / 1_000_000) 
 > > +        ut._timeout.nsec %= 1_000_000_000 
 > > +        ut._flags = (sys.Umtxabstime : uint32) 
 > > +        ut._clockid = 1 /* CLOCK_MONOTONIC. Not exported from sys. */ 
 >  
 > Can always export from sys. 
 >  
 > > +        utp = (&ut : void#) 
 > > +        utsize = (sizeof(sys._umtx_time) : void#) 
 > > +    ;; 
 > > + 
 > > +    while (rc = (sys.umtx_op((uaddr : void#), 
 > > +            sys.Umtxwaituintpriv, 
 > > +            (val : uint64), 
 > > +            utsize, 
 > > +            utp) : sys.errno)) == sys.Eintr 
 > >      ;; 
 > >   
 > > -    var ut : sys._umtx_time = [ 
 > > -        ._timeout = timeout#, 
 > > -        ._flags = (sys.Umtxabstime : uint32), 
 > > -        ._clockid = 1, /* CLOCK_MONOTONIC. Not exported from sys. */ 
 > > -    ] 
 > > -    -> sys.umtx_op((uaddr : void#), 
 > > -        sys.Umtxwaituintpriv, 
 > > -        (val : uint64), 
 > > -        (sizeof(sys._umtx_time) : void#), 
 > > -        (&ut : void#)) 
 > > +    match rc 
 > > +    | 0: -> 0 
 > > +    | sys.Eagain: -> sys.Eagain 
 > > +    | sys.Etimedout: -> sys.Etimedout 
 > > +    | err: std.fatal("error: futex returned {}\n", err) 
 >  
 > std.abort(), so we can get a nice core dump. 
 >  
 > > +    ;; 
 > >  } 
 > >   
 > >  const ftxwake = {uaddr 
 > > -    -> sys.umtx_op((uaddr : void#), sys.Umtxwakepriv, 1, Zptr, Zptr) 
 > > +    sys.umtx_op((uaddr : void#), sys.Umtxwakepriv, 1, Zptr, Zptr) 
 > >  } 
 > >   
 > >  const ftxwakeall = {uaddr 
 > > -    -> sys.umtx_op((uaddr : void#), sys.Umtxwakepriv, 0x7fffffff, Zptr, Zptr) 
 > > +    sys.umtx_op((uaddr : void#), sys.Umtxwakepriv, 0x7fffffff, Zptr, Zptr) 
 > >  } 
 > >   
 > >  impl atomic ftxtag = 
 > > diff --git a/lib/thread/futex+linux.myr b/lib/thread/futex+linux.myr 
 > > index c5dbf062..d20aca8b 100644 
 > > --- a/lib/thread/futex+linux.myr 
 > > +++ b/lib/thread/futex+linux.myr 
 > > @@ -1,3 +1,4 @@ 
 > > +use std 
 > >  use sys 
 > >   
 > >  use "atomic" 
 > > @@ -7,26 +8,58 @@ pkg thread = 
 > >      type ftxtag = uint32 
 > >      impl atomic ftxtag 
 > >   
 > > -    const ftxwait : (uaddr : ftxtag#, val : ftxtag, timeout : sys.timespec# -> int) 
 > > -    const ftxwake : (uaddr : ftxtag# -> int) 
 > > -    const ftxwakeall : (uaddr : ftxtag# -> int) 
 > > +    const ftxwait : (uaddr : ftxtag#, val : ftxtag, tmout : uint32 -> sys.errno) 
 > > +    const ftxwake : (uaddr : ftxtag# -> void) 
 > > +    const ftxwakeall : (uaddr : ftxtag# -> void) 
 > >  ;; 
 > >   
 > > -const ftxwait = {uaddr, val, timeout 
 > > -    -> (sys.futex((uaddr : int32#), 
 > > -        sys.Futexwait | sys.Futexpriv, 
 > > -        (val : int32), 
 > > -        timeout, 
 > > -        Zptr, 
 > > -        0) : int) 
 > > +const ftxwait = {uaddr, val, tmout 
 > > +    var rc 
 > > + 
 >  
 > > +    if tmout == 0 
 > > +        while (rc = (sys.futex((uaddr : int32#), 
 > > +                sys.Futexwait | sys.Futexpriv, 
 > > +                (val : int32), 
 > > +                Zptr, 
 > > +                Zptr, 
 > > +                0) : sys.errno)) == sys.Eintr 
 > > +        ;; 
 > > +    else 
 > > +        var t = (tmout : int64) 
 > > +        var ts 
 > > +        std.assert(sys.clock_gettime(`sys.Clockmonotonic, &ts) == 0, 
 > > +            "error: clock_gettime returned -1\n") 
 > > +        ts.nsec += (t % 1_000_000) * 1000 
 > > +        ts.sec += (ts.nsec / 1_000_000_000) + (t / 1_000_000) 
 > > +        ts.nsec %= 1_000_000_000 
 > > + 
 > > +        /* 
 > > +        Futexwaitbitset + Futexbitsetmatchany causes the timeout to be 
 > > +        treated as absolute rather than relative. 
 > > +        */ 
 > > +        while (rc = (sys.futex((uaddr : int32#), 
 > > +                sys.Futexwaitbitset | sys.Futexpriv, 
 > > +                (val : int32), 
 > > +                &ts, 
 > > +                Zptr, 
 > > +                sys.Futexbitsetmatchany) : sys.errno)) == sys.Eintr 
 > > +        ;; 
 > > +    ;; 
 >  
 > This could be extracted into a utility function shared across all the 
 > implementations, now that the time struct is regularized. Something 
 > like: 
 >  
 >     var tmptr = totimeout(&tm) 
 >     while (rc = sys.futex(..., tmptr, ...) == sys.Eintr) 
 >         break 
 >     ;; 
 >  
 > >  impl atomic ftxtag = 
 > > diff --git a/lib/thread/futex+openbsd:6.2.myr b/lib/thread/futex+openbsd:6.2.myr 
 > > index e3c9c413..cda80347 100644 
 > > --- a/lib/thread/futex+openbsd:6.2.myr 
 > > +++ b/lib/thread/futex+openbsd:6.2.myr 
 >  
 > Same comments as the other systems. 
 >  
 > >  impl atomic ftxtag = 
 > > diff --git a/lib/thread/futex+osx.myr b/lib/thread/futex+osx.myr 
 > > index 17478703..c07e6b3e 100644 
 > > --- a/lib/thread/futex+osx.myr 
 > > +++ b/lib/thread/futex+osx.myr 
 > >  } 
 > >   
 > Same comments as the other systems. 
 >  
 > >  impl atomic ftxtag = 
 >  
 > --  
 >     Ori Bernstein 
 >  
 > 



References:
[PATCH 2/4] Fix futex timeouts and handle futex error codes.iriri <iri@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
Re: [PATCH 2/4] Fix futex timeouts and handle futex error codes.Ori Bernstein <ori@xxxxxxxxxxxxxx>