Um… huh? Our shq stores nothing in the pdict, it uses an ets table to store values.
That is probably because in
is an async cast
, while out
is a call
that waits for a response.
The process does not really manage the data, it manages access to the data, which is stored in an ets table it owns.
Basically,the process has two integer pointers (counters), one for the front of the queue, one for the rear. The values are stored in the ets table with continuous keys between those two pointers, this way we can use a set
(O(1)
) table and don’t need ordered_set
(O(log(n)
).
As there are two updates involved in this process (counter update and insert or take), this needs to be synchronized, otherwise there are race conditions which may corrupt the queue, leading to holes and orphans.
If you do an in
, the value is stored with the current rear counter being the key, and the counter is then increased.
If you do an out
, the value at the current front counter is taken, and then the front counter is increased, then the taken value is returned.
If both counters are equal, the queue is empty.
Like this:
- after
shq:start
the queue is empty:- the data table D is empty <>
- the front counter F is 0
- the rear counter R is 0
- with an
shq:in(Shq, foo)
, this happens:- an object is inserted in the data table D, with current R as the key: <
{0, foo}
> - the front counter stays at 0
- the rear counter is increased to 1
- an object is inserted in the data table D, with current R as the key: <
- with an
shq:out(Shq)
, this happens:- the object with the current value of F is taken from the data table D, so value V is
foo
, and the data table D is empty again <> - the front counter is increased to 1
- the rear counter stays at 1
- V is returned
- the object with the current value of F is taken from the data table D, so value V is
The reverse (*_r
) functions do basically the same, but the roles of F and R are reversed in that case.
Not sure I explained that well… Anyway. The crucial point is that it is guaranteed that there are no values (rather, keys) in the data table below the front counter and also none above the rear counter (both would be inaccessible orphans, ie memory leaks), and all keys between them are continuous (so, no holes). This way, we can predict the keys with only those two counters, and don’t have to rely on the internal ordering of
ordered_set
.