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.