class Hamster::LazyList
@private
construct infinite ‘List`s.
By returning a `Cons` that in turn has a {LazyList} as its tail, one can
only called) when one of these operations is performed.
to `#head`, `#tail` and `#empty?`. The list is only realized (i.e. the block is
A `LazyList` takes a block that returns a `List`, i.e. an object that responds
def cached_size?
def cached_size? @size != nil end
def empty?
def empty? realize if @atomic.get != 2 @size == 0 end
def head
def head realize if @atomic.get != 2 @head end
def initialize(&block)
def initialize(&block) @head = block # doubles as storage for block while yet unrealized @tail = nil @atomic = Concurrent::AtomicReference.new(0) # haven't yet run block @size = nil end
def realize
def realize while true # try to "claim" the right to run the block which realizes target if @atomic.compare_and_swap(0,1) # full memory barrier here begin list = @head.call if list.empty? @head, @tail, @size = nil, self, 0 else @head, @tail = list.head, list.tail end rescue @atomic.set(0) MUTEX.synchronize { QUEUE.broadcast } raise end @atomic.set(2) MUTEX.synchronize { QUEUE.broadcast } return end # we failed to "claim" it, another thread must be running it if @atomic.get == 1 # another thread is running the block MUTEX.synchronize do # check value of @atomic again, in case another thread already changed it # *and* went past the call to QUEUE.broadcast before we got here QUEUE.wait(MUTEX) if @atomic.get == 1 end elsif @atomic.get == 2 # another thread finished the block return end end end
def size
def size @size ||= super end
def tail
def tail realize if @atomic.get != 2 @tail end