On Thu, 4 Jan 2001 16:39:28 -0800, Rob Bloodgood wrote:

>OK, for starters let me say that I'm getting all of this working on a
>largely monkey-see, monkey-do basis... the stuff in /samples was invaluable
>for getting started, but somehow I doubt that if you were to re-write them
>now, most of them would be quite different. :-)

You betcha.  The big problem is that the older ones started out as test
and reference programs back before I knew how to write proper tests.
Rather than retire them gracefully, I impressed them into example duty,
and many of them aren't appropriate for that.

At some point I plan to dump the lamer ones, revise the better ones,
write proper tutorials around whichever ones survive, and split them
off into a separate documentation distribution.

You mention later that the documentation bites, and I tend to agree.
0.1202 saw a big revision and restructuring, and I'm working on a style
guide for the next big revision to follow.  After that, it could very
well still suck, but it will suck in a consistent, deliberate way. :)


>> POE::Component::Server::TCP.  Later on you said it wasn't useful
>> because it
>> doesn't support binding to specific interfaces.  That's easily
>> fixed, though.
>> Take a look inside; it's a very tiny wrapper around SocketFactory.
>>
>> I'll add an interface parameter to it for 0.1204.  If you can't wait that
>> long, patch it and send me a diff. :)
>
>Well I was going to do that...
>but something's broken now, and socketfactory.perl complains:
>Bad arg length for Socket::unpack_sockaddr_un, length is 16, should be 110
>at ./socketfactory.perl line 306.

It looks like SocketFactory's getsockname method, which just maps to perl's
getsockname built-in function being called on the factory's socket handle,
is returning a bogus value.  I'm not sure why that would be, unless the
socket factory couldn't create the socket.

If that's true, the failure is probably being caused by an old socket at
the bind address.  If you have a /tmp/poe-usrv file lying around, unlink
it, and the problem should go away.


[Removed the bit about the non-traditional application needing meticulous
file locking or atomic operations.]

[Also removed the moot bits about different ways to call stuff.]


>OK, first point: I was mostly wondering if defining the alias for the
>session was the "approved" way to keep it alive.

Oh, sure.  The design assumption is that aliased sessions will be spoken
to by some random bunch of other sessions, so POE::Kernel keeps them
alive.  Aliases basically turn sessions into little daemons.


>2nd point: for this stage of development it returns immediately, but based
>on the suggestion and pseudocode I got from Torvald Riegel, and the
>drastically unclear documentation explaining them (sorry :-), I managed to
>switch my call() to a postback().

No offense taken.  POE is all familiar to me, and documentation is
way harder to write than code.  I've been doing this for years now,
and it's hard to look at documentation from the POV of someone who
isn't as familiar with the code as I am.


>The hardest part was figuring out who/what, in my program, corresponded to
>the Servlet & Client in the docs.

I've revised that section, and it's included at the end of this file.
Please let me know if it reads better.


>Now before you take umbrage at the documentation crack, let me say that I'm
>pretty sure that most of the intent for postbacks, as originally designed,
>was related first to Tk, and then in wrapping existing callbacks.
>A) all of the documented examples for postback mention Tk
>B) the only mentions of postback in the samples are in tk.perl
>( except for the passing note in names.pl that this section is now obsolete,
>because we now have session postbacks :-)

This is a fair and accurate conclusion.  A lot of things evolved into
place like that.


>Since I've only rarely used callbacks before (mostly in HTML::Parser's), the
>connection to the framework I'm in right now was a little while in coming.
>
>HOWEVER,
>after plugging at it for awhile, my flow now works like this:
>
>StreamServerSession::got_line {
>    my ($object, $sender, $kernel, $heap, $line) = @_[OBJECT, SENDER,
>KERNEL, HEAP, ARG0];
>
>    # bookkeeping here...
>
>    $kernel->post( db => fetch => $line, $sender->postback( reply =>
>$line ) );
>}
>
>(what is $sender all about, anyway?)

$_[SENDER] is the session that called $kernel->post() or $kernel->call()
to deliver an event to you.  It's sort of a reply-to field for the event
in case you want to verify its source or send something back.

Since postback() is a Session method, you can call it on $_[SESSION]
to create a postback for the current session.  In this case, the client
would give its postback to the server, and the server would call it to
send back a response.

  # This code is in a client session.  SESSION is this session,
  # so it refers to a client.
  my $my_postback = $_[SESSION]->postback( reply_with_this_event => $line );
  $kernel->post( db => fetch => $line, $my_postback );

The other case is where the server creates a postback to respond to the
client.  Here, it calls $_[SENDER]->postback( ... ) to create a postback
for the session that sent the query.

  # This code is in the server session.  SENDER is the session
  # that sent this event.  In this case, the sender is a client.
  my $client_postback = $_[SENDER]->postback( reply_with_this_event => $line );


[code removed]

>I'm just going to venture a guess here, but is this kind of thing the
>"correct" way to do what you said you were too lazy to do correctly in
>tutorial-chat.pl?

Not really, no.  The problem in a multi-user interactive chat or game
server is that sometimes responses are generated from input handlers,
and sometimes they're generated from queued events.  Filehandles can
generate events continuously until they're serviced, so POE calls their
event handlers immediately to shut them up.  Responses generated from
input handlers would be put right away, while responses from FIFO
events would lag behind.  This can break the relationship between cause
and effect in subtly disconcerting ways.

In tutorial-chat.perl I "wimped out" and made all the responses
immediate.  Later on, I wrote a chat/crossword puzzle server called
xws that goes the harder route.  It posts all the responses through
POE's FIFO event queue.  Either way, the responses are kept in order
of occurrence, and causality is maintained.


>Also,
>I'm curious about how the whole $wheel->put mechanism fits with the
>"flushed" state.

The "flushed" state is Wheel::ReadWrite's way of letting you know all
its output has been flushed to a file/socket handle.  If a session has
sent a "goodbye" message, it should wait for acknowledgement that it
was flushed to the socket before shutting down.  Otherwise the session
may _stop before the client sees the message.

You can also use it to send prompts once the output buffer has gone
empty, but be careful: the prompt will generate another "flushed"
event once it's been sent, and it's easy to go into a loop.


[Signals aren't delivered when DBD::Oracle is used.]

>> This doesn't sound right.  Can you provide more information about it?
>
>Actually, I'm going to have to call this a DEFINITE Oracle issue.  When I
>got a little further and was able to tell my DB session to shutdown, and I
>mean from the MOMENT the db handle was disconnected, my POE program reponded
>to Ctrl-C.  This is consistent with the behavior I've seen in other Perl
>programs that use DBD::Oracle. SIGINT responds only if there is a currently
>executing statement handle, BEFORE it returns... at that point SIGINT gives
>an Oracle message:
>DBD::Oracle::st fetch failed: ORA-01013: user requested cancel of current
>operation (DBD ERROR: OCIStmtFetch) at $0 line __LINE__
>
>It may be a task of major wizardry (translated: hours of digging thru
>obscure callbacks, XS, and OCI) to find this...?

Ow!  If you haven't already, try scouring the docs before grovelling
through the code.  Maybe there's a clean way to disable this behavior?


>And finally:
>I would like to have a way to stop my server via a message to my daemon thru
>the socket, like:
>
>if ($line eq 'quit') {
>    $kernel->alias_remove('db'); # kills the DBFetch thread
>    return;
>}
>
>Now, that's a snippet of my actual code, and it causes everything to quit
>except the TCPServer.  How would you suggest I signal the SocketFactory to
>shut down?

Oh, uh... I forgot to add that.  Someone else recently requested a
similar way to shut down a different component of mine.  To be honest,
I hadn't given consideration to programs that had to (gasp!) exit. :)

The immediate work-around is to signal the program to shut down.  You
can do this by posting a simulated SIGINT to the Kernel.  Since signals
propagate to their destination's children, and since the Kernel is an
ancestor of every session, signalling it will also signal everything
else.  That's how OS signals are delivered, so it's okay to do it.

  $kernel->signal( $kernel, 'INT' );

That will propagate a simulated SIGINT to everything, including the TCP
server.

I'm probably going to add a "stop" event to all my components so they
can be stopped upon request.


>TIA, and thanx for creating such a FRICKIN COOL module. ;-)

I'm glad you like it.  Don't hesitate to tell me where it sucks, so I
can fix those things.  Speaking of fixing, here's the revised postback
docs.  They'll appear this way in 0.1204 unless I find something else
to change.

-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net


=item postback EVENT_NAME, PARAMETER_LIST

postback() creates anonymous coderefs which, when called, post
EVENT_NAME events back to the session whose postback() method was
called.  Postbacks hold external references on the sessions they're
created for, so they keep their sessions alive.

The EVENT_NAME event includes two fields.  C<ARG0> contains a
reference to the PARAMETER_LIST passed to C<postback()>.  This is the
"request" field.  C<ARG1> holds a reference to the parameters passed
to the coderef when it's called.  That's the "response" field.

This creates a Tk button that posts an "ev_counters_begin" event to
C<$session> whenever it's pressed.

  $poe_tk_main_window->Button
    ( -text    => 'Begin Slow and Fast Alarm Counters',
      -command => $session->postback( 'ev_counters_begin' )
    )->pack;

C<postback()> works wherever a callback does.  It's also possible to
use postbacks for request/response protocols between sessions.

In the following code snippets, Servlet is a session that acts like a
tiny daemon.  It receives requests from "client" sessions, performs
som long-running task, and eventually posts responses back.  Client
sends requests to Servlet and eventually receives its responses.

  # Aliases are a common way for daemon sessions to advertise
  # themselves.  They also provide convenient targets for posted
  # requests.  Part of Servlet's initialization is setting its alias.

  sub Servlet::_start {
    ...;
    $_[KERNEL]->alias_set( 'server' );
  }

  # This function accepts a request event.  It creates a postback
  # based on the sender's information, and it saves the postback until
  # it's ready to be used.  Postbacks keep their sessions alive, so
  # this also ensures that the client will wait for a response.

  sub Servlet::accept_request_event {
    my ($heap, $sender, $reply_to, @request_args) =
      @_[HEAP, SENDER, ARG0, ARG1..$#_];

    $heap->{postback}->{$sender} =
      $sender->postback( $reply_to, @request_args );

    # Do something with @request_args.
    ...;
  }

  # When the server is ready to respond, it retrieves the postback and
  # calls it with the response's values.  The postback acts like a
  # "wormhole" back to the client session.  Letting the postback fall
  # out of scope destroys it, so it will stop keeping the session
  # alive.  The response event, however, will take up where the
  # postback left off, so the client will still linger at least as
  # long as it takes to receive its response.

  sub Servlet::ready_to_respond {
    my ($heap, $sender, @response_values) = @_[HEAP, ARG0, ARG1..$#_];

    my $postback = delete $heap->{postback}->{$sender};
    $postback->( @response_values );
  }

  # This is the client's side of the transaction.  Here it posts a
  # request to the "server" alias.

  sub Client::request {
    my $kernel = $_[KERNEL];
    $kernel->post( server => accept_request_event => reply_to => 1, 2, 3 );
  }

  # Here's where the client receives its response.  Postback events
  # have two parameters: a request block and a response block.  Both
  # are array references containing the parameters given to the
  # postback at construction time and at use time, respectively.

  sub Client::reply_to {
    my ($session, $request, $response) = @_[SESSION, ARG0, ARG1];

    print "Session ", $session->ID, " requested: @$request\n";
    print "Session ", $session->ID, " received : @$response\n";
  }

-End-


Reply via email to