"Conversations" and coroutines

Hi,

I am trying to write a some code that expresses a series of Commands/Responses over a serial link.

In pseudocode, this is what I want to accomplish:

   conv = Conversation.new(connection) { |x|
       x.transmit(:init)
       x.wait_for(:ok)
       x.transmit(:get_user_input)
       x.wait_for(:user_data) { |message| response = message.text }
       puts response
     rescue TimeoutException => e
       x.retry if x.attempts < 3
   }
   conv.start

I suppose that something like that can be done with continuations and coroutines (which I have never used before) Especially <http://www.all-thing.net/Ruby/coroutines.html> looks useful.

I would really appreciate any advice on how to best implement all this. Are there some projects/libraries that do something similar?

The interface I made up above could probably also be improved a lot and I'd appreciate your thoughts and suggestions.

Thank You,
Levin

Levin Alexander wrote:

Hi,

I am trying to write a some code that expresses a series of
Commands/Responses over a serial link.

In pseudocode, this is what I want to accomplish:

  conv = Conversation.new(connection) { |x|
      x.transmit(:init)
      x.wait_for(:ok)
      x.transmit(:get_user_input)
      x.wait_for(:user_data) { |message| response = message.text }
      puts response
    rescue TimeoutException => e
      x.retry if x.attempts < 3
  }
  conv.start

I'm guessing #start runs the block? Why not

  Thread.new {conv.start}

Or does the block get executed during Conversation.new, which just
queues up all the commands so that start can call them in order later?

···

--
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Hi ..

Hi,

I am trying to write a some code that expresses a series of
Commands/Responses over a serial link.

I think that the "serial link" part is important. How does the
underlying interface present its data? Interrupt or polled? And the
host, does it block on serial data?

If everything is inherently serial, then there is not much to be gained
with threads. Are you expecting to do the equivalent of receive a
packet then process it? If so, then having a polling thread that puts
its processed data in a buffer seems to make sense.

Coroutines are a funny thing. They are kind of like multitasking when
there is no other choice (a la FORTH or Modula-2, where I think they
came from). Ruby threads will buy you more that procedure swapping, I
think.

Just my $0.02

Regards,

···

On Wed, 2005-08-10 at 04:45 +0900, Levin Alexander wrote:

--
-mark. (probertm at acm dot org)

I am trying to write a some code that expresses a series of
Commands/Responses over a serial link.

In pseudocode, this is what I want to accomplish:

  conv = Conversation.new(connection) { |x|
      x.transmit(:init)
      x.wait_for(:ok)
      x.transmit(:get_user_input)
      x.wait_for(:user_data) { |message| response = message.text }
      puts response
    rescue TimeoutException => e
      x.retry if x.attempts < 3
  }
  conv.start

I'm guessing #start runs the block? Why not

  Thread.new {conv.start}

I am pretty sure that I need Threads, but I think a single 'receiver'-Thread inside the connection instance should be enough.

My main problem at this point is that I don't know how I want the final result to *look* like.

Or does the block get executed during Conversation.new, which just
queues up all the commands so that start can call them in order later?

No, the "conversation" should be an arbitrary program.

My current attempts wrap around an instance of IO, like so:

   # Connection wraps protocol details like a notion of "Packets" and
   # generates events if specific messages arrive
   connection = Connection.new( f = Tempfile.new("example") )

   # Add a Packet named :init that can be sent by
   # calling "connection.send_packet(:init)"
   connection.add_sender(:init, Packet.create(:data_format => "INIT") )

   # Add an event called :ok that is generated whenever a packet
   # starting with the letter "O" is received
   connection.add_receiver(:ok, Packet.create(:header => [?O]) )

   # Register an event handler
   connection.on_receive(:ok) { |m| puts "received message: OK" }

Given that I could probably do this:

   # Resumable from <http://www.all-thing.net/Ruby/coroutines.html&gt;

   conversation = Resumable.new { |r|
     connection.on_receive(:ok) { r.resume if r.suspended? }
     connection.send(:init)
     r.suspend
     puts "Got an answer!"
   }

   ...
   connection.start # start sending/receiving data
   conversation.call
   ...

That's not very pretty though

-Levin

···

Joel VanderWerf <vjoel@path.berkeley.edu> wrote: