Home Website Changes Log in Log out

Perl.com Article

Wiki is Love. Love the wiki. OBEY.

Objectives, Goals, Notes to Selves and Each Other While Writing This

Continuations are awesome. Mention Seaside. Perhaps get a dig in on fake continuations (they call them "Delimited Continuations" but even this is not true IMO) in Jifty.

Continuations are not perfect. Especailly since they're really co-routines (even I lie when I say that I have continuations). Memory overhead, non-core feature.

Continuations are fantastic. Event:: model (timed callbacks).

We can sort out the "I"/"we" stuff later.

The following is one approach. We can rewrite it or rework it. I just had to do something. You know how it goes =)

My writing style is casual and ranty and perhaps conversational (and perhaps confrontational). I'm sure there's good and bad in that, which means room for optimization.

Introduction to Continuation-Based Web Applications

For those who haven't heard of continuation-based web applications, here is a poem for you to ponder:

Web applications are stopped and then restarted between every single user interaction.

  cgi applications
  rise from ash then burn again
  upon each request

Imagine, if you will, writing a desktop application that had to completely exit and start from the beginning with each click of the mouse, each completion of a dialog box. To overcome this strange situation, the first thing your program would have to do is figure out what it should be doing (in the most likely case that it is in the middle of something) and pick up where it left off. There are two things that must be remembered between requests -- data state and control flow state.

Data state is pretty easy to take care of. Before you exit you stash all the data somewhere, in a file say, and give the user a secret number or a cookie. When they come back, so long as they haven't eaten their delicious cookie, they give it back and you figure out which data is theirs. This is fairly straightforward.

Control flow state, however, is a can of worms. Sometimes the user input itself will indicate what place in the application the user is at.

Ways to deal:

There is a fundamental difference between the way you structure web applications and the way you compose desktop or command-line applications. I'm not speaking of model-view-controller or other overall architecture, but rather at a much lower level.

Introduction to Continuity

It's difficult for us to come before you on the subject of HTTP.

So many projects and technologies offer so many bandaids for the problem, and here we are, saying "unplug the patient". I'm serious. I propose we //kill// statelessness in HTTP {ed: for web apps}. It was a bad idea.

HTTP followed the design of the Gopher protocol, which modeled hypertext on top of a file transfer model, where each "object" was fetched and then the connection broken. CGI was a hack, a kludge, an add-on, after the fact.

Then it went down hill from there. Session modules, object persistance frameworks, REST, JSON... the more stuff we built to cope with the fact that applications are now stateless, the more entrenched we got in the idea that applications //have// to be stateless.

They don't. Mainframe applications aren't. Even though the user is filling out forms on a glorified dumb terminal that posts input back now and then, the application doesn't have to serialize its state between screen submits. Unix applications most certainly aren't, neither command line applications nor graphical ones.

Okay, I can see you squirming in your seat, looking for an excuse to keep Web applications stateless, and the first cross with which to ward off statefullness is probably performance. Don't bother. mod_perl actually screw the pooch with the "one process per concurrent request" idea. So does FastCGI and company. In fact, there is no good reason to remain stateless, but it //will// take a while to wrap your head around. Even we kept trying to write applications in CGI style for a while after Brock made Continuity and we were starting to use it for serious applications. It's hard to break the mindset.

It's time to talk about coroutines. Continuations are a programming primitive. Let's say you have two routines. This happens a lot in software -- you'd be surprised. Sometimes there are more than two routines, especially in languages other than Perl. You could make one call the other. Then you have a caller and a callee. Let's also suppose that they need to pass values back and forth, so the caller is calling the callee in a loop. The caller can have complex nested if() statements, for() loops, while() loops, map { } expressions, and piles and lexical variables in various scopes. The callee can't. In fact, if it wants to remember anything at all, it either has to stick the values in static variables, in which case it can't be called recursively. This makes recusion suddenly a "problem" that people try to avoid. This also might amuse you, but complex programs often clobber data because a routine gets called that calls another routine that calls another routine that calls that first routine again, and that routine was stuff data somewhere, and the second invocation steps on the data from the first call. In terms of operating kernels, there are "top halves" and "bottom halves", and bottom halves can't call top halves and stuff like that, just for this reason. Object oriented programming solves a lot of problems but it doesn't solve this one. Instance data in an object is a common place to cache data that's private to a method and instance data getting clobbered is a common bug in object oriented systems. That's one way things can go wrong -- clobbering data that got stuffed somewhere. Another way

So, these coroutine people had a wacky, crazy, freaky idea -- hey, what if both the caller and callee could have if statements, while loops, for loops, map statements, lexical variables, and all of that, just like the caller? And furthermore, what if each new call into the callee created a new invocation that had its own state with regards to where it is in if statements, while loops, and so on, and what values the lexical variables have?

Do you see where I'm going with this? Each time the callee is called by a given caller, it resumes where it left off, and there can be lots of these. Now think about this in terms of Web applications for a moment. ... Holy crap!

The same thing that lets two routines call each other as peers also lets you write Web applications with state.

The technical accomplishment that made this possible is closures that also close over the callstack besides just the values of lexical variables, which is known as a continuation. And if the callee calls other callees, those get closed over too as part of the coroutine. And, joy of joys, this is available in Perl. I know, I can't believe this either -- it's just too cool. A very smart man named Marc Lehmann implemented this in his module, Coro. We love Marc =)

Caveat: There are some limits to Perl's implementation. Continations can't be cloned like they can in some other languages, so you can't try do transaction stuff, where you use them a snapshot of a routine's state that you can go back to when things go badly or to rewind time. Darn.

Okay, now for some examples.

Counter Example

Lets start with an example stolen from Seaside -- a simple counter.

http://beta4.com/seaside2/tutorial.html

#!/usr/bin/perl

use strict;
use Continuity;
use URI::Escape;

# Create the server and start the Event::loop
# Defaults to HTTP::Daemon backend on random port
# and cookie-based session tracking
Continuity->new->loop;

sub main {
  # We are given a handle to get new requests
  my $request = shift;

  # When we are first called we get a chance to initialize stuff
  my $counter = 0;

  # After we're done with that we enter a loop. Forever.
  while(1) {
    my $action = prompt($request,"Count: $counter", "++", "--");
    $counter++ if $action eq '++';
    $counter-- if $action eq '--';
  }
}

# Ask a question and keep asking until they answer
sub prompt {
  my ($request, $msg, @ops) = @_;
  $request->print("$msg<br>");
  foreach my $option (@ops) {
    my $uri_option = uri_escape($option);
    $request->print(qq{<a href="?option=$uri_option">$option</a>&nbsp;});
  }
  # Subtle! Halt, wait for next request, and grab the 'option' param
  my $option = $request->next->param('option');
  return $option || prompt($request, $msg, @ops);
}

Fancy Counter

And now we make their heads explode. This is similar to how Seaside does everything -- links actually invoke callbacks.

Seaside has an additional kernel layer that we don't, enabling them to easily put a bunch of counters on a single page. We should do that (not here probably) just to demonstrate how it is done. Or perhaps a general block-based kernel is needed, Continuity::Blocks or something. Each "block" (as in building-block) on the page gets its own coroutine inside the big one. But that is a complete side-track and I'm just jotting it down since I have a keyboard and the letters are getting displayed! (Scott likes this -- all of it -- but suggests moving it further down as a sort of reprise to the counter example.)

#!/usr/bin/perl

use strict;
use Continuity;
use URI::Escape;

# Create the server and start the Event::loop
# Defaults to HTTP::Daemon backend on random port
# and cookie-based session tracking
Continuity->new->loop;

# Ask a question and keep asking until they answer
sub prompt {
  my ($request, $msg, %ops) = @_;
  $request->print("$msg<br>");
  foreach my $option (keys %ops) {
    my $uri_option = uri_escape($option);
    $request->print(qq{<a href="?option=$uri_option">$option</a>&nbsp;});
  }
  # Subtle! Halt, wait for next request, and grab the 'option' param
  my $option = $request->next->param('option');
  # DoubleSubtle! Call the callback
  return $option ? $ops{$option}->() : prompt($request, $msg, %ops);
}

sub main {
  # We are given a handle to get new requests
  my $request = shift;

  # When we are first called we get a chance to initialize stuff
  my $counter = 0;

  # After we're done with that we enter a loop. Forever.
  while(1) {
    my $action = prompt($request,"Count: $counter",
      "++" => sub { $counter++ },
      "--" => sub {
        if($count == 0) {
          if(prompt($request,"Do you really want to GO NEGATIVE?",
              "Yes" => sub{1}, "No"=>sub{0})) {
            $counter--;
          }
        } else {
          $counter--;
        }
      }
    );
    if($counter == 42) {
      $request->print(q{
        <h1>The Answer to Life, The Universe, and Everything</h1>
      });
    }
  }
}

Chat Server

Pet Store

No. Too much code. This would be more for something to publish numbers from and say, hey, look, if you don't have to serialize carts to XML between each hit, the thing goes like 300 bajillion times faster!

Might be interesting to attack a multipage app to show how nonlinear flow control is handled, though -- for instance, what happens if a logged-in user opens two "item" pages and their cart in tabs.

Benefits

Keep your Lexicals

Lexical variables don't dissapear between requests. So there is no need for a separate session management module.

Keep your place in the Code

So there's no need for a dispatch table or dispatch framework. In fact, you'll be far less tempted to use some framework-ish thing that takes requests then rre-invokes your code. The structure of the application is expressed in terms of Perl primitives that you already know.

2.2 SERIALIZE YOUR CODE

You code flows from top to bottom, making it more readable and manageable.

2.3 SHARED DATA

Each continuation has access to the global variables that it shares with all others. Since only one continuation is being run at any given instant, there is no locking necessary and no race conditions (unless you make them yourself :) ).

2.4 EVENT-TRIGGERED ACTIONS

Coro also provides Coro::Event, so that we can do time-based or variable-watching-based event triggers.

2.5 MULTIPLEX SESSIONS INTO A SINGLE PROCESS

Each process can actively serve many end-users.

2.6 MOD_PERL BENEFITS

One of the biggest benefits of mod_perl is that the code is not re-compiled at each request. This is also a benefit of Continuity.

Philosophy

Here's the Continuity Philosophy:

Continuity

We've found that we can write much more complex Web applications much more quickly and most importantly, in far fewer lines, when we use Continuity. Some applications benefit more than others -- applications

[I'm not an actual Continuity user, but coming from a purely academic/peanut-gallery angle, here's a take using the coroutines/virtualization analogy that someone suggested:

We've found that Continuity provides a more natural approach to flow control and state management than traditional session-based approaches.

There's an analogy to operating system virtualization. Virtual machines make life easier by handling the messy work of encapsulating a machine's state (hard drive image, memory, and so forth) in an easy-to-manage package (a set of files). VMs also eliminate the requirement for extra physical hardware -- and the associated cost -- that's ordinarily a barrier to running many logical machines.

Coroutines and Continuity do for Perl programs something like what virtualization does for servers. They make complex state management easy -- indeed, nearly transparent to the coder. They also make it possible to simulate running many Perl severs with separate states for each user of your application, with much less of a cost in system resources.]

Conclusion

*whew*! Hard work!


Here is a dump of some more text, exported from kdissert. I will probably update the stuff below, so it shouldn't be edited directly (yet). The idea is to just push out some text, and then we'll mix-and-match.

Title: Continuity

Author: awwaiid Brock Wilcox

Email: awwaiid@thelackthereof.org

Abstract:

1.2 OTHER PROJECTS

We are not by a long shot the originators of the idea of continuation servers. The currently most well-known project is Seaside, but even before Seaside there have been papers published and projects based upon the idea of continuations inverting control on the web (see Paul Grahm's article).

1.2.1 SEASIDE

Seaside is a project by Avi Briant written in Smalltalk. It is the most well-known of the continuation servers.

Seaside is being actively developed.

Seaside Website [1]

1.2.2 JIFTY

Jifty claims to have continuations, but these are not at a language level. Jifty has continuations at a higher level -- it keeps track of the HTTP Requests and provides a runtime context for each request. Lexically scoped variables are not kept, for example. There is no shared memory area for coroutines on the same server to access.

1.2.3 JETTY

Jetty is a servlet container for java which provides continuations. It is being actively developed.

1.2.4 UNCOMMON WEB, WEE, ...

UnCommonWeb is a scheme continuation server.

1.3 LIBRARY VS FRAMEWORK

Basically frameworks dictate how you work, and libraries don't. Obviously these are sweeping statements to make, and many projects don't have such a clear-cut status of library or framework. The point is, that every decision we make hinges on "are we forcing the author to alter the way they work?"

We, as a library author, are trying to build up an API for the user to use, rather than dictate the structure of their application. Using Continuity, the application could have an MVC architecture or it could have a script-like structure or no structure at all and it it doesn't hinge upon our library.

3 PERL IMPLEMENTATION

3.1 CORO

Coro is a Perl Module (on CPAN) written by Marc Lehmann (who totally rocks!). The library allows for coroutines in Perl.

The implementation has to dig in at a fairly low level of Perl's guts. It swaps out the execution context, which includes "callchain + lexical variables + @_ + $_ + $@ + $/ + C stack"

Coro works on Linux, BSD, Mac, Windows, and most Unices -- each platform needs its own set of work-arounds to swap the C stack in and out, with Unix falling back on setjmp/longjmp.

Coro additionally has a compatabilty layer with the Event module, allowing us to utilize event-based models. So an application can wake itself up to do some action not only when it gets a request, but when a file is ready or when a period of time has past.

3.2 HTTP::DAEMON

3.3 FCGI

3.4 STRUCTURE

3.4.1 ADAPTOR

Each adaptor interfaces with an HTTP server backend (either HTTP::Daemon or FCGI.pm) and adds some common methods between them all.

3.4.2 MAPPER

Continuity::Mapper takes an HTTP request (usually an HTTP::Request) and maps it to a running coroutine (or creates a new coroutine).

The mapper actually holds the list of coroutines.

3.4.3 MANAGER

Right now the manager is actually the main Continuity.pm module. It sets up the server and holds the main loop that looks for new requests or events.

4 EXAMPLE APPLICATIONS

Continuity is done using Behavior-Driven-Development, which means that we've generated several examples of it's use.

4.1 NUMBER GUESSING GAME

Guess a number between 0 and 100!

4.2 COUNTER

This is a very simple application, with very minimal application code. It has two routines -- the main loop, and the prompt.

The prompt displays some text, provides options, and waits for a response.

The main loop keeps track of the counter, calling prompt to ask for further instructions from the user.

4.3 AJAX CHAT

Each user gets two coroutines.

The send-message coroutine lists to the user for input, and adds said input to the global chat-room message list.

The recieve-message coroutine watches the message list, and when it changes notifies the user of the change.

The client runs javascript, doing a long-pull to get the message list. That is, it requests the message list, but doesn't receive a reply until there is a new message to display. If this timeouts, it re-starts it's long-pull request.

4.4 ZOMBIE GAME

Zombies!

Leftover Parts

Installing and Prerequisites

CPAN install Continuity. Grabs Coro and others.

1 BACKGROUND INFORMATION

Cut out but can be un-cut as needed

1.1 WHAT ARE CONTINUATIONS?

Continuations are a way to suspend a part of your program, and resume it later. It is somewhat like a resume-able exception catching mechanism.

First-class continuations can be copied and executed from the same point multiple times. We don't have those, we have "one-shot" continuations, aka coroutines. These are not as powerful as full continuations, but it turns out that we don't need the full power.

Seaside gets first class continuations, but I don't think that it uses them. TODO: prove it.