val avg = (prices: Events[Double])
.scanPast((initialPrice, 1))((a, x) => (a._1 + x, a._2 + 1))
.map(a => a._1 / a._2)
val avg = (prices: Events[Double])
.scanPast((initialPrice, 1))((a, x) => (a._1 + x, a._2 + 1))
.map(a => a._1 / a._2)
val avg = prices.emit {
var (sum, num) = (initialPrice, 1)
(emitter, x) => {
sum += x
num += 1
emitter.react(sum / num)
}
}
Fuse the code and concatenate the intermediate queues.
From the JDK Lock-Free Concurrent Linked Queue JavaDoc:
Iterators are weakly consistent, returning elements reflecting the state of the queue at some point at or since the creation of the iterator.
Consider a data structure with following properties:
head
- position of the first element in the queuelast
- position less than or equal to the first empty entrynull
, then some element, and finally the special value REMOVED
head
increase monotonically from 0 until the array lengthlast
eventually points to the first empty entry (eventually fast)Support data structure is any immutable data structure into which we can push single-shot queues.
The support data structure must be a sequence, i.e. it must retain order.
Dequeue must occasionally steal the support data structure from the enqueue (right) side.
Left support structure non-empty, no need to steal.
What about snapshots?
It's hard to get a consistent view of a fast-moving object...
...unless you can freeze it.
How to atomically prevent subsequent enqueue and dequeue operations?
The freeze operation is as good as a snapshot.
After a freeze, the previously mutable data structure becomes an immutable data structure.
If we can find an immutable data structure that:
Then we have an efficient lock-free concatenation operation.
Conc-Trees as the support data structure:
More information in the paper.