Naut-Peer/include/engine.h
ookami125 f85278fcf2 engine: add engine_peer_list for per-peer status
Enumerates a torrent's connected peers (ip/port/state/rate/bytes/have)
for the UI's peer list — the per-peer detail lost in the engine swap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 00:35:23 -04:00

175 lines
8 KiB
C

/*
* engine.h - Public ABI for the multi-peer download engine.
*
* The engine owns all peer connections via a fixed pool of event loops (one OS
* thread each). Torrents are pinned to a loop ("affinity"); every connection of
* a torrent lives on that loop, so each loop thread is the sole owner of its
* connections, its arena, and its torrents' piece state. The hot path
* (recv -> parse -> handoff -> schedule) is therefore lock-free.
*
* Data plane: each loop owns one arena slab and an SPSC ready-ring; the consumer
* thread drains completed blocks across loops with engine_poll_ready() and
* returns spent slots with engine_release_slot(). Control plane (add torrent,
* add peer, set priorities) is delivered to the owning loop via a command queue.
*
* The legacy single-peer peer_* API (peer.h) is a thin wrapper over a 1-loop /
* 1-torrent / 1-peer engine.
*/
#ifndef TORRENT_ENGINE_H
#define TORRENT_ENGINE_H
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PEER_BLOCK_SIZE 16384u /* BitTorrent block size */
/* Connection/torrent state, mirrored to status. */
typedef enum {
PEER_STATE_IDLE = 0,
PEER_STATE_CONNECTING = 1,
PEER_STATE_HANDSHAKE = 2,
PEER_STATE_CHOKED = 3,
PEER_STATE_RUNNING = 4,
PEER_STATE_STOPPED = 5,
PEER_STATE_ERROR = 6
} peer_state;
typedef enum {
PEER_OK = 0,
PEER_ERR_CONNECT = 1,
PEER_ERR_HANDSHAKE = 2,
PEER_ERR_CLOSED = 3,
PEER_ERR_PROTOCOL = 4,
PEER_ERR_IO = 5,
PEER_ERR_NOMEM = 6
} peer_error;
typedef struct {
uint32_t loop_count; /* event-loop threads (0 => min(ncpu, 8)) */
uint32_t slots_per_loop; /* arena depth per loop in 16 KiB slots (0=>def)*/
uint32_t max_pipeline; /* per-connection outstanding-request cap (0=>def)*/
uint32_t request_timeout_ms;/* re-request a block after this long (0=>def) */
uint32_t recv_buffer_bytes; /* SO_RCVBUF override; 0 => kernel autotuning */
uint32_t encryption; /* 0 = plaintext only; 1 = MSE, offer RC4 +
* plaintext (most compatible); 2 = MSE, require
* RC4 (refuse plaintext) */
uint32_t utp; /* 0 = TCP; 1 = µTP (UDP). May combine with
* encryption to run MSE over µTP. */
uint32_t connect_timeout_ms;/* drop a peer that hasn't finished connecting +
* handshaking within this long (0 => 10000).
* Reclaims unreachable/silent peers instead of
* leaving them stuck. */
uint32_t fallback; /* 1 => if a peer fails before the BitTorrent
* handshake, retry the same endpoint over the
* next transport/encryption combo (TCP+MSE ->
* TCP+plain -> µTP+MSE -> µTP+plain, ordered by
* the utp/encryption prefs above). Reaches far
* more of a real swarm. 0 => single attempt. */
} engine_config;
/*
* One delivered block. Payload lives at:
* (uint8_t*)engine_arena_base(e, loop) + (uint64_t)slot * PEER_BLOCK_SIZE
* valid until returned via engine_release_slot(e, loop, slot).
*/
typedef struct {
uint32_t torrent; /* torrent id this block belongs to */
uint32_t piece;
uint32_t begin;
uint32_t len;
uint32_t loop; /* arena that holds the slot */
uint32_t slot; /* slot index within that loop's arena */
} engine_block;
/* Aggregated status for one torrent. */
typedef struct {
int32_t state; /* peer_state (best connection's state) */
int32_t error; /* peer_error of a failed connection, if any */
uint64_t bytes_received;
uint64_t blocks_received;
uint32_t peers; /* connections attached (incl. failed) */
uint32_t peers_connected; /* handshake completed (choked or running) */
uint32_t peers_failed; /* connections that errored out */
uint32_t outstanding; /* in-flight requests summed across peers */
uint32_t free_slots; /* free arena slots on the torrent's loop */
uint32_t pipeline_target; /* summed adaptive target across peers */
double rate_bps; /* summed download rate */
double rtt_min_ms; /* smallest observed request->block RTT */
} torrent_status;
typedef struct engine engine;
/* Lifecycle. */
engine *engine_create(const engine_config *cfg);
void engine_destroy(engine *e);
/* Register a torrent. Returns its id (>= 0) or -1 on error. The arrays are
* copied. piece_length/total_size/num_pieces describe the torrent geometry. */
int32_t engine_add_torrent(engine *e, const uint8_t info_hash[20],
const uint8_t peer_id[20], uint64_t piece_length,
uint64_t total_size, uint32_t num_pieces);
/* Open a connection to a peer for a torrent (ip = dotted-quad or IPv6 literal). */
int engine_add_peer(engine *e, uint32_t torrent_id, const char *ip, uint16_t port);
/* Priority vector / single priority / re-arm — see peer.h docs for semantics. */
int engine_set_priorities(engine *e, uint32_t torrent_id,
const uint8_t *priorities, uint32_t count);
int engine_set_priority(engine *e, uint32_t torrent_id, uint32_t piece,
uint8_t priority);
int engine_request_piece(engine *e, uint32_t torrent_id, uint32_t piece);
/* Engine-wide download throttle in bytes/sec; 0 = unlimited (default). Bounds
* the aggregate receive rate by gating outgoing block requests. Safe to call at
* any time from any thread. */
void engine_set_download_rate(engine *e, uint64_t bytes_per_sec);
/* Data plane (single consumer thread). */
uint32_t engine_poll_ready(engine *e, engine_block *out, uint32_t max);
void engine_release_slot(engine *e, uint32_t loop, uint32_t slot);
int engine_wait(engine *e, int timeout_ms);
void *engine_arena_base(engine *e, uint32_t loop);
uint64_t engine_arena_bytes(engine *e, uint32_t loop);
uint32_t engine_loop_count(engine *e);
void engine_torrent_status(engine *e, uint32_t torrent_id, torrent_status *out);
/* One connected peer's live state, for the UI's per-peer list. */
typedef struct {
char ip[64];
uint16_t port;
int32_t state; /* peer_state */
int32_t unchoked; /* peer has unchoked us */
uint64_t bytes_received; /* total payload bytes from this peer */
double rate_bps; /* current download rate from this peer */
uint32_t have_pieces; /* pieces this peer advertises */
uint32_t num_pieces; /* torrent piece count (for progress ratio) */
} engine_peer_info;
/* Fill `out` with up to `max` connected peers (handshake completed) of a
* torrent; returns the number written. Best-effort snapshot read on the caller
* thread (like engine_torrent_status), so fine for status, not control. */
uint32_t engine_peer_list(engine *e, uint32_t torrent_id, engine_peer_info *out,
uint32_t max);
/* Diagnostic: write a human-readable dump of one torrent's piece-selection and
* per-connection state to `out`. Reports, for every still-wanted piece
* (priority > 0), whether a peer has claimed it (requested), how many connected
* peers advertise it (availability), and how many in-flight block requests it
* has across all peers — the data needed to tell apart a stuck piece no peer
* has, one a dead/idle peer claimed but never delivered, and one the scheduler
* is simply not picking. Reads loop-owned state from the caller's thread without
* locking (like engine_torrent_status), so it is a best-effort snapshot meant
* for debugging, not control. */
void engine_dump_torrent(engine *e, uint32_t torrent_id, FILE *out);
#ifdef __cplusplus
}
#endif
#endif /* TORRENT_ENGINE_H */