tin  1.5.9
tin API

tin API

Purpose

tin is the combined C++ API for deterministic real-time systems. It includes bounded runtime primitives, typed state-machine support, portable I/O contracts, and platform adapter contracts.

New contributors should start with developer onboarding before changing architecture, runtime, platform, tooling, or docs. The contributor path includes a framework walkthrough, minimal complete tin app, decision guide, design invariants, and testing map.

Use tin when a project needs:

  • runtime primitives for fixed-capacity queues, channels, tasks, and timers;
  • tsm behavior definitions and runtime policies;
  • tio I/O facade contracts;
  • thal platform and HAL boundary contracts;
  • build targets for the same API surfaces.

API Entry Points

  • tin/runtime.h for runtime primitives and the concrete tin::queue, tin::channel, tin::actor_link, and tin::actor_group aliases.
  • tin::tick_duration, tin::tick_count, and tin::ticks(n) for semantic scheduler ticks. Define TSM_TICK_REP to select a wider unsigned representation and TSM_TICK_PERIOD to select a std::ratio tick period.
  • tin::queue<T, StoragePolicy, OverflowPolicy> for explicit FIFO storage.
  • tin::channel<T, Capacity> for bounded local value movement with handle views.
  • tin::actor_group<Actors...> and typed actor ports for composing local runtime components under a caller-owned execution loop.
  • tsm::task, tsm::after_ticks(n), tsm::yield(), and tsm::cooperative_executor for static cooperative workflows documented in coroutines.
  • tin::dispatch_context for target-neutral tick/sequence metadata.
  • tin::event_sink<Sink, Event> and tin::event_source<Source, Event> for adapter-facing concepts.

Use the generated namespace reference for exact declarations.

Example: Bounded Sample Channel

This example moves ADC samples from a driver-shaped ingress point into a bounded runtime channel. The channel's capacity and overflow behavior are part of the type, so queue pressure is visible in code review.

#include <cstdint>
#include "tin/runtime.h"
struct SensorSample {
std::uint16_t millivolts{};
};
void adc_ready_from_isr(SensorSample sample) {
(void)samples.try_send_from_isr(sample);
}
bool read_next_sample(SensorSample& out) {
return samples.try_receive(out);
}
Definition: sync.h:507
bool try_send_from_isr(T const &value)
Definition: sync.h:539
bool try_receive(T &value)
Definition: sync.h:549
Tin runtime-kernel facade.

Handles can expose separate send and receive views without transferring channel ownership:

auto tx = samples.sender();
auto rx = samples.receiver();
(void)tx.try_send(SensorSample{ .millivolts = 1200 });
SensorSample sample{};
if (rx.try_receive(sample)) {
// Convert the sample into product behavior or an adapter record.
}
runtime::sender< T > sender() noexcept
Definition: sync.h:588
runtime::receiver< T > receiver() noexcept
Definition: sync.h:593

Latest-sample use cases can use a channel policy that overwrites the newest accepted value while preserving a non-consuming reader:

using LatestVoltage =
LatestVoltage latest_voltage;
auto latest = latest_voltage.latest_reader();
(void)latest_voltage.try_send(SensorSample{ .millivolts = 3300 });
SensorSample observed{};
(void)latest.latest(observed);
runtime::latest_reader< T > latest_reader() const noexcept
Definition: sync.h:598

Example: Adapter Concepts

Adapters describe their boundary with concepts:

#include "tin/runtime.h"
struct OverVoltage {};
template<typename Runtime>
requires tin::event_sink<Runtime, OverVoltage>
void publish_fault(Runtime& runtime) {
(void)runtime.send_event(OverVoltage{});
}
requires(!has_transition_type_c< T > &&has_transition_member_c< T >) struct transitions_of< T >
Definition: transition.h:479
detail::runtime_impl< Definition, Policy, MachinePolicy > Runtime
Definition: runtime.h:531

The concept states only that the sink can accept a local typed event. Ownership, threading, transport, and middleware policy remain outside tin.

Example: Actor Composition

Actors are the tin composition unit for systems with several local components that advance under a caller-owned loop. A source actor can publish a typed value, a runtime actor can own behavior, and a sink actor can consume the resulting report.

#include "tin/runtime.h"
struct Sample {
int value{};
};
struct SampleSource {
using event_type = Sample;
bool publish(Sample sample) {
return samples.try_send(sample);
}
bool try_receive(Sample& sample) {
return samples.try_receive(sample);
}
};
struct SampleSink {
bool send_event(Sample sample) {
total += sample.value;
return true;
}
bool step() { return false; }
std::size_t drain() { return 0; }
bool empty() const { return true; }
std::size_t pending_events() const { return 0; }
int total{};
};
SampleSource source;
SampleSink sink;
auto output = tin::make_output_port<Sample>(source);
auto input = tin::make_input_port<Sample>(sink);
auto link = tin::actor_link{ output, input };
tin::actor_group actors{ sink };
(void)source.publish(Sample{ .value = 2 });
(void)link.step();
(void)actors.step();
bool try_send(T const &value)
Definition: sync.h:517
actor_link(Source &, Sink &) -> actor_link< Source, Sink >
actor_group(Actors &...) -> actor_group< Actors... >
auto send_event(Runtime &runtime, Event &&event)
Definition: coroutine.h:972

The ports and link do not allocate, create threads, or register callbacks. If a bounded sink is full, the link keeps one pending sample and retries it later. See actor tutorial and examples/actor_cell for a complete actor example with execution loop and resource accounting.

Related Surfaces