CSPEx v1.1.0 CSP.Channel
Module used to create and manage channels.
Options
There are some options that may be used to change a channel behavior, but the channel's options can only be set during it's creation.
The available options are:
name
- Registers the channel proccess with a name. Note that the naming constraints are the same applied to aGenServer
.buffer_type
- The type of the buffer used in the channel (default::blocking
).buffer_size
- The maximum capacity of the channel's buffer (default:0
).
Buffer types
There are three suported buffer types:
:blocking
- The channel never blocks until the buffer capacity is full.:sliding
- The channel never blocks, but when the buffer is full, it starts discarding the older values on it to make room for the newer ones.:dropping
- The channel never blocks, but when the buffer is full, it starts discarding any new values that are put on it, keeping the old ones.
Collections interoperability
You can use a channel just like any collection:
channel = Channel.new
pid = spawn_link(fn -> Enum.into([:some, :data], channel) end)
Process.alive?(pid) #=> true
Channel.get(channel) #=> :some
Process.alive?(pid) #=> true
Channel.get(channel) #=> :data
Process.alive?(pid) #=> false
All functions from Enum
and Stream
are available, but you must take into
consideration the blocking operations:
channel = Channel.new
# This line will block until someone reads all the ten values.
Enum.into(1..10, channel)
# This line will block until someone puts at least 4 values on the channel.
# (More if there are more listeners on the same channel)
Enum.take(channel, 4)
# This line will block until someone closes the channel.
Enum.into(channel, [])
Example
You can use a channel in a supervision tree:
import Supervisor.Spec
children = [
worker(Channel, [[name: MyApp.Channel, buffer_size: 10]])
]
{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)
# You can use all the functions with the registered name instead
# of the channel struct
Channel.put(MyApp.Channel, :data)
Channel.put(MyApp.Channel, :other)
Channel.get(MyApp.Channel) #=> :data
Channel.get(MyApp.Channel) #=> :other
# If you want to use it as a collection just call Channel.wrap/1
channel = Channel.wrap(MyApp.Channel)
Enum.into(1..5, channel)
Enum.count(channel) #=> 5
You can use channels in any part of list comprehensions:
channel = Enum.into(1..5, Channel.new(buffer_size: 5))
:ok = Channel.close(channel)
other_channel = for x <- channel, into: Channel.new(buffer_size: 5) do
x * 2
end
:ok = Channel.close(other_channel)
Enum.to_list(other_channel) #=> [2, 4, 6, 8, 10]
Summary
Functions
Function responsible for closing a channel
Returns true
if the channel is closed or false
otherwise
Function responsible for fetching a value of the channel
Returns true
or false
wheter the value is present on the channel
Function responsible for creating a new channel
Function responsible for putting a value in the channel
Returns the current size of the channel
Non-linking version of CSP.Channel.start_link/1
Function responsible for the starting of the channel
Wraps the PID or registered name in a Channel struct
Types
buffer_type :: :blocking | :sliding | :dropping
channel_ref :: term | t
option ::
{:buffer_size, non_neg_integer} |
{:buffer_type, buffer_type} |
{:name, GenServer.name}
t :: %CSP.Channel{ref: term}
Functions
Specs
close(channel_ref) :: :ok
Function responsible for closing a channel.
Example
iex> channel = Channel.new
iex> Channel.closed?(channel)
false
iex> Channel.close(channel)
iex> Channel.closed?(channel)
true
Specs
closed?(channel_ref) :: boolean
Returns true
if the channel is closed or false
otherwise.
Specs
get(channel_ref) :: term
Function responsible for fetching a value of the channel.
It will block until a value is inserted in the channel or it is closed.
Always returns nil
when the channel is closed.
Example
iex> channel = Channel.new
iex> spawn_link(fn -> Channel.put(channel, :data) end)
iex> Channel.get(channel)
:data
iex> Channel.close(channel)
iex> Channel.get(channel)
nil
Specs
member?(channel_ref, term) :: boolean
Returns true
or false
wheter the value is present on the channel.
Function responsible for creating a new channel.
Useful for using channels outside of a supervision tree.
Example
iex> channel = Channel.new
iex> spawn_link(fn -> Channel.put(channel, :data) end)
iex> Channel.get(channel)
:data
Specs
put(channel_ref, term) :: :ok
Function responsible for putting a value in the channel.
It may block until a value is fetched deppending on the buffer type of the channel.
Raises if trying to put nil
or if trying to put anything in a closed channel.
Example
iex> channel = Channel.new(buffer_size: 5)
iex> Channel.put(channel, :data)
iex> Channel.put(channel, :other)
iex> Channel.close(channel)
iex> Enum.to_list(channel)
[:data, :other]
Specs
size(channel_ref) :: non_neg_integer
Returns the current size of the channel.
Remember that the size of the channel is the number of items in the buffer plus the number of pending "put" operations.
Example
iex> channel = Enum.into(1..3, Channel.new(buffer_size: 4))
iex> Channel.size(channel)
3
iex> Channel.put(channel, 4)
iex> Channel.size(channel)
4
iex> spawn_link(fn -> Channel.put(channel, 5) end)
iex> :timer.sleep(10)
iex> Channel.size(channel)
5 # 4 items in the full buffer plus one pending "put"
Specs
start(options) :: GenServer.on_start
Non-linking version of CSP.Channel.start_link/1
Specs
start_link(options) :: GenServer.on_start
Function responsible for the starting of the channel.
Ideal for using a CSP in a supervision tree.
Specs
wrap(channel_ref) :: t
Wraps the PID or registered name in a Channel struct.
If the passed in value is already a Channel struct, return it unchanged.
Example
iex> {:ok, pid} = Channel.start_link(buffer_size: 5)
iex> channel = Channel.wrap(pid)
iex> Enum.into(1..5, channel)
iex> Channel.close(channel)
iex> Enum.to_list(channel)
[1, 2, 3, 4, 5]
iex> channel = Channel.new
iex> channel == Channel.wrap(channel)
true