Skip to content

Waiting

Problem

Here’s the problem: you’re writing an application that needs to execute an action every few seconds. In a language with blocking operations, I could just call sleep and be done with it. It might not be the most elegant solution but it would work. In Pony, no such obvious solution exists. One of Pony’s key features is there are no blocking operations. It’s a bit of a riddle: how do you wait when you can’t wait?

Solution

You want to use the Time package. In particular, the Timer and Timers types.

A timer allows you to execute code at set intervals. Let’s walk through using a timer. Below is a simple application that prints out a number to the console every 5 seconds until someone terminates the program:

use "time"

actor Main
  new create(env: Env) =>
    let timers = Timers
    let timer = Timer(NumberGenerator(env), 0, 5_000_000_000)
    timers(consume timer)

class NumberGenerator is TimerNotify
  let _env: Env
  var _counter: U64

  new iso create(env: Env) =>
    _counter = 0
    _env = env

  fun ref _next(): String =>
    _counter = _counter + 1
    _counter.string()

  fun ref apply(timer: Timer, count: U64): Bool =>
    _env.out.print(_next())
    true

Zooming in on the key bits, we first set up our timers, create one and add it to our set of timers:

    let timers = Timers
    let timer = Timer(NumberGenerator(env), 0, 5_000_000_000)
    timers(consume timer)

The Timer constructor takes 3 arguments, the class to notify, how long until our timer expires and how often to fire. In our example code, an instance of NumberGenerator will be called every 5 billion nanoseconds i.e. every 5 seconds until the program is killed.

Here’s our method in NumberGenerator that gets executed:

  fun ref apply(timer: Timer, count: U64): Bool =>
    _env.out.print(_next())
    true

If we were to compile and run our application, we’d end up with some output like:

$ ./timer
1
2
3
4
5
6

Discussion

It’s not the most exciting output in the world but, it’s a pattern that can be adapted to many different scenarios. Timer can be put to use for rate limiting outgoing network connections, creating buffers that flush at a set interval, implementing timeouts and variety of other time based blocking operations.


This pattern is based on a blog post previously published by Sean T. Allen.