bricks v0.1.0 Bricks.Socket behaviour View Source
A Socket represents a connected Socket - that is it is manufactured after a connection has been established.
As in Erlang/OTP, a Socket is owned by a single process, the owner. No other process may interact with the Socket, but the owner can hand off ownership to another process. If the owner dies, the socket will be closed.
A Socket is at any given time (as per OTP) in one of these modes:
passivemode - data can only be received when you callrecv.activemode - the socket transforms packets into messages as they arrive and transmits them to the owner.bounded activemode - likeactivemode, but caps the number of packets that will be buffered in the owner mailbox before requiring a reset by the owner.
You should not expect a Socket to be in a particular mode, rather
you should explicitly set it when you receive the Socket with
set_active/2. The default activity depends upon the type of Socket
and probably the OTP release you’re running under.
Picking a mode
passivemode: a safe default when you don’t have aggressive performance requirements. The kernel will buffer some amount of data and the rest will be rejected via (TCP) backpressure.activemode: the process mailbox becomes an effectively unlimited sized buffer. If you fail to keep up, you might buffer until the BEAM has eaten all of the memory and gets killed. But while it doesn’t, it’s low latency and high throughput. WARNING: I hope you know what you’re doing, use bounded activebounded activemode: like active mode, but caps the number of packets that will be buffered in the owner mailbox. Achieves better performance thanpassivemode but without the risks associated withactivemode.
Bounded Active Mode
In addition to taking a boolean value, set_active can take an
integer, entering what I call bounded active mode (BAM). BAM
provides most of the performance benefits of active mode with the
security of not having your BEAM get OOMkilled from overbuffering.
If passive mode doesn’t meet your needs, use BAM - avoid OOM!
In BAM, the socket behaves as in active mode, sending messages
that are received to the owner. However, it also maintains an
internal active window counter of the number of packets that may
be received in active mode, after which the socket is placed into
passive mode and a notification message is sent to the owner who can
put the socket back into BAM with set_active/2.
The bam_window field in the Socket struct holds the intended active
window. It is used by extend_active/1 to easily reset BAM.
BAM and setting active
set_active/2 can take the following values:
true- enableactivemodefalse- enablepassivemode:once- enable BAM for one packetinteger()- adjust the internal active window counter by this many, send a message to notify the owner when the socket is made passive
Note that while the first three all set the value, providing an integer can behave in two ways, depending on the current mode:
You are not in BAM, the active counter is set to this value. If it is lower than zero, the socket is made passive.
You are in BAM, the active counter is adjusted by this value (by addition). If you pass a negative number and cause the counter to go 0 or lower, the socket is made passive.
We recommend only using set_active/2 with a positive integer and
when the socket is in passive mode (such as when you have been
informed the socket has just been made passive). This simplifies the
problem and you can pretend that it does set the internal counter.
If you ignore the last paragraph, you’ll have to keep track of the
current active window to know what the new one will be after calling
set_active/2. The (undocumented) decr_active/1 function may be
useful to call when you receive a packet.
This behaviour is inherited from OTP. We would not choose to implement it this way ourselves.
Link to this section Summary
Types
The activity mode of the socket. See module documentation
The type of data received from the socket. Depends upon socket binary mode
The errors fetch_active/1 may return
The return type of fetch_active/1
The errors handoff/2 may return
The return type of handoff/2
A host to connect to (or a filepath to a unix socket)
An IPv4 or IPv6 address
An IPv4 address
An IPv6 address
The errors that new/1 may return
Options provided to new/1
A port number
The errors send_data/2 may return
The return type of send_data/2
The return type of set_active/2
A connected socket
The activity mode used for extending Bounded Active Mode
Functions
Closes the socket. Always returns :ok
Extends the bounded active mode (BAM) of a socket by its bam_window
Extends the active of a socket by the given window
Fetches the current activity of the socket
Hands a Socket off to a new process, which becomes the owner
Creates a new Socket from a map of options
Turns the socket passive, clearing any active data out of the mailbox. and returning it as one binary Note: Assumes binary mode!
Receives all available data from the socket.
Times out if the Socket’s receive_timeout is breached
Receives data from the socket
Receives data from the socket
Sends the given iolist data down the socket
Changes the socket’s activity mode. Valid activities:
true- enableactivemodefalse- enablepassivemode:once- enable BAM for one packetinteger()- adjust the internal active window counter by this many
Callbacks
Closes the socket
Fetches the active mode of the socket
Makes another process the new owner of the Socket
Receive from the Socket
Send the data down the socket
Set the activity mode of the socket
Link to this section Types
The activity mode of the socket. See module documentation.
The type of data received from the socket. Depends upon socket binary mode
fetch_active_error() :: Bricks.Error.Closed.t() | Bricks.Error.Posix.t()
The errors fetch_active/1 may return
fetch_active_return() :: {:ok, active()} | {:error, fetch_active_error()}
The return type of fetch_active/1
handoff_error() :: Bricks.Error.BadOption.t() | Bricks.Error.Closed.t() | Bricks.Error.Posix.t()
The errors handoff/2 may return
handoff_return() :: {:ok, t()} | {:error, handoff_error()}
The return type of handoff/2
A host to connect to (or a filepath to a unix socket)
An IPv4 or IPv6 address
An IPv4 address
An IPv6 address
new_error() :: Bricks.Error.BadOption.t() | Bricks.Error.UnknownOptions.t()
The errors that new/1 may return
Options provided to new/1
A port number
recv_error() :: Bricks.Error.Closed.t() | Bricks.Error.Posix.t()
recv_result() :: {:ok, data(), t()} | {:error, recv_error()}
send_error() :: Bricks.Error.Closed.t() | Bricks.Error.Posix.t()
The errors send_data/2 may return
The return type of send_data/2
set_active_return() :: {:ok, t()} | {:error, Bricks.Error.Posix.t()}
The return type of set_active/2
A connected socket
The activity mode used for extending Bounded Active Mode
Link to this section Functions
Closes the socket. Always returns :ok
Extends the bounded active mode (BAM) of a socket by its bam_window
extend_active(t(), window()) :: set_active_return()
Extends the active of a socket by the given window
Fetches the current activity of the socket
Hands a Socket off to a new process, which becomes the owner
Creates a new Socket from a map of options
Required keys:
module: callback module for the given socket tyehandle: underlying reference to the socketactive: current activity modedata_tag: tag used to identify a data message from the socketerror_tag: tag used to identify an error message from the socketclosed_tag: tag used to identify a closed message from the socketpassive_tag: tag used to identify a passive message from the socket
Optional keys:
receive_timeout: default timeout used for calls torecvbam_window: default active window to use for resetting bounded active mode
passify(t()) :: {:ok, binary(), Bricks.Socket.t()} | {:closed, binary()} | {:error, term()}
Turns the socket passive, clearing any active data out of the mailbox. and returning it as one binary Note: Assumes binary mode!
Receives all available data from the socket.
Times out if the Socket’s receive_timeout is breached.
Receives data from the socket
If the socket is not in raw mode (default) or size is zero:
Receives all available data from the socket
If the socket is in raw mode and size is not zero:
Receives exactly size bytes of data from the socket
Times out if the Socket’s receive_timeout is breached.
recv(t(), non_neg_integer(), timeout()) :: recv_result()
Receives data from the socket
- If the socket is not in raw mode (default) or
sizeis zero: Receives all available data from the socket - If the socket is in raw mode and
sizeis not zero: Receives exactlysizebytes of data from the socket
Times out if timeout is breached.
Sends the given iolist data down the socket.
There is no timeout for this operation unless one was specified during construction or set after construction.
set_active(t(), active()) :: set_active_return()
Changes the socket’s activity mode. Valid activities:
true- enableactivemodefalse- enablepassivemode:once- enable BAM for one packetinteger()- adjust the internal active window counter by this many
Note that while the first three all set the value, providing an integer can behave in two ways, depending on the current mode:
You are not in BAM, the active counter is set to this value. If it is lower than zero, the socket is made passive.
You are in BAM, the active counter is adjusted by this value (by addition). If you pass a negative number and cause the counter to go below 0, the socket is made passive.
We recommend only using set_active/2 with a positive integer and
when the socket is in passive mode (such as when you have been
informed the socket has just been made passive). This simplifies the
problem and you can pretend that it does set the internal counter.
If you ignore the last paragraph, you’ll have to keep track of the
current active window to know what the new one will be after calling
set_active/2. The (undocumented) decr_active/1 function may be
useful to call when you receive a packet. You could also use
fetch_active/1, but this will be slower.
This behaviour is inherited from OTP. We would not choose to implement it this way ourselves.
Link to this section Callbacks
Closes the socket
Fetches the active mode of the socket
Makes another process the new owner of the Socket
recv(t(), non_neg_integer(), timeout()) :: recv_result()
Receive from the Socket
Send the data down the socket
set_active(t(), active()) :: set_active_return()
Set the activity mode of the socket