Continuations for Lazy Programmers

I can imagine years from now, grand children sitting on my knee tugging at my ears “grandpa, what’s a continuation?”…

So, here’s the basic problem I have.  I want to do some I/O operation, and I want that operation to occur ‘in the background’, meaning, I don’t want the rest of my application to block while that operation occurs.  At the same time though, I want to come back to the site where I issued the i/o once it has completed.  What?


local line = socket:readline(128);

In this case, I want to do a ‘readline’ from a socket, and once the line has been read, no matter how long it takes, print out the result of reading that line. At the same time, if other things are happening in my application, I don’t want the application to “block” on this readline. I want to cooperatively share the CPU, so that as soon as this operation wants to ‘block’, it will instead ‘yield’ and allow something else to run, eventually returning when there’s more i/o to be done in a non-blocking manner.

At a very low level, you could be doing async promise/futures, etc, and decorating your code in various ways to indicate what’s happening under the covers.

In TINN, I put this sort of thing very deep within the EvenScheduler. So, all socket I/O is automatically non-blocking, and MUST occur within the context of the scheduler. This is fine, as long as you have a scheduler running things. So, TINN has that scheduler, but how do you get things going?

Here’s a bit of code that does a fairly straight forward retrieval of a http resource:

-- file: http_get.lua
local Runtime = require("Runtime")
local REST = require("http_rest");

local main = function()
  local url = arg[1];
  local showheaders = arg[2] or false;

  onfinish = function(result)

  spawn(REST.GET, url, showheaders, onfinish);


> tinn http_get.lua true
// Print out a whole bunch of content, including http headers

The critical piece here is the first line: local Runtime = require(“Runtime”);

This will load a scheduler for the rest of the application. This runtime provides the default scheduler, as well as some convenience functions for the ‘run’, ‘spawn’, ‘stop’.

The pattern here is to create some function, name it ‘main’ if you like, it doesn’t really matter. What’s important is that your ‘main’ is the routine that starts everything else for your application. From there, call the ‘run()’ function, handing it your ‘main’. This will start a loop which will continue until it is explicitly stopped.

Within the main, I call ‘spawn’ to start my “GET”. I also hand it a ‘onfinish’, which is the routine I want executed once the function returns. In this particular case, the function will be called by the “GET()” function, not by the scheduler. This is a flexible approach to chaining. It doesn’t impose a callback mechanism. You can implement whatever callback mechanism you like. In this particular case, once the function is finishing, it will just call “stop()”, which will stop the scheduler, and effectively exit the process.

The Runtime module is fairly straight forward as well. It’s primarily some convenience and encapsulation. The primary requirement is having a global table named “Runtime”, which MUST have a scheduler named “Scheduler”. If you have this, then your async io will work automatically.

local Scheduler = require("EventScheduler");

Runtime = {}
Runtime.Scheduler = Scheduler();

Runtime.Run = function(self, func, ...)
  if func ~= nil then
    self:Spawn(func, ...);

Runtime.Spawn = function(self, func, ...)
  self.Scheduler:Spawn(func, ...);

Runtime.Stop = function(self)

  Convenience Functions

run = function(func, ...)
  return Runtime:Run(func, ...);

spawn = function(func, ...)
  return Runtime:Spawn(func, ...);

stop = function()
  return Runtime:Stop();

-- Simple Return
return Runtime

So, there you go. Within the TINNSnips project, the ‘restutils’ has a ‘Runtime.lua’ file that does exactly this.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s