From: Kero van Gelder Date: 2001-09-25T03:35:19+09:00 Subject: [ruby-talk:21643] Sequentializing input (and output/error) streams --9amGYk9869ThD9tj Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hello all, In previous discussions, I got a feeling people like to take input from different streams (Socket, stdin, ...) one by one. I've made a simple, non-fair module Serialize that does just that. It is attached, plus a zipfile in which rbtk/rbtk.rb demonstrates the use of Serialize. It uses a file descriptor associated with an X display (i.e. a network connection to an X server). I'd like comments, then I'll put it in RAA. (Or should it be in Ruby itself? It gives other parts/extensions the chance to cooperate, e.g. Tk.mainloop doesn't care about sockets and vice versa, but that can be undesirable.) Bye, Kero. +--- Kero ------------------------------ kero@chello.nl ---+ | Don't split your mentality without thinking twice | | Proud like a God -- Guano Apes | +--- M38c ------- http://members.chello.nl/~k.vangelder ---+ --9amGYk9869ThD9tj Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="serialize.rb" # Author: Kero van Gelder # Copyright: Kero van Gelder (the GNU Lesser General Public License applies) # Since: 24 Sep 2001 module Serialize @inputs = [] @outputs = [] @errors = [] @bindings = {} =begin == Serialize::(un)set_input/output/error (De)Associated a block with an input/output/error file descriptor, to be serialized with other input/output/error fd's, i.e. the blocks associated with those fd's are never executed simultaneously. The block must be given and you must ensure the block will let the associated stream be saturated, that is, input must be consumed, output buffers must be filled. (Otherwise the loop in Serialize::serialize will never block anymore, though it will not prevent other blocks from being called.) =end def Serialize::set_input(input, &block) raise "Must provide a block" unless block @inputs << input unless @bindings.fetch(input, nil) @bindings.store(input, block) end def Serialize::set_output(output, &block) raise "Must provide a block" unless block @outputs << output unless @bindings.fetch(output, nil) @bindings.store(output, block) end def Serialize::set_error(error, &block) raise "Must provide a block" unless block @errors << error unless @bindings.fetch(error, nil) @bindings.store(error, block) end def Serialize::unset_input(input) @inputs.delete(input) @bindings.delete(input) end def Serialize::unset_output(output) @outputs.delete(output) @bindings.delete(output) end def Serialize::unset_error(error) @errors.delete(error) @bindings.delete(error) end =begin == Serialize::serialize Start serializing events. It is suggested that once this loop is started, you only set and unset file descriptors in the blocks invoked by this thread, not other threads. =end def Serialize::serialize() loop { break if @inputs.empty? and @outputs.empty? and @errors.empty? available = select(@inputs, @outputs, @errors, nil).flatten available.each { |fd| @bindings.fetch(fd, nil).call } } puts "Serialize::serialize() ended (no fd's left)" if $DEBUG end end --9amGYk9869ThD9tj Content-Type: application/zip Content-Disposition: attachment; filename="rbtk.zip" Content-Transfer-Encoding: base64 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 ANICAAB3HgAAAAA= --9amGYk9869ThD9tj--