Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# clang-tidy gate for the taut library (src/ + public headers). The enabled families are
# the high-signal, low-false-positive ones (real correctness/perf/portability defects and
# the static analyzer). The disabled entries are pure-style or noisy checks that flag no
# actual defects in this codebase leaving them on would make the gate break on cosmetics
# actual defects in this codebase - leaving them on would make the gate break on cosmetics
# rather than bugs. Verified: src/ + include/taut pass clean with WarningsAsErrors.
Checks: >
-*,
Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ option(TAUT_BUILD_FUZZERS "Build libFuzzer targets (clang only)" OFF)
option(TAUT_INSTALL "Generate install and package-config rules" ${TAUT_DEFAULT_EXTRAS})

# The core library. CORE modules (codec, window, rto, timers, rx, flow, swim, loop)
# are added source-by-source through the PLAN §2 module loop each behind its own
# are added source-by-source through the PLAN §2 module loop - each behind its own
# design brief + comprehension gate. For now it carries only version.cc plus the
# public API sketch headers (Week 1 S1).
add_library(taut STATIC
Expand Down Expand Up @@ -84,7 +84,7 @@ if(TAUT_BUILD_FUZZERS)
add_subdirectory(fuzz)
endif()

# ---- Benchmarks (§7) opt-in; not part of the default dev/CI loop ---------------------
# ---- Benchmarks (§7) - opt-in; not part of the default dev/CI loop ---------------------
# taut vs kernel-TCP vs ENet latency matrix. Off by default (ENet FetchContent needs network
# at configure time; sanitizers would corrupt the numbers). Configure a release build with
# -DTAUT_BUILD_BENCH=ON to build bench/latency_bench, tcp_baseline, enet_baseline.
Expand Down
2 changes: 1 addition & 1 deletion bench/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Benchmark binaries (§7). Enabled with -DTAUT_BUILD_BENCH=ON (see root CMakeLists); Linux
# only (real sockets + ENet). Kept out of the default dev/CI build so the core loop stays
# fast and network-free. Run these from a *release* build sanitizers would dominate the
# fast and network-free. Run these from a *release* build - sanitizers would dominate the
# latency numbers.
include(FetchContent)

Expand Down
4 changes: 2 additions & 2 deletions bench/enet_baseline.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ENet baseline for the latency matrix (§7). Same 512 B workload over an ENet reliable channel
// (channel 0, ENET_PACKET_FLAG_RELIABLE). ENet is the closest existing peer to taut reliable
// messages over UDP with its own ARQ and RTT-based retransmit so it is the more honest "did
// (channel 0, ENET_PACKET_FLAG_RELIABLE). ENet is the closest existing peer to taut - reliable
// messages over UDP with its own ARQ and RTT-based retransmit - so it is the more honest "did
// you actually beat a real library" comparison than TCP alone. One ENet packet == one message
// (no length prefix). Modes: rr (headline round-trip probe), latency (open-loop Poisson),
// throughput (clean-link goodput).
Expand Down
6 changes: 3 additions & 3 deletions bench/latency_bench.cc
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// taut message-latency load generator (§7). One binary, several modes over a symmetric pair
// of taut Sessions on real UDP sockets. Roles: --role receiver binds (bind,port); --role
// sender binds (bind,port+1) and targets (addr,port). The taut Session (poll/tick) has no
// epoll driver yet, so both roles pump poll()+tick() in a tight loop exactly what the
// epoll driver yet, so both roles pump poll()+tick() in a tight loop - exactly what the
// future Node event loop will do per iteration.
//
// Modes:
// rr : closed-loop request-reply (one outstanding). Client sends a 512 B request,
// server echoes it, client records the round-trip. This is the headline
// latency-vs-loss probe (netperf TCP_RR style) rate-independent, unbiased,
// latency-vs-loss probe (netperf TCP_RR style) - rate-independent, unbiased,
// and it isolates per-message recovery latency (RTO + head-of-line) from
// throughput throttling. See docs/BENCHMARKS.md for why this replaces open-loop
// Poisson as the headline.
Expand Down Expand Up @@ -37,7 +37,7 @@
namespace {

// Decorates a UdpTransport to count datagrams/bytes actually put on (and taken off) the
// socket including retransmits and pure acks. Lets the taut sender report its app-level
// socket - including retransmits and pure acks. Lets the taut sender report its app-level
// wire cost (UDP payload; IP/UDP headers are added by the kernel and show up in wire.csv).
class CountingTransport : public taut::UdpTransport {
public:
Expand Down
4 changes: 2 additions & 2 deletions bench/scripts/netns_setup.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# netns_setup.sh the PLAN §6.5 fault-injection fixture.
# netns_setup.sh - the PLAN §6.5 fault-injection fixture.
#
# Builds a veth pair spanning two network namespaces and applies a symmetric netem
# impairment (loss / delay+jitter / reorder / duplicate) to BOTH directions, with
Expand Down Expand Up @@ -114,7 +114,7 @@ do_up() {
offloads_off
apply_netem add

echo "netns_setup: up ${NS_A}(${IP_A}) <-> ${NS_B}(${IP_B}), netem [$(netem_args)] each way"
echo "netns_setup: up - ${NS_A}(${IP_A}) <-> ${NS_B}(${IP_B}), netem [$(netem_args)] each way"
}

do_netem() {
Expand Down
4 changes: 2 additions & 2 deletions bench/scripts/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ def make_plots(s_rr, by_over, by_thr, data_dir):
med = [agg(by_over[k][l])[0] for l in losses]
ax.plot(losses, med, marker="s", label=SERIES_LABEL[k], color=colors.get(k))
ax.axhline(1.0, color="gray", ls="--", alpha=0.6, label="ideal (1.0)")
ax.set_title("Bandwidth overhead vs loss (sustained load) taut pays for its tail")
ax.set_title("Bandwidth overhead vs loss (sustained load) - taut pays for its tail")
ax.set_xlabel("loss (%, each direction)")
ax.set_ylabel("bytes on wire / goodput bytes")
ax.grid(True, alpha=0.3)
Expand All @@ -250,7 +250,7 @@ def make_plots(s_rr, by_over, by_thr, data_dir):
vals = [agg(by_thr[k])[0] for k in keys]
ax.bar([SERIES_LABEL[k] for k in keys], vals,
color=[colors.get(k) for k in keys])
ax.set_title("Clean-link (0% loss) throughput TCP/ENet win")
ax.set_title("Clean-link (0% loss) throughput - TCP/ENet win")
ax.set_ylabel("goodput (Mbit/s)")
ax.grid(True, axis="y", alpha=0.3)
for i, v in enumerate(vals):
Expand Down
6 changes: 3 additions & 3 deletions bench/scripts/run_matrix.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# - taut, each class in TAUT_CLASSES latency_bench
# and captures per-run latency percentiles (bench/data/latency.csv), send-side counts
# (send.csv) and interface-level bytes-on-wire (wire.csv). Also runs a 0%-loss saturating
# throughput point (throughput.csv) the clean-link axis where TCP/ENet are expected to win.
# throughput point (throughput.csv) - the clean-link axis where TCP/ENet are expected to win.
#
# Re-execs under sudo (netns/tc need NET_ADMIN). The binaries do not need root; they inherit
# it only because `ip netns exec` runs them in the namespace.
Expand Down Expand Up @@ -145,7 +145,7 @@ fi

# ---- optional: open-loop Poisson sustained-load latency (RUN_OPENLOOP=1) ---------------
# Shows delivery ratio + coordinated-omission-corrected latency; TCP/ENet saturate under
# loss (received << offered) that divergence is a finding, not a bug (see BENCHMARKS.md).
# loss (received << offered) - that divergence is a finding, not a bug (see BENCHMARKS.md).
if [[ "${RUN_OPENLOOP:-0}" == "1" ]]; then
for loss in ${LOSSES}; do
apply_netem "${loss}"
Expand All @@ -156,7 +156,7 @@ if [[ "${RUN_OPENLOOP:-0}" == "1" ]]; then
done
fi

# ---- clean-link (0%) saturating throughput the axis where TCP/ENet win ---------------
# ---- clean-link (0%) saturating throughput - the axis where TCP/ENet win ---------------
if [[ "${RUN_THROUGHPUT:-1}" == "1" ]]; then
apply_netem 0
echo "=== throughput @ 0% loss (RTT ${RTT}ms) ==="
Expand Down
14 changes: 7 additions & 7 deletions bench/scripts/soak.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# soak.sh the PLAN §9 Week-2 HARD CHECKPOINT and the loss sweep.
# soak.sh - the PLAN §9 Week-2 HARD CHECKPOINT and the loss sweep.
#
# Checkpoint: transfer a 10 MB file over taut class 2 across the veth/netns fixture at the
# §6.5 impairment (loss 5%, delay 30ms +/- 10ms, reorder 1%, duplicate 0.5%), assert the
Expand Down Expand Up @@ -52,7 +52,7 @@ while [[ $# -gt 0 ]]; do
done

if [[ "$(id -u)" -ne 0 ]]; then
echo "soak.sh: must run as root (use sudo) netns exec needs it" >&2
echo "soak.sh: must run as root (use sudo) - netns exec needs it" >&2
exit 1
fi
if [[ ! -x "${BIN}/send_file" || ! -x "${BIN}/recv_file" ]]; then
Expand Down Expand Up @@ -116,7 +116,7 @@ run_one() {
return 0
}

echo "=== taut netem soak bin=${BIN} ==="
echo "=== taut netem soak - bin=${BIN} ==="
echo

# --- Checkpoint: 20 consecutive green at the full §6.5 impairment (loss 5%). ---
Expand All @@ -130,25 +130,25 @@ for i in $(seq 1 "${RUNS}"); do
printf " run %2d/%d: PASS (%ss, %s kB/s)\n" "${i}" "${RUNS}" \
"$(echo "${result}" | cut -d' ' -f1)" "$(echo "${result}" | cut -d' ' -f2)"
else
printf " run %2d/%d: FAIL streak broken\n" "${i}" "${RUNS}"
printf " run %2d/%d: FAIL - streak broken\n" "${i}" "${RUNS}"
break
fi
done

echo
if [[ "${green}" -eq "${RUNS}" ]]; then
echo "CHECKPOINT RESULT: ${green}/${RUNS} GREEN PASS"
echo "CHECKPOINT RESULT: ${green}/${RUNS} GREEN - PASS"
checkpoint_ok=1
else
echo "CHECKPOINT RESULT: ${green}/${RUNS} green FAIL (need ${RUNS} consecutive)"
echo "CHECKPOINT RESULT: ${green}/${RUNS} green - FAIL (need ${RUNS} consecutive)"
checkpoint_ok=0
fi

# --- Sweep: goodput vs loss. ---
if [[ "${DO_SWEEP}" -eq 1 ]]; then
echo
echo ">>> SWEEP: $(awk "BEGIN{printf \"%.1f\", ${SWEEP_SIZE}/1048576}") MB at loss {0,1,5,10,20}%"
echo " (kB/s here is rough soak goodput to show the loss trend NOT the §7 benchmark,"
echo " (kB/s here is rough soak goodput to show the loss trend - NOT the §7 benchmark,"
echo " which uses proper methodology + TCP/ENet baselines and is owned by feat/bench)"
printf " %-6s %-8s %-10s %-8s\n" "loss%" "result" "wall_s" "kB/s"
for loss in 0 1 5 10 20; do
Expand Down
2 changes: 1 addition & 1 deletion cmake/Sanitizers.cmake
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Global sanitizer wiring. Set TAUT_SANITIZE to a comma-separated list
# (e.g. "address,undefined") and every target in the build is instrumented and linked
# consistently required for ASan/UBSan to work across the whole process. The `dev`
# consistently - required for ASan/UBSan to work across the whole process. The `dev`
# preset sets address,undefined; release leaves it empty.
set(TAUT_SANITIZE "" CACHE STRING "Comma-separated sanitizers, e.g. address,undefined")

Expand Down
2 changes: 1 addition & 1 deletion demo/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Demo binaries (Linux). Relaxed warning set demos aren't library code.
# Demo binaries (Linux). Relaxed warning set - demos aren't library code.
add_executable(echo_server echo_server.cc)
target_link_libraries(echo_server PRIVATE taut taut_test_warnings)

Expand Down
2 changes: 1 addition & 1 deletion demo/echo_server.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Minimal echo server over the taut event loop (Week 1 S3 skeleton). Binds a UDP socket
// and echoes every datagram back to its sender. No reliability yet under loss, drops
// and echoes every datagram back to its sender. No reliability yet - under loss, drops
// are expected. Ctrl-C to stop.
#include <cstdint>
#include <cstdio>
Expand Down
4 changes: 2 additions & 2 deletions demo/mesh_node.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// mesh_node a 5-node SWIM membership demo on the deterministic in-process SimNet
// mesh_node - a 5-node SWIM membership demo on the deterministic in-process SimNet
// (§5.9 deliverable). No sockets/epoll: one virtual clock, five Swim instances, seeded so the
// run is byte-reproducible. It converges the mesh, partitions one node, and heals it, printing
// the live membership table and the measured time-to-detect / time-to-reconverge.
Expand Down Expand Up @@ -38,7 +38,7 @@ std::uint64_t ekey(const taut::Endpoint& e) {
return (static_cast<std::uint64_t>(e.addr_be) << 16) | e.port_be;
}

// See tests/unit/swim_test.cc a transport decorator that severs links in both directions to
// See tests/unit/swim_test.cc - a transport decorator that severs links in both directions to
// model a partition, without touching the shared SimNet.
class LinkFilter : public taut::UdpTransport {
public:
Expand Down
2 changes: 1 addition & 1 deletion demo/recv_file.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// recv_file receive a file from send_file over taut class 2 (ReliableOrdered).
// recv_file - receive a file from send_file over taut class 2 (ReliableOrdered).
//
// The first class-2 message is an 8-byte little-endian length header; the rest are the file,
// delivered in order (class 2 guarantees exactly-once, in-order delivery), so we simply
Expand Down
6 changes: 3 additions & 3 deletions demo/send_file.cc
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// send_file transfer a file to recv_file over taut class 2 (ReliableOrdered).
// send_file - transfer a file to recv_file over taut class 2 (ReliableOrdered).
//
// The file is read into memory, chunked into <= kChunk-byte messages, and streamed over a
// single Session. Message 0 is an 8-byte little-endian length header (see demo/file_xfer.h);
// messages 1..N are the file. We fill the send window, then poll()/tick() the Session until
// every message is acknowledged (in_flight() == 0), which with cumulative acks means the
// every message is acknowledged (in_flight() == 0), which - with cumulative acks - means the
// receiver has delivered the whole file in order. sha256 of the sent bytes is printed so it
// can be compared with recv_file's.
//
Expand Down Expand Up @@ -150,7 +150,7 @@ int main(int argc, char** argv) {
const std::size_t n = std::min(demo::kChunk, data.size() - off);
if (!session.send(taut::Class::ReliableOrdered,
std::span<const std::byte>(data.data() + off, n))) {
return; // window full drain acks and retry next iteration
return; // window full - drain acks and retry next iteration
}
off += n;
}
Expand Down
6 changes: 3 additions & 3 deletions include/taut/codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ enum class PacketType : std::uint8_t {
};

// Header flag bits (§5.2). SACK (bit0) is parsed by this codec; membership (bit1) and
// keyed-CRC (bit2) are not yet handled a decoder rejects those bits as Unsupported and
// keyed-CRC (bit2) are not yet handled - a decoder rejects those bits as Unsupported and
// encode refuses to emit them.
enum class Flag : std::uint8_t {
SackPresent = 0x01,
MembershipPiggyback = 0x02,
KeyedCrc = 0x04,
};

// Why decode rejected a datagram makes fuzz failures categorizable.
// Why decode rejected a datagram - makes fuzz failures categorizable.
enum class DecodeError : std::uint8_t {
Ok = 0,
TooShort, // fewer bytes than the base header
Expand Down Expand Up @@ -65,7 +65,7 @@ struct Packet {
};

// Serialize `pkt` into `out`. Returns bytes written, or 0 on failure (buffer too small,
// payload > 65535 or datagram > 1200, or an unsupported flag bit set only SackPresent is
// payload > 65535 or datagram > 1200, or an unsupported flag bit set - only SackPresent is
// emittable). When Flag::SackPresent is set, the 8-byte bitmap is written at offset 21.
// The CRC is computed and written.
std::size_t encode(const Packet& pkt, std::span<std::byte> out);
Expand Down
2 changes: 1 addition & 1 deletion include/taut/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace taut {
// every field is a documented design knob.
struct Config {
// The thesis knob (§5.5): retransmit floor well below TCP's ~200 ms Linux minimum.
// Safe on a closed mesh, unsafe on the open internet be able to explain why.
// Safe on a closed mesh, unsafe on the open internet - be able to explain why.
std::chrono::milliseconds rto_floor{25};

// Fixed send window in packets (§5.4). No congestion control in v1 (§5.8).
Expand Down
2 changes: 1 addition & 1 deletion include/taut/crc32c.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace taut {
// One-shot: CRC of a byte span.
std::uint32_t crc32c(std::span<const std::byte> data);

// Incremental API used by the codec's two-chunk CRC (bytes before the crc field,
// Incremental API - used by the codec's two-chunk CRC (bytes before the crc field,
// then 4 zero bytes, then bytes after). Usage:
// auto s = crc32c_init();
// s = crc32c_update(s, chunk_a);
Expand Down
4 changes: 2 additions & 2 deletions include/taut/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace taut {

// Public transport node (§5.1). Single-threaded by design (§5.7); no exceptions cross
// this boundary fallible calls return bool/expected-style results. This is the Week 1
// this boundary - fallible calls return bool/expected-style results. This is the Week 1
// S1 API *sketch*: declarations only. The implementation lands module-by-module through
// the PLAN §2 loop (loop.cc / transport.cc), each behind its comprehension gate.
class Node {
Expand All @@ -35,7 +35,7 @@ class Node {
PeerId add_peer(std::string_view addr, std::uint16_t port);

// Enqueue a message for delivery under the given reliability class.
// Returns false on backpressure (window full) the caller decides what to do.
// Returns false on backpressure (window full) - the caller decides what to do.
bool send(PeerId peer, Class cls, ByteSpan payload);

void on_message(MessageHandler handler);
Expand Down
4 changes: 2 additions & 2 deletions include/taut/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ namespace taut {
// Reliable session with a single peer over a UdpTransport. Covers the send-buffer ring +
// cumulative acks + SACK/fast-retransmit + RTO retransmit (send path), the three reliability
// classes on the receive path (§5.3), and receiver-driven flow control with a zero-window
// persist probe (§5.6). Driven by poll() (process inbound) and tick() (fire timers) the
// persist probe (§5.6). Driven by poll() (process inbound) and tick() (fire timers) - the
// real event loop and the deterministic sim harness both just call those.
//
// Wire note: cum_ack here means "next expected sequence" (lowest reliable seq not yet
// received), i.e. the TCP-style ack convention, which handles gaps unambiguously; this
// refines PLAN §5.2's "highest received" wording (see docs/DESIGN-window.md). Consequently a
// SACK bit i marks reliable seq (cum_ack + 1 + i) the seq at cum_ack is by definition the
// SACK bit i marks reliable seq (cum_ack + 1 + i) - the seq at cum_ack is by definition the
// gap the receiver is still missing.
class Session {
public:
Expand Down
2 changes: 1 addition & 1 deletion include/taut/sim_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct Impairments {
class SimNet;

// One endpoint on a SimNet, satisfying UdpTransport. Created via SimNet::endpoint; its
// clock and I/O are the SimNet's. fd() is -1 (not epoll-pollable sim tests pump manually).
// clock and I/O are the SimNet's. fd() is -1 (not epoll-pollable - sim tests pump manually).
class SimEndpoint : public UdpTransport {
public:
std::size_t send(const Endpoint& to, std::span<const std::byte> data) override;
Expand Down
2 changes: 1 addition & 1 deletion include/taut/swim.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct SwimConfig {
// Membership gossip rides in the packet PAYLOAD for now (no header/flags changes); the §5.2
// header-piggyback path is a later merge step. Uses PacketType::{Ping,PingReq,Pong,Join} with
// Class::Unreliable. Sender identity comes from the transport (`RecvResult::from`), mirroring
// real SWIM's use of the UDP source address it is never carried in the payload.
// real SWIM's use of the UDP source address - it is never carried in the payload.
//
// Driven exactly like Session: poll() drains inbound datagrams, tick() advances the
// time-based state machine off the transport clock. The event loop and the deterministic sim
Expand Down
4 changes: 2 additions & 2 deletions src/codec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ DecodeError decode(std::span<const std::byte> in, Packet& out) {
// Read the SACK-present bit to size the packet before the CRC check (the field is at
// offset 3, always within the 21 B we've already length-checked). A corrupted bit0 makes
// header_len/total wrong, so decode fails as LengthOverrun or, if sizes still line up,
// BadCrc never a misparse (see DESIGN-codec.md check-order note).
// BadCrc - never a misparse (see DESIGN-codec.md check-order note).
const std::uint8_t flags = load_u8(in, 3);
const bool sack = (flags & static_cast<std::uint8_t>(Flag::SackPresent)) != 0;
const std::size_t header_len = kBaseHeaderSize + (sack ? kSackSize : 0);
Expand All @@ -139,7 +139,7 @@ DecodeError decode(std::span<const std::byte> in, Packet& out) {

// Verify integrity before trusting any other field: CRC over [0,17) + 4 zero bytes
// (the zeroed crc field) + [21, total). The [21,total) chunk covers the SACK section and
// payload alike. Uses the incremental API (D11) no copy.
// payload alike. Uses the incremental API (D11) - no copy.
const std::uint32_t stored_crc = load_u32_le(in, kCrcOffset);
static constexpr std::array<std::byte, 4> kZeroCrc{};
std::uint32_t crc = crc32c_init();
Expand Down
Loading
Loading