backpressure.pony

"""
# Backpressure Package

The Backpressure package allows Pony programmers to participate in Pony's
runtime backpressure system. The goal of the backpressure system is to prevent
an actor's mailbox from growing at an unbounded rate.

At a high level, the runtime backpressure system works by adjusting the
scheduling of actors. When an actor becomes overloaded, the Pony runtime will
deprioritize scheduling the actors that are sending to it. This change in
scheduling allows the overloaded actor to catch up.

The Pony runtime can detect overloading based on message queue size. However,
the overloading of some types of actors is harder to detect. Let's take the
case of actors like `TCPConnection`.

`TCPConnection` manages a socket for sending data to and receiving data from
another process. TCP connections can experience backpressure from outside
our Pony program that prevents them from sending. There's no way for the Pony
runtime to detect this, so intervention by the programmer is needed.

`TCPConnection` is a single example. This Backpressure package exists to allow
a programmer to indicate to the runtime that a given actor is experiencing
pressure and sending messages to it should be adjusted accordingly.

Any actor that needs to be able to tell the runtime to "send me messages
slower" due to external conditions can do so via this package. Additionally,
actors that maintain their own internal queues of any sort, say for buffering,
are also prime candidates for using this package. If an actor's internal
queue grows too large, it can call `Backpressure.apply` to let the runtime know
it is under pressure.

## Example program

```pony
use "backpressure"
use "net"

actor SlowConsumer is (TCPConnectionActor & ServerLifecycleEventReceiver)
  var _tcp_connection: TCPConnection = TCPConnection.none()
  let _bp_auth: ApplyReleaseBackpressureAuth
  let _out: OutStream

  new create(auth: TCPServerAuth, fd: U32,
    bp_auth: ApplyReleaseBackpressureAuth, out: OutStream)
  =>
    _bp_auth = bp_auth
    _out = out
    _tcp_connection = TCPConnection.server(auth, fd, this, this)

  fun ref _connection(): TCPConnection =>
    _tcp_connection

  fun ref _on_start_failure(reason: StartFailureReason) =>
    None

  fun ref _on_throttled() =>
    _out.print("Experiencing backpressure!")
    Backpressure.apply(_bp_auth)

  fun ref _on_unthrottled() =>
    _out.print("Releasing backpressure!")
    Backpressure.release(_bp_auth)

  fun ref _on_received(data: Array[U8] iso): ReadAction =>
    KeepReading
```

## Caveat

The runtime backpressure is a powerful system. By intervening, programmers can
create deadlocks. Any call to `Backpressure.apply` should be matched by a
corresponding call to `Backpressure.release`. Authorization via the
`ApplyReleaseBackpressureAuth` capability is required to apply or release
backpressure. By requiring that the caller have a token to apply or release a
backpressure, rogue 3rd party library code can't run wild and unknowingly
interfere with the runtime.
"""

use @pony_apply_backpressure[None]()
use @pony_release_backpressure[None]()

primitive Backpressure
  """
  Applies and releases runtime backpressure.
  """
  fun apply(auth: ApplyReleaseBackpressureAuth) =>
    @pony_apply_backpressure()

  fun release(auth: ApplyReleaseBackpressureAuth) =>
    @pony_release_backpressure()