1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! `SharedState` — the daemon's central state object shared by every
//! request handler.
//!
//! Every connection handler receives an `Arc<SharedState>` and reads from
//! these fields directly. Most fields are append-only after `DaemonServer::bind`
//! (`sessions`, `journal`, `artifact_store`); the lock-free `DashMap`s are
//! contended by request handlers concurrently.
use super::*;
/// Shared state accessible by all connection handlers.
pub(super) struct SharedState {
pub(super) sessions: SessionManager,
pub(super) system_includes: Mutex<SystemIncludeCache>,
/// Dependency graph: tracks include relationships and cache verdicts.
pub(super) dep_graph: DepGraph,
/// In-memory artifact cache: artifact_key_hex → artifact data.
pub(super) artifacts: DashMap<String, CachedArtifact>,
/// Metadata cache + change journal. The watcher feeds file-change events
/// into this, which downgrades confidence so `lookup()` re-hashes on
/// next access. Without the watcher, stat-verify on every `lookup()` is
/// the fallback (correct but slower).
pub(super) cache_system: CacheSystem,
/// File watcher for proactive metadata invalidation.
pub(super) watcher: Mutex<Option<NotifyWatcher>>,
/// Directories currently being watched (avoid duplicate watches).
pub(super) watched_dirs: Mutex<HashSet<NormalizedPath>>,
/// Shutdown signal — shared so request handlers can trigger shutdown.
pub(super) shutdown: Arc<Notify>,
/// Epoch seconds of last client activity (for idle timeout).
pub(super) last_activity: AtomicU64,
/// Daemon start time (epoch seconds).
pub(super) start_time: u64,
/// Global stats collector.
pub(super) stats: StatsCollector,
/// Phase-level profiler for hot-path breakdown.
pub(super) profiler: PhaseProfiler,
/// On-disk artifact cache for hardlink optimization on cache hits.
pub(super) artifact_dir: NormalizedPath,
/// On-disk path for the persisted [`MetadataCache`] snapshot.
///
/// Written on flush (`Clear`) and shutdown (`Shutdown`); read at
/// daemon startup so warm-side daemons spawned after `soldr load`
/// start with their fast path already populated instead of an
/// empty `DashMap`. See `crate::fscache::persistence`.
pub(super) metadata_path: NormalizedPath,
/// Temporary directory for injected depfiles.
pub(super) depfile_tmpdir: NormalizedPath,
/// Ultra-fast hit cache: context_key → (clock, artifact_key_hex, timestamp).
/// When the journal clock hasn't advanced since the last verified hit,
/// we skip all stat/hash/depgraph work and jump straight to artifact lookup.
pub(super) fast_hit_cache: DashMap<ContextKey, FastHitEntry>,
/// Whether the file watcher is active. Fast-hit cache is only used when
/// the watcher is running, since we rely on it for change detection.
pub(super) watcher_active: AtomicBool,
/// Response file expansion cache keyed by canonical root path.
/// Each entry carries the transitive response-file hashes required to
/// validate freshness before reusing the cached expansion.
pub(super) rsp_cache: DashMap<NormalizedPath, RspCacheEntry>,
/// Request-level fast path cache: hash(compiler, args, cwd) → pre-computed context.
/// When the same compile request is seen again and the fast-hit cache still
/// holds a valid entry, this allows skipping ALL heavy work: system include
/// discovery, watch_directories, response file expansion, arg parsing,
/// context building, and dep_graph registration.
pub(super) request_cache: DashMap<ContentHash, RequestCacheEntry>,
/// Session-level worktree-root cache resolved once at SessionStart.
pub(super) session_worktree_roots: DashMap<SessionId, SessionWorktreeRoot>,
/// Cross-root request-cache validation: (request fingerprint, root) -> last
/// verified artifact and journal clock. This lets repeated sibling hits
/// validate with journal checks instead of re-hashing every input.
pub(super) request_validation_cache: DashMap<RequestValidationKey, RequestValidationEntry>,
/// Compiler executable hash cache keyed by compiler path.
pub(super) compiler_hash_cache: CompilerHashCache,
/// Pre-filter for watch_directories: raw (non-canonicalized) paths we've
/// already processed. Avoids expensive canonicalize() syscalls (~1-5ms each
/// on Windows) for directories that are already being watched.
pub(super) watched_raw_dirs: DashMap<NormalizedPath, ()>,
/// PCH source registry: pch_output_path → source_header_path.
/// When a PCH generation succeeds, we record the mapping so that
/// consuming compilations can hash the source header instead of the
/// non-deterministic PCH binary.
pub(super) pch_source_map: DashMap<NormalizedPath, NormalizedPath>,
/// JSONL compile journal for build replay.
pub(super) journal: CompileJournal,
/// Bytes currently in spawn_blocking persistence tasks, invisible to eviction.
pub(super) in_flight_bytes: AtomicUsize,
/// Limits concurrent disk persistence tasks to prevent memory pileup
/// when disk I/O is slow and compilation requests are fast.
pub(super) persist_semaphore: Arc<tokio::sync::Semaphore>,
/// In-memory artifact index (bincode blob-backed) for fast startup and
/// persistence. Hot-path reads and writes go through `state.artifacts`;
/// this store holds the same data and snapshots it to disk periodically.
///
/// Arc-wrapped so the background index-writer task (see `index_writer_tx`)
/// can hold its own clone for batched `insert` calls without contending
/// with the request-handler path.
pub(super) artifact_store: Arc<ArtifactStore>,
/// Sender to the background index-writer task. Persist call-sites push
/// `(key_hex, ArtifactIndex)` pairs here and return immediately; the
/// writer task drains the channel and flushes to the on-disk blob in
/// batches.
///
/// Decouples the artifact-persist semaphore (which gates concurrent disk
/// writes) from the periodic index snapshot, so a slow flush no longer
/// holds a persist permit while other artifacts wait. See
/// `tests/persist_pool_bench.rs` for the data motivating this split.
pub(super) index_writer_tx: tokio::sync::mpsc::UnboundedSender<(String, ArtifactIndex)>,
/// Notify the index-writer to drain its WAL and exit on graceful shutdown.
/// Without this, the writer would only see the channel close after every
/// `Arc<SharedState>` ref (including those held by spawned persist tasks)
/// drops — which can race with runtime abort and lose unflushed entries.
pub(super) index_writer_shutdown: Arc<Notify>,
/// Whether the background artifact loading has completed.
pub(super) artifacts_loaded: AtomicBool,
/// Fingerprint manager: tracks per-watch dirty state for `zccache fp` commands.
pub(super) fingerprint: FingerprintManager,
/// Whether the in-memory dep graph is backed by a persisted snapshot.
///
/// Set to `true` when the graph is loaded from disk on startup (via
/// `set_dep_graph`) or when a periodic/shutdown save completes
/// successfully. Surfaced in `DaemonStatus.dep_graph_persisted` so the
/// CLI can distinguish "persisted graph" from "first-run, not yet flushed"
/// without inferring it from the on-disk file size.
pub(super) dep_graph_persisted: AtomicBool,
/// Optional load-time warning to mirror into every session log.
///
/// Populated by `set_depgraph_load_warning` when the daemon's startup load
/// of the persisted depgraph fell back to a cold session (version
/// mismatch, corrupt header, or unexpected I/O error). The string is
/// emitted once per session into the per-session log (`last-session.log`)
/// at `SessionStart` time so the cold fallback is never silent. Issue #320.
pub(super) depgraph_load_warning: Mutex<Option<String>>,
/// In-flight `Request::GenericToolExec` coalescing map (issue #272).
///
/// Concurrent callers with the same exec cache key share a `Notify` here:
/// the first caller spawns the tool and inserts; subsequent callers wait
/// on the same `Notify` and re-attempt the cache lookup once it fires,
/// guaranteeing the tool runs exactly once for the herd.
pub(super) in_flight_exec: DashMap<String, Arc<Notify>>,
}