Skip to main content

Thread Map

Venturi has one owner of the document at a time: the UI thread holds the Session (project + undo history), and only lends it to the MCP host thread between frames when MCP is enabled. Edits are applied only by whoever holds it. Every other thread works on a copy, a snapshot or its own inputs, and talks back through channels, atomics or a small shared slot.

Long-lived threads

thread-map.png

The serial workers (proxy, waveform, thumbnail, the three mix_buffers queues) share one helper, vv_session::worker::Worker: an mpsc job queue drained by one thread, closed and joined on drop. RenderAhead has its own loop because it must react to playhead jumps mid-decode.

Short-lived threads

ThreadSpawned byWhy off the UI thread
Import probes (one per core, up to one per file)vv_session::import_workerProbing a video costs ~100 ms
OTIO readSession::import_otioReads the file and probes its media
Relinkrelink_job::spawnIndexes folders by file name
forced-relinkforced_relink::spawn_searchSearches a folder tree for matching media
Export + 3 stage threadsSession::exportDecode, compose, encode and audio mix run in parallel (see chapter 6)
Silence analysissilence_dialogRuns VAD over each media's audio
Fast-forward stretchtimeline_audioStretches 8 s windows of the mix, one request at a time
File dialogsproject_io, export_dialogA native dialog on the event-loop thread marks the app unresponsive
MCP media toolsvv_mcp::media_toolsrender_frame and get_audio_levels decode on a project snapshot
HW decode probehw_decode::startGPU enumeration and CUDA init take up to a few hundred ms
Encoder checkvv_media::encodeEach HW encoder is tested with a real encode
Font warm-upvv_render::text::warm_upThe system font scan would stall the first title
Drop readwayland_dndReading a drop waits on the source app
stdin pumpvv_mcp::bridge_stdio--attach copies both directions at once

Rules for the audio callback

RuleHow
Never blockOnly try_lock: a busy lock means "keep the current state" or one block of silence.
Never allocate or freeMixerState is built on the UI thread, which also keeps every published state alive until the callback has let go of it, so the last drop happens there.
Own the clockThe callback advances position with a compare-exchange, so a seek that lands mid-block wins.