# Coroutines for Ruby # # Copyright (C) 2001 Marc De Scheemaecker, All Rights Reserved. # # This software is provided 'as-is', without any express or implied warranty. # In no event will the authors be held liable for any damages arising from the # use of this software. # # Permission is granted to anyone to use this software for any purpose, # including commercial applications, and to alter it and redistribute it # freely, subject to the following restrictions: # # 1. The origin of this software must not be misrepresented; you must not # claim that you wrote the original software. If you use this software in a # product, an acknowledgment in the product documentation would be # appreciated but is not required. # # 2. Altered source versions must be plainly marked as such, and must not be # misrepresented as being the original software. # # 3. This notice may not be removed or altered from any source distribution. class Coroutine # Coroutines allow blocks to run concurrently while you control when the # context is switched between them. # # Say you want to execute two blocks, like this: # # block1 block2 # block1.start # *** block1 runs # --------------> block1.switch(block2) # *** block2 runs # <-------------- block2.switch(block1) # *** block1 runs # --------------> block1.switch(block2) # *** block2 runs def initialize(&block) # Creates a coroutine. The associated block does not run yet. @started = false @finished = false @block = Proc::new { callcc{|@cc|} block.call @finished = true @started = false } end def start # Starts the block. It's an error to call this method on a coroutine # that has already been started. raise "Block already started" if @started @started = true @block.call end def switch(coroutine) # Switches context to another coroutine. You need to call this method # on the current coroutine. switch = true if not coroutine.finished? then callcc{|@cc|} if switch then switch = false if coroutine.running? then coroutine.continuation.call else coroutine.start end end end end def running? # Returns true if the associated block is started and has not yet # finished @started and not @finished end def finished? # Returns true if the associated block is finished @finished end def continuation # Returns the associated continuation object @cc end end # This example creates two coroutines. The first one prints all letters; the # second one prints 26 numbers. The context between the two coroutines switches # whenever a letter or digit is printed. if __FILE__ == $0 then $c1 = Coroutine::new do for i in 'a'..'z' do printf "%s ", i $c1.switch($c2) end end $c2 = Coroutine::new do for i in 1..26 do printf "%i ", i $c2.switch($c1) end end $c1.start printf "\n" end