This document describes several of the factors that constrain the design of libtock_core.
Tock is designed to run on hardware with limited program storage and limited RAM. On Google's H1 chip, the Tock kernel and apps are limited to a 256 KiB flash region. The HiFive1 Rev B board has only 16 KiB of RAM.
Note that a Tock system may have multiple process binaries, each of which uses libtock_core, so libtock_core must minimize its size impact to the extent possible.
A process binary is generally stored in non-volatile memory and contains:
.crt0_header section).text section).rodata section).data section)A process' memory section is in RAM and contains:
.bss section).data section)libtock_core developers should consider the space usage of their code, and where the space usage is (non-volatile memory or RAM). Important note: the .data section consumes space in both non-volatile memory and RAM!
Most apps will not use all functionality in libtock_core. The size impact of using libtock_core in an app should be commensurate with the libtock_core functionality that app uses. If done correctly, this will allow libtock_core's users to build Tock systems with multiple small, single-purpose apps without excessive code duplication.
Programmatic tests are important to verify that new functionality works correctly and to keep existing functionality working through nontrivial refactorings. Different types of tests offer different capabilities:
libtock_core code, as the code they test must run on both Tock and the host OS. These tests must either test code that does not directly use system calls, or must direct the system calls to a fake Tock kernel. These tests can be fast (compile and run in seconds), and can easily simulate a variety of failure modes (e.g. kernel errors) that may be difficult to generate with a real Tock kernel.libtock_core's interaction with a real Tock kernel, albeit with limited hardware access.libtock_core‘s functionality but also the Tock kernel’s interaction with the real hardware. However, automating these tests is difficult, and currently we do not have a way to run hardware tests in CI.If libtock_core is functional on real hardware with a real kernel, it should be able to support emulation tests and hardware tests. However, host-based unit tests require more work. In its intended use case, libtock_core is not supposed to be portable, supporting only the Tock kernel. Hoever, host-based unit tests are very valuable, both for rapid development and for testing error handling code. To support host-based tests, libtock_core must be built out of portable pieces that can be unit tested, even though the library as a whole is not portable.
Testing pieces of libtock_core against a fake kernel can be done using dependency injection, but most dependency injection techniques have considerable code size and RAM usage costs. libtock_core needs either a dependency injection technique that has minimal size impact or an alternative mechanism for testing libtock_core's system call error handling logic.
The general Rust ecosystem has converged on futures as its building block for interoperable asynchronous APIs, but futures have a size cost that makes them impractical for some use cases of libtock_core. Although libtock_core is still a work in progress (see issue 217), it will probably use an asynchronous API design based on the Asynchronous Components using Zero Sized Type Pointers exploration.
In order to interoperate with other libraries as well as the async/await functionality built in to Rust, we need libtock_core to interface well with futures. In addition, there are several use cases of libtock_core that consist primarily of synchronous code, so we need to be able to use libtock_core's APIs in a synchronous manner.
The libtock-c developers are already familiar with some of the complications that come up when synchronous code is interfaced to asynchronous code. For example, calling printf -- an extremely common debugging tool -- causes callbacks to run in the background. This causes surprising and hard-to-debug reentrancy issues.
Some of users of libtock_core intend to use it in applications that will undergo code audits for security certifications. To make this auditing practical, it is important to allow users to use libtock_core without pulling in a large dependency tree.
As a general rule, a libtock_core should avoid having required dependencies unless those dependencies are minimal (that is, avoiding the dependency would require reimplementing nearly all of it). Dependencies included in the Rust toolchain (such as the core crate and its dependencies) are an exception, as they are a part of the language itself.
alloc dependencyIt should be possible to use libtock_core without bringing in the alloc. Of course, it is fine for libtock_core to offer a memory allocator as an optional feature.