[PATCH 3/4] Add rwlocks.
[Thread Prev] | [Thread Next]
- Subject: [PATCH 3/4] Add rwlocks.
- From: iriri <iri@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx>
- Reply-to: myrddin-dev@xxxxxxxxxxxxxx
- Date: Sat, 11 Aug 2018 19:15:05 -0700
- To: "myrddin-dev" <myrddin-dev@xxxxxxxxxxxxxx>
I'm not 100% sure about the futex-based implementation, which is based on musl's. I kind of think it should favor writers more.
---
lib/thread/bld.sub | 2 +
lib/thread/rwlock+futex.myr | 90 ++++++++++++++++++++++
lib/thread/rwlock.myr | 149 ++++++++++++++++++++++++++++++++++++
lib/thread/test/mutex.myr | 15 ++--
lib/thread/test/rwlock.myr | 50 ++++++++++++
5 files changed, 297 insertions(+), 9 deletions(-)
create mode 100644 lib/thread/rwlock+futex.myr
create mode 100644 lib/thread/rwlock.myr
create mode 100644 lib/thread/test/rwlock.myr
diff --git a/lib/thread/bld.sub b/lib/thread/bld.sub
index ed2ea2dc..eff3c5bd 100644
--- a/lib/thread/bld.sub
+++ b/lib/thread/bld.sub
@@ -6,11 +6,13 @@ lib thread =
condvar.myr
mutex.myr
ncpu.myr
+ rwlock.myr
sem.myr
waitgrp.myr
# futex-based impls
mutex+futex.myr
+ rwlock+futex.myr
sem+futex.myr
waitgrp+futex.myr
diff --git a/lib/thread/rwlock+futex.myr b/lib/thread/rwlock+futex.myr
new file mode 100644
index 00000000..a8aadf45
--- /dev/null
+++ b/lib/thread/rwlock+futex.myr
@@ -0,0 +1,90 @@
+use std
+
+use "atomic"
+use "futex"
+
+pkg thread =
+ type rwlock = struct
+ _state : ftxtag /* _nreaders:31 : uint32, _wbit:1 : uint32 */
+ ;;
+
+ const mkrw : (-> rwlock)
+ const rwrlock : (rw : rwlock# -> void)
+ const rwwlock : (rw : rwlock# -> void)
+ const rwtryrlock : (rw : rwlock# -> bool)
+ const rwtrywlock : (rw : rwlock# -> bool)
+ const rwrunlock : (rw : rwlock# -> void)
+ const rwwunlock : (rw : rwlock# -> void)
+;;
+
+const Nrmask = 0x7fffffff
+const Wbit = 0x80000000
+
+const mkrw = {
+ -> [._state = 0]
+}
+
+const rwrlock = {rw
+ for ; ;
+ var s = xget(&rw._state)
+ match s & Nrmask
+ | Nrmask - 1: std.die("error: rwlock overflowed\n")
+ | Nrmask:
+ if xcas(&rw._state, s, Nrmask | Wbit) == s
+ ftxwait(&rw._state, Nrmask | Wbit, 0)
+ ;;
+ | _:
+ if xcas(&rw._state, s, s + 1) == s
+ -> void
+ ;;
+ ;;
+ ;;
+}
+
+const rwwlock = {rw
+ for ; ;
+ var s = xcas(&rw._state, 0, Nrmask)
+ if s == 0
+ -> void
+ ;;
+
+ if xcas(&rw._state, s, s | Wbit) == s
+ ftxwait(&rw._state, s | Wbit, 0)
+ ;;
+ ;;
+}
+
+const rwtryrlock = {rw
+ for ; ;
+ var s = xget(&rw._state)
+ match s & Nrmask
+ | Nrmask - 1: std.die("error: rwlock overflowed\n")
+ | Nrmask: -> false
+ | _:
+ if xcas(&rw._state, s, s + 1) == s
+ -> true
+ ;;
+ ;;
+ ;;
+ -> false /* Unreachable */
+}
+
+const rwtrywlock = {rw
+ -> xcas(&rw._state, 0, Nrmask) == 0
+}
+
+const rwrunlock = {rw
+ var prev = xadd(&rw._state, -1)
+ std.assert(prev & Nrmask != 0, "error: rwlock underflowed\n")
+ if prev & Nrmask == 1 && prev & Wbit != 0
+ if xcas(&rw._state, Wbit, 0) == Wbit
+ ftxwake(&rw._state)
+ ;;
+ ;;
+}
+
+const rwwunlock = {rw
+ if xchg(&rw._state, 0) & Wbit != 0
+ ftxwakeall(&rw._state) /* Just Do It(tm). */
+ ;;
+}
diff --git a/lib/thread/rwlock.myr b/lib/thread/rwlock.myr
new file mode 100644
index 00000000..a8241218
--- /dev/null
+++ b/lib/thread/rwlock.myr
@@ -0,0 +1,149 @@
+use std
+
+use "common"
+use "mutex"
+use "sem"
+
+pkg thread =
+ type rwlock = struct
+ _head : rwwaiter#
+ _tail : rwwaiter#
+ _lock : mutex
+ _state : uint32 /* _nreaders:31 : uint32, _wbit:1 : uint32 */
+ ;;
+
+ const mkrw : (-> rwlock)
+ const rwrlock : (rw : rwlock# -> void)
+ const rwwlock : (rw : rwlock# -> void)
+ const rwtryrlock : (rw : rwlock# -> bool)
+ const rwtrywlock : (rw : rwlock# -> bool)
+ const rwrunlock : (rw : rwlock# -> void)
+ const rwwunlock : (rw : rwlock# -> void)
+;;
+
+/*
+We can't use a condvar here if we want to avoid heap allocations, which we do,
+if only for consistency with the futex based implementation.
+*/
+type rwwaiter = struct
+ next : rwwaiter#
+ sem : sem
+;;
+
+const Nrmask = 0x7fffffff
+const Wbit = 0x80000000
+
+const mkrw = {
+ -> [._lock = mkmtx()]
+}
+
+const rwrlock = {rw
+ for ; ;
+ mtxlock(&rw._lock)
+ match rw._state & Nrmask
+ | Nrmask - 1: std.die("error: rwlock overflowed\n")
+ | Nrmask:
+ rw._state |= Wbit
+ var waiter = std.mk([.sem = mksem(0)])
+ if rw._tail != Zptr
+ rw._tail.next = waiter
+ ;;
+ rw._tail = waiter
+
+ mtxunlock(&rw._lock)
+ semwait(&waiter.sem)
+ std.free(waiter)
+ | _:
+ rw._state++
+ mtxunlock(&rw._lock)
+ -> void
+ ;;
+ ;;
+}
+
+const rwwlock = {rw
+ for ; ;
+ mtxlock(&rw._lock)
+ if rw._state == 0
+ rw._state = Nrmask
+ mtxunlock(&rw._lock)
+ -> void
+ ;;
+
+ /* Favor writers over readers to avoid writer starvation. */
+ rw._state |= Wbit
+ var waiter = std.mk([.sem = mksem(0)])
+ waiter.next = rw._head
+ rw._head = waiter
+
+ mtxunlock(&rw._lock)
+ semwait(&waiter.sem)
+ std.free(waiter)
+ ;;
+}
+
+const rwtryrlock = {rw
+ mtxlock(&rw._lock)
+ match rw._state & Nrmask
+ | Nrmask - 1: std.die("error: rwlock overflowed\n")
+ | Nrmask:
+ mtxunlock(&rw._lock)
+ -> false
+ | _:
+ rw._state++
+ mtxunlock(&rw._lock)
+ -> true
+ ;;
+}
+
+const rwtrywlock = {rw
+ var rc
+ mtxlock(&rw._lock)
+ if rw._state == 0
+ rw._state = Nrmask
+ rc = true
+ else
+ rc = false
+ ;;
+ mtxunlock(&rw._lock)
+ -> rc
+}
+
+const rwrunlock = {rw
+ mtxlock(&rw._lock)
+ var prev = rw._state--
+ std.assert(prev & Nrmask != 0, "error: rwlock underflowed\n")
+
+ var writer = Zptr
+ if prev & Nrmask == 1 && prev & Wbit != 0
+ if rw._state == Wbit
+ rw._state = 0
+ if rw._head != Zptr
+ writer = rw._head
+ rw._head = rw._head.next
+ if rw._tail == writer
+ rw._tail = Zptr
+ ;;
+ ;;
+ ;;
+ ;;
+ mtxunlock(&rw._lock)
+
+ if writer != Zptr
+ sempost(&writer.sem)
+ ;;
+}
+
+const rwwunlock = {rw
+ mtxlock(&rw._lock)
+ if rw._state & Wbit != 0
+ var head = Zptr
+ while (head = rw._head) != Zptr
+ rw._head = head.next
+ sempost(&head.sem)
+ ;;
+ rw._tail = Zptr
+ ;;
+ rw._state = 0
+ mtxunlock(&rw._lock)
+}
diff --git a/lib/thread/test/mutex.myr b/lib/thread/test/mutex.myr
index fd58df13..30624449 100644
--- a/lib/thread/test/mutex.myr
+++ b/lib/thread/test/mutex.myr
@@ -6,19 +6,16 @@ use thrtestutil
const Nherd = 20
var val : uint64 = 0
-var done : uint32 = 0
var mtx : thread.mutex
+var done
const main = {
- done = 0
- val = 0
-
mtx = thread.mkmtx()
+ done = thread.mkwg(Nherd)
+
thrtestutil.mkherd(Nherd, incvar)
- while thread.xget(&done) != Nherd
- /* nothing */
- ;;
- if val != 1000 * 20
+ thread.wgwait(&done)
+ if val != 1000 * (Nherd : uint64)
std.fatal("mutexes are broken, got {}\n", val)
;;
}
@@ -29,5 +26,5 @@ const incvar = {
val++
thread.mtxunlock(&mtx)
;;
- thread.xadd(&done, 1)
+ thread.wgpost(&done)
}
diff --git a/lib/thread/test/rwlock.myr b/lib/thread/test/rwlock.myr
new file mode 100644
index 00000000..94f69c24
--- /dev/null
+++ b/lib/thread/test/rwlock.myr
@@ -0,0 +1,50 @@
+use std
+use thread
+
+use thrtestutil
+
+const Nherd = 20
+const Nloops = 100_000
+
+var val : uint64
+var nreaders : uint32
+var nwriters : uint32
+var rw : thread.rwlock
+var done
+
+const main = {
+ rw = thread.mkrw()
+ done = thread.mkwg(Nherd)
+
+ thrtestutil.mkherd(Nherd, read)
+ thrtestutil.mkherd(Nherd, incvar)
+ thread.wgwait(&done)
+ if val != Nloops * (Nherd : uint64)
+ std.fatal("rwlocks are broken, got {}\n", val)
+ ;;
+}
+
+const incvar = {
+ for var i = 0; i < Nloops; i++
+ thread.rwwlock(&rw)
+ thread.xadd(&nwriters, 1)
+ std.assert(thread.xget(&nreaders) == 0, "incvar: rwlocks are broken\n")
+ val++
+ thread.xadd(&nwriters, -1)
+ thread.rwwunlock(&rw)
+ ;;
+ std.put("done\n")
+ thread.wgpost(&done)
+}
+
+const read = {
+ /* Linux seems to not want to end the process when there are still running threads. */
+ while thread.xget(&done._val) != 0
+ thread.rwrlock(&rw)
+ thread.xadd(&nreaders, 1)
+ std.assert(thread.xget(&nwriters) == 0, "read: rwlocks are broken\n")
+ thread.xadd(&nreaders, -1)
+ thread.rwrunlock(&rw)
+ std.usleep(1000)
+ ;;
+}
--
2.18.0
| Re: [PATCH 3/4] Add rwlocks. | Ori Bernstein <ori@xxxxxxxxxxxxxx> |
- Prev by Date: [PATCH 2/4] Fix futex timeouts and handle futex error codes.
- Next by Date: [PATCH 4/4] Only attempt to ftxwake in sempost if there might be a waiter.
- Previous by thread: [PATCH 2/4] Fix futex timeouts and handle futex error codes.
- Next by thread: Re: [PATCH 3/4] Add rwlocks.
- Index(es):