117 lines
3.1 KiB
Factor
117 lines
3.1 KiB
Factor
! Copyright (C) 2008 Slava Pestov.
|
|
! See http://factorcode.org/license.txt for BSD license.
|
|
USING: deques dlists kernel threads continuations math
|
|
concurrency.conditions combinators.short-circuit accessors
|
|
locals ;
|
|
IN: concurrency.locks
|
|
|
|
! Simple critical sections
|
|
TUPLE: lock threads owner reentrant? ;
|
|
|
|
: <lock> ( -- lock )
|
|
<dlist> f f lock boa ;
|
|
|
|
: <reentrant-lock> ( -- lock )
|
|
<dlist> f t lock boa ;
|
|
|
|
<PRIVATE
|
|
|
|
: acquire-lock ( lock timeout -- )
|
|
over owner>>
|
|
[ 2dup [ threads>> ] dip "lock" wait ] when drop
|
|
self >>owner drop ;
|
|
|
|
: release-lock ( lock -- )
|
|
f >>owner
|
|
threads>> notify-1 ;
|
|
|
|
:: do-lock ( lock timeout quot acquire release -- )
|
|
lock timeout acquire call
|
|
quot lock release curry [ ] cleanup ; inline
|
|
|
|
: (with-lock) ( lock timeout quot -- )
|
|
[ acquire-lock ] [ release-lock ] do-lock ; inline
|
|
|
|
PRIVATE>
|
|
|
|
: with-lock-timeout ( lock timeout quot -- )
|
|
pick reentrant?>> [
|
|
pick owner>> self eq? [
|
|
2nip call
|
|
] [
|
|
(with-lock)
|
|
] if
|
|
] [
|
|
(with-lock)
|
|
] if ; inline
|
|
|
|
: with-lock ( lock quot -- )
|
|
f swap with-lock-timeout ; inline
|
|
|
|
! Many-reader/single-writer locks
|
|
TUPLE: rw-lock readers writers reader# writer ;
|
|
|
|
: <rw-lock> ( -- lock )
|
|
<dlist> <dlist> 0 f rw-lock boa ;
|
|
|
|
<PRIVATE
|
|
|
|
: add-reader ( lock -- )
|
|
[ 1+ ] change-reader# drop ;
|
|
|
|
: acquire-read-lock ( lock timeout -- )
|
|
over writer>>
|
|
[ 2dup [ readers>> ] dip "read lock" wait ] when drop
|
|
add-reader ;
|
|
|
|
: notify-writer ( lock -- )
|
|
writers>> notify-1 ;
|
|
|
|
: remove-reader ( lock -- )
|
|
[ 1- ] change-reader# drop ;
|
|
|
|
: release-read-lock ( lock -- )
|
|
dup remove-reader
|
|
dup reader#>> zero? [ notify-writer ] [ drop ] if ;
|
|
|
|
: acquire-write-lock ( lock timeout -- )
|
|
over writer>> pick reader#>> 0 > or
|
|
[ 2dup [ writers>> ] dip "write lock" wait ] when drop
|
|
self >>writer drop ;
|
|
|
|
: release-write-lock ( lock -- )
|
|
f >>writer
|
|
dup readers>> deque-empty?
|
|
[ notify-writer ] [ readers>> notify-all ] if ;
|
|
|
|
: reentrant-read-lock-ok? ( lock -- ? )
|
|
#! If we already have a write lock, then we can grab a read
|
|
#! lock too.
|
|
writer>> self eq? ;
|
|
|
|
: reentrant-write-lock-ok? ( lock -- ? )
|
|
#! The only case where we have a writer and > 1 reader is
|
|
#! write -> read re-entrancy, and in this case we prohibit
|
|
#! a further write -> read -> write re-entrancy.
|
|
{ [ writer>> self eq? ] [ reader#>> zero? ] } 1&& ;
|
|
|
|
PRIVATE>
|
|
|
|
: with-read-lock-timeout ( lock timeout quot -- )
|
|
pick reentrant-read-lock-ok? [
|
|
[ drop add-reader ] [ remove-reader ] do-lock
|
|
] [
|
|
[ acquire-read-lock ] [ release-read-lock ] do-lock
|
|
] if ; inline
|
|
|
|
: with-read-lock ( lock quot -- )
|
|
f swap with-read-lock-timeout ; inline
|
|
|
|
: with-write-lock-timeout ( lock timeout quot -- )
|
|
pick reentrant-write-lock-ok? [ 2nip call ] [
|
|
[ acquire-write-lock ] [ release-write-lock ] do-lock
|
|
] if ; inline
|
|
|
|
: with-write-lock ( lock quot -- )
|
|
f swap with-write-lock-timeout ; inline
|