2.6 Behn

In this lesson we introduce the notion of a vane, which should be thought of as a kernel module for Arvo (if you don't know what that means yet, just keep reading). We first present a primer on what vanes are and how they communicate, followed by an examination of the simplest vane, known as Behn (pronounced like "bane"), which is Arvo's timekeeper.

Following this lesson, we will introduce Gall, a vane used to build user space apps. Then we have a walkthrough where we construct an egg timer as a Gall app that interacts with Behn.

Vanes

In order to introduce vanes, we must first introduce the concept of a kernel. The kernel is in some sense the "core" of an operating system, in that it has complete control over all events in the system and may perform any operation. It is the first part of the operating system to boot, and is responsible for spawning all other processes and routing their communication to one another. The tasks a kernel performs are very low level - managing memory, prioritizing processes, keeping track of time, managing hardware I/O, etc.

Kernel code is generally kept to be as minimal as possible, and so almost anything non-trivial is put into an extension called a kernel module. Vanes are kernel modules for Arvo. Arvo currently has eight vanes known as Ames, Behn, Clay, Dill, Eyre, Ford, Gall, and Hall. You can read a short description of each one in Arvo, along with a more thorough explanation of how vanes communicate than the following.

Vanes communicate with one another by exchanging chunks of data known as moves. We will get into more detail above what a move is in our example of Behn below, (and also see Arvo) but at a high level one should think of a move as either a request from one vane to another or a response to a request. In our egg timer app, Gall and Behn will be exchanging moves.

Notably, vanes are not something that a user ever interacts with directly. They live in an area of the memory called "kernel space", while all user processes live in user space. Applications in user space make system calls to the kernel, which the kernel may or may not obey (for security and stability purposes). These requests could be things such as asking for the time, getting the input of a hardware device, making an http request, or moving a file. In our egg timer app, we will be making system calls to ask Behn to handle the timer aspect.

Behn

In this section we introduce the simplest Arvo vane, Behn. This is not exhaustive, but just enough to know what is going on inside of Behn when we make a system call to it in the egg timer app. We discuss relevant types, some arms that are only called internally, and arms that may be called by another vane via a move.

Behn is an Arvo vane that acts as a simple timer. It allows vanes and applications to set timer events, which are managed in a simple priority queue.

An app or vane can make a request to Behn to be informed when a specified amount of time has elapsed. Behn then produces effects to begin a unix timer for that request. The unix timer informs Behn after the specified time has elapses, which then informs the requestee. There is no guarantee that the requestee will be informed of the elapsed timer at the precise moment it requested, or even that it will be close. The only guarantee is that the requester will be informed at some point after the allotted time has passed.

Types

We review here some of the commonly used types in the Behn vane as found in behn.hoon.

timer

+$  timer  [date=@da =duct]

A timer consists of a @da (an absolute date at which the timer will go off) and a duct (representing the causal stack that began the timer). As part of its state, Behn keeps track of a list of timers:

behn-state

+$  behn-state
  $:  timers=(list timer)
      unix-duct=duct
      next-wake=(unit @da)
      drips=drip-manager
  ==

We see that a behn-state is a list of all timers that Behn is currently keeping track of. unix-duct is Behn's link to the unix timer. next-wake is the next time that Behn is supposed to inform a requestee that the specified timer has elapsed. We will ignore drips for the purpose of this tutorial.

move

Arvo vanes communicate via moves:

+$  move  [p=duct q=(wind note gift:able)]

A duct is a call stack, which is a list of paths that represent a step in a causal chain of events. A wind is a kernel action. See the following arm from arvo.hoon:

++  wind                                                ::  new kernel action
          |*  [a=mold b=mold]                           ::  forward+reverse
          $%  [%pass p=path q=a]                        ::  advance
              [%slip p=a]                               ::  lateral
              [%give p=b]                               ::  retreat
          ==   

Here we see that wind produces a wet gate that takes in two molds, which for the move type for Behn are notes and gifts. When a vane needs to request something of another vane, it %passes a note. When a vane produces a result that was requested, it %gives a gift to the callee.

Internal arms

The following arms are part of the event-core of Behn, which are essentially the internal functions of the Behn vane that are not directly accessible to other vanes and applications. We will not review all of them here, we only list some of most important ones.

++set-unix-wake

This arm is what Behn uses to tell the unix timer when to %wake Behn.

++set-timer

This arm is a dry gate that takes in a timer and adds it to the list of timers in the state. ++set-timer automatically places the new timer in chronological order, so that the timer at the front of the list of timers is the timer that will expire the soonest.

++wait

++  wait  |=(date=@da set-unix-wake(timers.state (set-timer [date duct])))

This arm is called when Behn is %passed a note telling Behn to %wait. This adds a timer to the list of timers in the state and correspondingly tells the unix timer to tell Behn when the specified time has passed.

++rest

++  rest  |=(date=@da set-unix-wake(timers.state (unset-timer [date duct])))

This undoes a ++wait operation. That is, ++rest removes a timer from the list of timers.

++wake

This arm is ultimately called by the unix timer to let Behn know that a previously specified amount of time has elapsed.

++born

++  born  set-unix-wake(next-wake.state ~, unix-duct.state duct)

This arm is called when Behn is first launched. It gives Behn a duct to the unix timer and initializes the behn-state with the face state with a null list of timers.

External interface

Arvo talks to Behn via four arms, ++call, ++load, ++scry, ++stay, and ++take, which ultimately call arms in the event-core of which we have listed a subset above. Here we will only look at ++call and ++take.

++call

When Behn is %passed a note from another vane, that is enacted upon by Arvo via the ++call gate. The note specifies what action to take, which are references to the internal arms. This is done by including in the note a symbol such as %born, %rest, %wait, or %wake, which ultimately call the arms with those names in the event-core.

++take

When Behn is ready to inform another vane or application that a timer has elapsed, Arvo activates the ++take gate. That is, Arvo takes a gift from Behn that includes the symbol %wake, and %passes it to the the requestee. %wake is the only kind of gift that Behn can make, but other vanes may possess multiple responses.