Non Blocking Channel Operations in Ruby

Basic sends and receives on channels are blocking. However, we can use select with a default clause to implement non-blocking operations, including non-blocking multi-way selects.

require 'thread'

messages = Queue.new
signals = Queue.new

# Here's a non-blocking receive. If a value is
# available on `messages` then it will be retrieved.
# If not, it will immediately take the `else` case.
if messages.length > 0
  msg = messages.pop(true)
  puts "received message #{msg}"
else
  puts "no message received"
end

# A non-blocking send works similarly. Here `msg`
# is added to the `messages` queue without blocking.
msg = "hi"
begin
  messages.push(msg, true)
  puts "sent message #{msg}"
rescue ThreadError
  puts "no message sent"
end

# We can use multiple conditions above the `else`
# clause to implement a multi-way non-blocking
# select. Here we attempt non-blocking receives
# on both `messages` and `signals`.
if messages.length > 0
  msg = messages.pop(true)
  puts "received message #{msg}"
elsif signals.length > 0
  sig = signals.pop(true)
  puts "received signal #{sig}"
else
  puts "no activity"
end

To run the program:

$ ruby non_blocking_queue_operations.rb
no message received
no message sent
no activity

In this Ruby code, we use Queue from the thread library to simulate channels. The pop(true) method attempts a non-blocking receive, while push(element, true) attempts a non-blocking send. If these operations would block, they raise a ThreadError, which we catch to implement the non-blocking behavior.

Note that Ruby doesn’t have a direct equivalent to Go’s select statement for channels. Instead, we use conditional statements (if/elsif/else) to check the state of our queues and perform the appropriate action.

This approach provides similar functionality to the original Go code, allowing for non-blocking operations on queue-like structures in Ruby.