diff options
author | Ori Bernstein <ori@eigenstate.org> | 2015-09-18 19:34:46 -0700 |
---|---|---|
committer | Ori Bernstein <ori@eigenstate.org> | 2015-09-18 19:34:46 -0700 |
commit | b92c74cab80c4c1fa912d76422d5c83fcad308de (patch) | |
tree | 8c17394c39386d85de4ec491aafabf095634237a /lib/thread | |
parent | 1b5b7ee4b7a10b39835369ac12462ef2231b6f2b (diff) | |
download | mc-b92c74cab80c4c1fa912d76422d5c83fcad308de.tar.gz |
Add futex-based future implementation.
Diffstat (limited to 'lib/thread')
-rw-r--r-- | lib/thread/bld.proj | 3 | ||||
-rw-r--r-- | lib/thread/common.myr | 5 | ||||
-rw-r--r-- | lib/thread/condvar+linux.myr | 3 | ||||
-rw-r--r-- | lib/thread/future+linux.myr | 48 | ||||
-rw-r--r-- | lib/thread/future.myr | 63 | ||||
-rw-r--r-- | lib/thread/mutex+linux.myr | 2 | ||||
-rw-r--r-- | lib/thread/test/future.myr | 51 |
7 files changed, 171 insertions, 4 deletions
diff --git a/lib/thread/bld.proj b/lib/thread/bld.proj index 84295f3..2fd110b 100644 --- a/lib/thread/bld.proj +++ b/lib/thread/bld.proj @@ -1,10 +1,11 @@ lib thread = - future.myr + common.myr # linux impl of basic thread primitives condvar+linux.myr mutex+linux.myr spawn+linux.myr + future+linux.myr atomic-impl+x64.s atomic.myr diff --git a/lib/thread/common.myr b/lib/thread/common.myr new file mode 100644 index 0000000..b88a480 --- /dev/null +++ b/lib/thread/common.myr @@ -0,0 +1,5 @@ +use std + +pkg thread = + generic Zptr = 0 castto(@a#) +;; diff --git a/lib/thread/condvar+linux.myr b/lib/thread/condvar+linux.myr index fe00408..2d9aba3 100644 --- a/lib/thread/condvar+linux.myr +++ b/lib/thread/condvar+linux.myr @@ -2,6 +2,7 @@ use std use sys use "atomic.use" +use "common.use" use "mutex.use" pkg thread = @@ -16,8 +17,6 @@ pkg thread = const condbroadcast : (cond : cond# -> void) ;; -generic Zptr = 0 castto(@a#) - const mkcond = {mtx -> [._mtx = mtx, ._seq = 0] } diff --git a/lib/thread/future+linux.myr b/lib/thread/future+linux.myr new file mode 100644 index 0000000..1977026 --- /dev/null +++ b/lib/thread/future+linux.myr @@ -0,0 +1,48 @@ +use std +use sys + +use "atomic.use" +use "common.use" + +pkg thread = + type future(@a) = struct + _state : int32 + _val : @a + ;; + + generic mkfut : (-> future(@a)) + generic futset : (fut : future(@a)#, val : @a -> bool) + generic futget : (fut : future(@a)# -> @a) + generic futtryget : (fut : future(@a)# -> std.option(@a)) +;; + +const Clear = 0 +const Setting = 1 +const Set = 2 + +generic mkfut = { + -> [._state = Clear ] +} + +generic futset = {fut, val + /* If we don't get a clear, we failed to set the value */ + if xcas(&fut._state, Clear, Setting) != Clear + -> false + ;; + fut._val = val + fut._state = Set + sys.futex(&fut._state, sys.Futexwake | sys.Futexpriv, 0x7fffffff, Zptr, Zptr, 0) + -> true +} + +generic futget = {fut + var st + + /* we can transition from Set to Setting. */ + st = fut._state + while st != Set + sys.futex(&fut._state, sys.Futexwait | sys.Futexpriv, st, Zptr, Zptr, 0) + st = fut._state + ;; + -> fut._val +} diff --git a/lib/thread/future.myr b/lib/thread/future.myr new file mode 100644 index 0000000..da6252b --- /dev/null +++ b/lib/thread/future.myr @@ -0,0 +1,63 @@ +use std + +use "mutex.use" + +pkg thread = + type future(@a) = struct + mtx : mutex + set : bool + val : @a + ;; + + generic mkfut : (-> future(@a)) + generic futset : (fut : future(@a)#, val : @a -> bool) + generic futget : (fut : future(@a)# -> @a) + generic futtryget : (fut : future(@a)# -> std.option(@a)) + generic futclear : (fut : future(@a)# -> void) +;; + +const Unset = 0 +const Waiting = 1 +const Set = 2 + +generic mkfut = { + var fut + + fut = [.mtx = mkmtx() ] + mtxlock(&fut.mtx) + -> fut +} + +generic futset = {fut, val + if fut.set + -> false + ;; + /* compiler doesn't reorder shit */ + fut.val = val + fut.set = true + mtxunlock(&fut.mtx) + -> true +} + +generic futtryget = {fut + var val + + if !fut.set + -> `std.None + ;; + mtxlock(&fut.mtx) + val = fut.val + mtxunlock(&fut.mtx) + -> `std.Some val +} + +generic futget = {fut + var val + + mtxlock(&fut.mtx) + val = fut.val + mtxunlock(&fut.mtx) + -> val +} + + diff --git a/lib/thread/mutex+linux.myr b/lib/thread/mutex+linux.myr index 6b3818d..42f9fac 100644 --- a/lib/thread/mutex+linux.myr +++ b/lib/thread/mutex+linux.myr @@ -2,6 +2,7 @@ use std use sys use "atomic.use" +use "common.use" pkg thread = type mutex = struct @@ -18,7 +19,6 @@ pkg thread = pkglocal const Contended = 2 ;; -generic Zptr = 0 castto(@a#) var nspin = 10 /* FIXME: pick a sane number, based on CPU count */ const mkmtx = { diff --git a/lib/thread/test/future.myr b/lib/thread/test/future.myr new file mode 100644 index 0000000..76710c7 --- /dev/null +++ b/lib/thread/test/future.myr @@ -0,0 +1,51 @@ +use std +use sys +use thread + +use "test/util.use" + +var fut +var nready : int32 +var ndone : int32 + +const main = { + nready = 0 + ndone = 0 + fut = thread.mkfut() + /* set after we have some waiters */ + mkherd(100, getfuture) + while nready != 100 + /* spin */ + ;; + std.put("done waiting for ready\n") + std.assert(ndone == 0, "thread proceeded too soon\n") + thread.futset(&fut, 666) + std.assert(thread.futset(&fut, 1) == false, "double set future\n") + while ndone != 100 + std.put("ndone: {}\n", ndone) + /* spin */ + ;; + std.put("double set future ok") + /* start up a few more to make sure we can still read */ + mkherd(50, getfuture) + while ndone != 150 + /* spin */ + ;; + + + /* set ahead of time */ + ndone = 0 + fut = thread.mkfut() + thread.futset(&fut, 666) + std.assert(thread.futset(&fut, 666) == false, "double set future\n") + mkherd(100, getfuture) + while ndone != 100 + /* spin */ + ;; +} + +const getfuture = { + thread.xadd(&nready, 1) + std.assert(thread.futget(&fut) == 666, "wrong value gotten from future") + thread.xadd(&ndone, 1) +} |