Module netstack3_core::context

source ·
Expand description

Execution contexts.

This module defines “context” traits, which allow code in this crate to be written agnostic to their execution context.

All of the code in this crate operates in terms of “events”. When an event occurs (for example, a packet is received, an application makes a request, or a timer fires), a function is called to handle that event. In response to that event, the code may wish to emit new events (for example, to send a packet, to respond to an application request, or to install a new timer). The traits in this module provide the ability to emit new events. For example, if, in order to handle some event, we need the ability to install new timers, then the function to handle that event would take a TimerContext parameter, which it could use to install new timers.

Structuring code this way allows us to write code which is agnostic to execution context - a test fake or any number of possible “real-world” implementations of these traits all appear as indistinguishable, opaque trait implementations to our code.

The benefits are deeper than this, though. Large units of code can be subdivided into smaller units that view each other as “contexts”. For example, the ARP implementation in the [crate::device::arp] module defines the ArpContext trait, which is an execution context for ARP operations. It is implemented both by the test fakes in that module, and also by the Ethernet device implementation in the crate::device::ethernet module.

This subdivision of code into small units in turn enables modularity. If, for example, the IP code sees transport layer protocols as execution contexts, then customizing which transport layer protocols are supported is just a matter of providing a different implementation of the transport layer context traits (this isn’t what we do today, but we may in the future).

Synchronized vs Non-Synchronized Contexts

Since Netstack3 aspires to be multi-threaded in the future, some resources need to be shared between threads, including resources which are accessed via context traits. Sometimes, this resource sharing has implications for how the context’s behavior is exposed via its API, and thus has implications for how the consuming code needs to interact with that API.

For this reason, some modules require two different contexts - a “synchronized” context and a “non-synchronized” context. Traits implementing a synchronized context are named FooSyncContext while traits implementing a non-synchronized context are named FooContext. Note that the implementation of a non-synchronized context trait may still provide access to shared resources - the distinction is simply that the consumer doesn’t need to be aware that that’s what’s happening under the hood. As a result, this shared access is not assumed by the context trait itself. Its API is designed just as it would be if exclusive access were assumed. For example, even though a multi-threaded implementation of TimerContext would need to synchronize on a shared set of timers, the TimerContext trait is designed as though it were providing a vanilla, unsynchronized container.

When synchronized contexts provide access to state, they do it via a with-style API:

trait FooSyncContext<S> {
    fn with_state<O, F: FnOnce(&mut S) -> O>(&mut self, f: F) -> O;
}

This style is easy to implement when state is shared and mutated via interior mutability (e.g., using mutexes), and is also easy to implement when state is accessed exclusively (e.g., when writing a test fake). It also makes it clear that a critical section is starting and ending, and thus makes it clear to the programmer that they’re performing a potentially expensive operation, and hopefully encourages them to minimize the duration of the critical section.

Since the with_xxx method operates on &mut self, it prevents other operations on the context from happening concurrently. This prevents deadlocks which occur as a result of a single mutex being locked while it is held by the locking thread - in other words, it prevents lock reentrance.

Traits