Non Blocking Channel Operations in Elixir

Basic sends and receives on channels are blocking in Elixir. However, we can use receive with a after clause to implement non-blocking receives, and send with Process.info(self(), :message_queue_len) to implement non-blocking sends.

defmodule NonBlockingChannelOperations do
  def run do
    messages_pid = spawn(fn -> receive_loop() end)
    signals_pid = spawn(fn -> receive_loop() end)

    # Here's a non-blocking receive. If a value is
    # available on `messages_pid` then `receive` will take
    # that value. If not, it will immediately take the `after` clause.
    receive do
      {:message, msg} ->
        IO.puts("received message #{msg}")
    after
      0 -> IO.puts("no message received")
    end

    # A non-blocking send works similarly. Here `msg`
    # is sent to the `messages_pid` process, and we check
    # if the message was sent by examining the process mailbox.
    msg = "hi"
    send(messages_pid, {:message, msg})
    if Process.info(messages_pid, :message_queue_len) == {:message_queue_len, 1} do
      IO.puts("sent message #{msg}")
    else
      IO.puts("no message sent")
    end

    # We can use multiple patterns above the `after` clause
    # to implement a multi-way non-blocking receive. Here we
    # attempt non-blocking receives on both `messages_pid` and `signals_pid`.
    receive do
      {:message, msg} ->
        IO.puts("received message #{msg}")
      {:signal, sig} ->
        IO.puts("received signal #{sig}")
    after
      0 -> IO.puts("no activity")
    end
  end

  defp receive_loop do
    receive do
      _ -> receive_loop()
    end
  end
end

NonBlockingChannelOperations.run()

To run the program:

$ elixir non_blocking_channel_operations.exs
no message received
no message sent
no activity

In this Elixir version, we use processes to simulate channels. The spawn function creates new processes that run the receive_loop function, which keeps the processes alive and ready to receive messages.

We use receive with an after 0 clause to implement non-blocking receives. For non-blocking sends, we send the message and then check the process mailbox length to determine if the message was sent successfully.

The multi-way non-blocking select is implemented using multiple patterns in the receive block, followed by an after 0 clause for the default case.

Note that Elixir’s concurrency model is based on the actor model, which is somewhat different from Go’s channel-based concurrency. However, this example demonstrates how to achieve similar non-blocking operations in Elixir.