Ruby/Python Continuations: Turning a block callback into a read()-method?

After failing on a yield/iterator-continuation problem in Python (see
below) I tried the Ruby (1.8.2) language first time on that construct:
The example tries to convert a block callback interface
(Net::FTP.retrbinary) into a read()-like iterator function in order to
virtualize the existing FTP class as kind of file system. 4 bytes max
per read in this first simple test below. But it fails on the second
continuation with ThreadError after this second continuation really
executing!? Any ideas how to make this work/correct?

(The question is not about the specific FTP example as it - e.g. about a
rewrite of FTP/retrbinary or use of OS tricks, real threads with polling
etc... - but about the continuation language trick to get the execution
flow right in order to turn any callback interface into an "enslaved
callable iterator". Python can do such things in simple situations with
yield-generator functions/iter.next()... But Python obviously fails by a
hair when there is a function-context barrier for "yield". Ruby's
block-yield-mechanism seems to not at all have the power of real
generator-continuation as in Python, but in principle only to be that what a normal callback would be in Python. Yet "callcc" seemes to be promising - I thought so far :frowning: )

=== Ruby callcc Pattern : execution fails with ThreadError!? ===========
require 'net/ftp'
module Net

class FTPFile
   def initialize(ftp,path)
      @ftp = ftp
      @path=path
      @flag=true
      @iter=nil
   end
   def read
      if @iter
         puts "@iter.call"
         @iter.call
      else
         puts "RETR "+@path
         @ftp.retrbinary("RETR "+@path,4) do |block|
            print "CALLBACK ",block,"\n"
            callcc{|@iter| @flag=true}
            if @flag
               @flag=false
               return block
            end
         end
      end
   end
end

end

ftp = Net::FTP.new("localhost",'user','pass')
ff = Net::FTPFile.new(ftp,'data.txt')
puts ff.read()

=== Output/Error ====

vs:~/test$ ruby ftpfile.rb
RETR data.txt
CALLBACK robe
robe
@iter.call
CALLBACK rt

/usr/lib/ruby/1.8/monitor.rb:259:in `mon_check_owner': current thread
not owner (ThreadError)
         from /usr/lib/ruby/1.8/monitor.rb:211:in `mon_exit'
         from /usr/lib/ruby/1.8/monitor.rb:231:in `synchronize'
         from /usr/lib/ruby/1.8/net/ftp.rb:399:in `retrbinary'
         from ftpfile.rb:17:in `read'
         from ftpfile.rb:33
vs:~/test$

=== Python Pattern : I cannot write down the idea because of a barrier ===

#### I tried a pattern like:
     ....
     def open(self,ftppath,mode='rb'):
         class FTPFile:
             ...
             def iter_retr()
                 ...
                 def callback(blk):
                     how-to-yield-from-here-as-iter_retr blk???
                 self.ftp.retrbinary("RETR %s" % self.relpath,callback)
             def read(self, bytes=-1):
                 ...
                 self.buf+=self.iter.next()
                 ...
             ...
         ....

···

=====

Robert

         @ftp.retrbinary("RETR "+@path,4) do |block|
            print "CALLBACK ",block,"\n"
            callcc{|@iter| @flag=true}
            if @flag
               @flag=false
               return block
            end
         end

Hi Robert,

I guess you are running into some threading/synching of Net::FTP here.
Simply put, the pattern works, although it is harder to manage properly
than one might think:

  # A small cc demo on the topic of IO. Of course there is
  # File#readline doing the same job as this fabulously, this
  # is just for demonstration.

···

#
  class CCFile
    def initialize
      @file = File.open(__FILE__, 'r+')
      @cc = nil
    end

    def read
      @cc.call if @cc && !@file.eof?
      @file.each do |line|
        puts line
        callcc do |cc|
          @cc = cc
          return line
        end
      end
    end
  end

  f = CCFile.new
  2.times do
    f.read
  end
  # let the files close themselves

You have probably remarked that the @flag variable isn't strictly
neccessary. Is that something like what you are trying to do ?

Also note the interaction with the loop. This is why cc's are not widely
used...

Keep going, you are on the right track. I can't comment on your Python,
since I am a total n00b in Python.

best whishes,
k
software manufaktur - rubylab.newfoundedpower.com