On Tue, Sep 21, 2004 at 02:38:55AM +0900, Bill Kelly wrote: > Hi, > > From: "Brian Schr?der" <ruby / brian-schroeder.de> > > > in the program only one thread at a time is reading, and only one at a > > time is writing. I wanted to know if I could read and write to the same > > socket from two different threads simultaneously. And I think this is a > > subset of the answer, so I take it for "yes". > > I didn't know if two threads simultaneously accessing the same > socket was legal I believe it is, if you think about how Ruby implements threads internally. It doesn't use any O/S threading at all (it even works under MS-DOS, not that I've tried it myself :-) Rather, the Ruby interpreter chugs along the annotated syntax tree, changes to another Thread, chugs along another big of syntax tree, and so on. For threads which are blocked on I/O, it uses select() to determine when they are ready to be scheduled again. > http://bwk.homeip.net/ftp/dorkbuster/wallfly/buffered-io.rb > > The above is an IO class whose read thread just slurps data > as fast as it can in the background. And whose write thread > writes data out, similarly, when it can. So the code interfacing > with this class gets a non-blocking read & write, with infinite > buffer size. (Up to available RAM of course.) For my purposes > it has been convenient... dunno if it would be useful to anyone > else. If so I could put it on RAA (?) You stuff data down one socket and read it back from the same socket, like a loopback? If so I think it could be written much more simply; for example it is superfluous to write if select([@sock_rd], nil, nil, nil) dat = @sock_rd.recv(65536) when you can just do dat = @sock_rd.recv(65536) because Ruby handles the select() behind the scenes to prevent one thread blocking another, as outlined above; recv deschedules the thread until at least one byte is available. In fact I think the core could be re-written as something like this: ----------------------------------------------------------------------- require 'thread' class BufferedIO def initialize(sock) @sock = sock @queue = Queue.new @rd_thread = Thread.new { background_read } @wr_thread = Thread.new { background_write } end def background_read while true dat = @sock.recv(65536) break if dat.nil? or dat.empty? @queue.push(dat) end @queue.push(nil) # EOF indication end def background_write while dat = @queue.pop @sock.write(dat) end end def close @sock.close @rd_thread.join @wr_thread.join end end ----------------------------------------------------------------------- which looks a lot less like C and a lot more like Ruby :-) However that doesn't include your 'signal' functionality, nor do I have access to your timed-wait.rb, so I can't prove it against your Unit tests. Not sure how useful such a thing would be for RAA though. There is already the "Queue" class in thread.rb, which is a queue of objects rather than a queue of bytes, as I've used above. I think that's a more generic and useful pattern. There is also a SizedQueue which limits the maximum number of objects it contains. Queue and SizedQueue are thread-safe, which is why there are no Mutexes in the code above, although if paranoid you might want one to ensure that @sock.read, @sock.write and @sock.close are mutually exclusive. (But then, if @sock.write blocked, that would prevent @sock.read from running, which I don't think is what you want) > It's been stress tested with as many variations of reads, writes, > random & non random timing delays as I could think of to devise, > running for days under these stress conditions without a hiccup. > I realize that doesn't prove anything, but I have a good feeling > about its reliability at this point. That's definitely good :-) Regards, Brian.