GenLoop
This library is an adaptation of awesome Ulf Wiger's library :plain_fsm for
Elixir. It reuses as :plain_fsm code as possible, but adds some features :
-
OTP system behaviours for starting processes, stopping processes and name
registering. That means that you can use the classic naming conventions as in
GenServer:name = atom_key name = {:global, key} name = {:via, Registry, {AppRegistry, key}} GenLoop.start_link(module, args, name: name) GenLoop.send(name, message) GenLoop.stop(name) receive/2macro inspired from ashneyderman/plain_fsm_ex that automatically handles system messages. It handles parent:EXITmessages if your process traps exits too.
This is still a work in progress, notably the documentation must be completed.
Installation
The package can be installed by adding gen_loop and plain_fsm to your list
of dependencies in mix.exs: We need to add plain_fsm because it is old on
Hex repository, we use a more up-to-date version of the library (mainly to
handle terminate/2 callback).
def deps do
[
{:gen_loop, "~> 0.1.0"},
{:plain_fsm, github: "uwiger/plain_fsm", commit: "ae9eca8a8df8f61a32185b06882a55d60e62e904"},
]
endWhy ?
This library is a direct concurrent to GenServer and GenFsm : it provides selective receive and more freedom but makes it easier to shoot yourself in the foot.
More info in plain_fsm rationale.
How To ?
This section is still to be done, but basically :
-
First,
use GenLoop, enter: :my_loopin your module, where:my_loopis the name of a function in your module. -
Call
GenLoop.start_link(__MODULE__, args), you can also give options likename. -
Maybe
def init(args_list_from_start_link)function that will return as in GenServer behaviour :{:ok, state}. -
Define your
my_loop(state_from_init)function where your code now runs in a supervised process. -
Use the
receive/2macro in your main state function (classicreceive/1is fine in transient states):def my_loop(state) my_state = change_stuf(state) receive my_state do # Pass the state to use the macro rcall(from, msg) -> # Match a message from GenLoop.call/2 reply(from, :ok) # Reply with GenLoop.reply (automatically imported) my_loop(state) # Don't forget to re-enter the loop rcast(msg) -> # Match a message from GenLoop.cast state = do_stuff(msg) my_loop(state) msg -> # Match a mere message from Kernel.send/2 or GenLoop.send/2 state = do_stuff(msg) other_loop(state) # You can go to another loop to change state after 1000 -> my_loop(state) end # You must not have any code after receive. end receivemust be the last expression in the function.
Have a look at loop_example.ex.
Alternative
GenLoop is designed for communicating processes : servers, FSMs, etc. Have a look at the Task module if you just want to supervise autonomous processes.
GenLoop is not a replacement for GenServer : if your have only one loop in your module with a "catch all messages" clauses, you woud better use GenServer instead of GenLoop.