Current section
Files
Jump to
Current section
Files
lib/misc/edeque.ex
defmodule :edeque do
@moduledoc false
@doc """
A concurrent deque implemented using ETS and counters.
Usage:
{ets, c} = :edeque.new()
:edeque.push(ets, c, "London")
:edeque.push(ets, c, "New York")
:edeque.push(ets, c, "Hong Kong")
:edeque.push_front(ets, c, "Kyoto")
:edeque.put(ets, c, 0, "Tokyo")
:edeque.stream(ets) |> Enum.to_list() |> IO.inspect()
:edeque.destroy(ets)
"""
def new do
ets = new_table()
c = new_counters()
{ets, c}
end
def new(name) do
ets = new_table(name)
c = new_counters()
{ets, c}
end
def new_counters do
:counters.new(3, [:write_concurrency])
end
def new_table do
:ets.new(:deque, [:ordered_set, :public])
end
def new_table(name) do
ets =
:ets.new(name, [
:ordered_set,
:public,
:named_table,
read_concurrency: true,
write_concurrency: true
])
c = :counters.new(3, [:write_concurrency])
{ets, c}
end
def push(ets, c, value) do
:counters.add(c, 2, 1)
tail = :counters.get(c, 2)
:counters.add(c, 3, 1)
:ets.insert(ets, {tail, value})
end
def push_front(ets, c, value) do
:counters.sub(c, 1, 1)
head = :counters.get(c, 1)
:counters.add(c, 3, 1)
:ets.insert(ets, {head, value})
end
def put(ets, c, index, value) do
ix = :counters.get(c, 1) + index
:ets.insert(ets, {ix, value})
end
def foldl(ets, acc, fun) do
:ets.foldl(fn {_k, v}, acc -> fun.(v, acc) end, acc, ets)
end
def foldr(ets, acc, fun) do
:ets.foldr(fn {_k, v}, acc -> fun.(v, acc) end, acc, ets)
end
@doc """
Returns a stream of values in the deque
"""
def stream(ets) do
Stream.resource(
fn -> :ets.first(ets) end,
fn
:"$end_of_table" ->
{:halt, nil}
key ->
case :ets.lookup(ets, key) do
[{^key, value}] ->
next = :ets.next(ets, key)
{[value], next}
[] ->
next = :ets.next(ets, key)
{[], next}
end
end,
fn _ -> :ok end
)
end
def size(c) do
:counters.get(c, 3)
end
def flush(ets, c) do
:ets.delete_all_objects(ets)
:counters.put(c, 1, 0)
:counters.put(c, 2, 0)
:counters.put(c, 3, 0)
end
def reset_counters(c) do
:counters.put(c, 1, 0)
:counters.put(c, 2, 0)
:counters.put(c, 3, 0)
end
def destroy(ets) do
:ets.delete(ets)
end
end