Here’s my solution. Its “processes” are just plain threads. It works
pretty well, but it hangs on rings of size 1.
$processes = []
def kill_processes
$processes.each do |thr|
thr.exit!
end
end
thread_proc = proc do
Thread.stop
if Thread.current[:count] == 0
Thread.current[:next] = $processes.first
else
Thread.current[:next] = Thread.new(&thread_proc)
Thread.current[:next][:count] = Thread.current[:count] - 1
$processes.push(Thread.current[:next])
true until Thread.current[:next].stop?
Thread.current[:next].run
end
while true
Thread.stop
msg = Thread.current[:message]
cnt = Thread.current[:message_count]
Thread.current[:message] = nil
Thread.current[:message_count] = nil
if cnt == 0
kill_processes
else
Thread.current[:next][:message_count] = cnt - 1
Thread.current[:next][:message] = msg
#On small rings, the message can circle around before the first
thread has stopped
true until Thread.current[:next].stop?
Thread.current[:next].run
end
end
end
processes, cycles = ARGV.map{|n| n.chomp.to_i}
$processes.push(Thread.new(&thread_proc))
true until $processes.first.stop?
$processes.first[:count] = processes - 1
$processes.first.run
puts “Creating #{processes} processes…”
sleep(0.1) until $processes.length == processes
puts “Timer started.”
start_time = Time.new
puts “Sending a message around the ring #{cycles} times…”
$processes.first[:message_count] = processes * cycles
$processes.first[:message] = “Good day!”
$processes.first.run
sleep(0.1) while $processes.first.alive?
puts “Done.”
puts "Time in seconds: " + (Time.new.to_i - start_time.to_i).to_s
----- Original Message ----
From: Ruby Q. [email protected]
To: ruby-talk ML [email protected]
Sent: Friday, August 17, 2007 8:15:37 AM
Subject: [QUIZ] Process Rings (#135)
The three rules of Ruby Q.:
-
Please do not post any solutions or spoiler discussion for this quiz
until
48 hours have passed from the time on this message. -
Support Ruby Q. by submitting ideas as often as you can:
- Enjoy!
Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone
on Ruby T. follow the discussion. Please reply to the original quiz
message,
if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
I recently wrote about a challenge in the Programming Erlang book on my
blog:
http://blog.grayproductions.net/articles/2007/08/13/erlang-message-passing
Language comparison issues aside, just figuring out how to build a ring
of
“processes” was quite the brain bender for me. That always makes for
good Ruby
Quiz material, in my opinion.
The task is straight forward:
1. Your program should take two command-line arguments: a number of
processes and a number of cycles.
2. Begin by creating the requested number of processes, in a ring.
For example, when three processes are requested, process one
creates and sends messages to process two, which creates and
sends
messages to process three. The third process then sends its
messages back to process one.
3. Pass a message around your ring of processes a number of times
equal to the requested cycles. Print timing results for how
long this takes.
The message you pass doesn’t much matter. A simple String is fine. You
may
also wish to pass a counter with it, to verify the correct number of
sends.
I’ll leave the definition of “processes” intentionally vague. Ruby
doesn’t have
an equivalent to Erlang processes so we will just say that each process
should
represent a node where we could run some instructions concurrently. Be
creative.