TorrentSession
The download/upload engine for a single torrent. It is the live counterpart of libtorrent's torrent (torrent.cpp). It wires the pure-core pieces on coroutines: announce → collect peers → run a PeerConnection per peer → a PiecePicker-driven request pipeline → write blocks to DiskIo → verify completed pieces → broadcast have → and serve blocks back to peers we've unchoked.
Tunables come from the ported SettingsPack (the libtorrent defaults), exactly the knobs torrent/peer_connection read from m_settings: request_timeout, piece_timeout, request_queue_time, max_out_request_queue, initial_picker_threshold, strict_end_game_mode, unchoke_interval, optimistic_unchoke_interval, unchoke_slots_limit.
The request scheduler is a faithful port of request_blocks.cpp + the relevant peer_connection machinery:
Dynamic request queue (
update_desired_queue_size): each peer's pipeline depth isrequest_queue_time × download-rate / block-size, clamped to[2, max_out_request_queue]. Slow start grows the depth by one per block until the rate stops climbing. Snubbed or end-game peers drop to 1.True end-game (
request_a_block): when the swarm has no free block left for a peer with an empty queue, the engine re-requests one block already asked of another peer (gated bystrict_end_game_mode). The first copy to arrive is kept, and every other holder gets acancel(torrent::cancel_block).Snubbing (
snub_peer): the engine snubs a peer that has outstanding requests but sends no payload forpiece_timeout. A snubbed peer gets queue depth 1 and reverse picking, so it converges on common pieces instead of keeping rare pieces unavailable. The engine also cancels its newest request when that request blocks a piece from completing. A replacement is requested first, so the picker cannot hand the same block back.Choking: a periodic Choker round (
unchoke_interval) limits uploads to the fastest peers + one rotating optimistic slot. Choked peers are not served.Rate limiting: payload moves through the engine's RateLimiter, which is the live io.github.yuroyami.kitetorrent.bandwidth.BandwidthManager wiring. Uploads acquire quota before the send and downloads after the receive, which applies back-pressure to the read loop.
µTP: with a UtpSocketManager attached, outgoing connections try uTP first and fall back to TCP (
outgoing_utp+outgoing_tcp).Verified resume, state machine, pause/resume, priorities and sequentialDownload as before.
A single Mutex guards all shared state (picker, have, the peer set); sends happen outside the lock. Every read and write of that state must hold the mutex.
Constructors
Properties
Alert sink (off-lock). The owning io.github.yuroyami.kitetorrent.session.engine.KiteTorrentEngine sets this to forward per-torrent events into the session alert queue. Defaults to null (no-op). Called at the event sites this engine touches: tracker reply/error, peer connect/disconnect/ban, piece finished, torrent finished, hash failures.
Invoked (off-lock) when the fast-resume state has gone stale and should be persisted: the torrent finished or was paused. The app responds by calling saveResumeData and writing the bytes (the port of libtorrent's save_resume_data / NEED_SAVE_RESUME).
Invoked (off-lock) whenever a piece is verified and completed.
Invoked (off-lock) whenever state changes.
Seed-time cap in seconds; 0 = unlimited. Pauses seeding once met.
Pick pieces in index order instead of rarest-first (torrent_flags::sequential_download).
Share-ratio cap in per-mille (2000 = 2.0×); 0 = unlimited. Pauses seeding once met.
Current lifecycle state.
Super-seeding / initial-seeding (torrent_flags::super_seeding). When seeding, advertise only one (rarest) piece to each peer at a time. Hand out the next one only once the peer announces it got the previous. The seed then pushes the whole torrent into the swarm with minimal redundant uploads while bootstrapping it.
Upload (unchoke) slots for this torrent: max_uploads. Settable live, default from settings.
Functions
Apply fast-resume atp without hashing: whole pieces in AddTorrentParams.havePieces are flushed into the picker as had (piece_flushed, which is documented safe to call straight from open for exactly this), and each block recorded in AddTorrentParams.unfinishedPieces is marked finished so the picker won't re-request the partial-piece progress already on disk. Saved stats, the sequential flag and piece/file priorities are restored too. The port of torrent adopting resume data.
Connect to up to maxPeers of the given endpoints, each on its own coroutine.
Peers currently snubbed (no payload despite outstanding requests).
Peers we're currently uploading to (unchoked), bounded by uploadSlots (max_uploads).
Capture the current piece state as fast-resume AddTorrentParams. This is the port of save_resume_data / write_resume_data. The result round-trips through ResumeData.write/read (embedding the raw info dict so the torrent reloads without its .torrent file) and feeds straight back into applyResumeData on the next run. Verified have pieces become both havePieces and verifiedPieces (every v1 piece is hash-checked before it's marked had); partially-downloaded pieces export their on-disk (FINISHED) blocks as an unfinished block bitmask.
Set a file's download priority. A piece shared by two files takes the higher of the two files' priorities (libtorrent's rule), recomputed across all files.
Set a single piece's priority (0 = don't download, 7 = highest).
Adopt fast-resume state, or recheck when there's none, then start the maintenance tick, announce, and connect to peers. With resumeData present the saved have bitfield is trusted (no rehash). That is what fast resume is for: restarts skip the full-disk re-read recheck would otherwise do.