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
4 changes: 3 additions & 1 deletion docs/plans/PROFILER-PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,9 @@ These are SharpLsp's shipped profiler features. Cutover is blocked until each ro
- a hot function with no location reached the UI as file `""`, line 0 (lspkit#15);
- the UI's Stop never sent `memory.stop`, so the server kept the session and its graphs and still answered steps (lspkit#15);
- a timed CPU session never left `cpu.list` once its duration elapsed (lspkit#15);
- the host accepted a profiler command with no argument, two arguments or a non-object argument (`profiler/host.rs`).
- the host accepted a profiler command with no argument, two arguments or a non-object argument (`profiler/host.rs`);
- a sample in an F# `match`'s decision tree lost its file, so `Csv.parseLine` split into a located function and an unlocated twin holding 61% of its samples, and the Windows chunk failed on #406 (#408, lspkit#19);
- FsOrderLedger's burn tick took the process-wide gate around every `priceAll` call. EventPipe's sampler stops a thread at its next safe point, so the uncontended lock call drew 98% of the burn thread's samples, and `priceAll` sat at the 2% hot-function threshold. The joint CPU and memory soak failed two runs in three on macOS. The tick only reads, so it now runs outside the gate, and `priceAll` is on every burn sample (#408).
- [ ] Assertions of the deleted suites with no replacement: counters webview and counter sessions, `openTrace`/`convertTrace` of a `.nettrace`, dump collection with cancellable progress, `inspectObject`, `diffSnapshots`/`detectLeaks` panels over dumps, graph browser interactions. Each maps to a parity gap above.

### Documentation and cutover
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// FsOrderLedger's single process-wide gate. Commands arrive on the stdin thread while the
// "workload" thread drips orders and burns CPU, so every mutation, every workload tick and every
// "workload" thread drips orders and burns CPU, so every mutation, every drip burst and every
// reply snapshot runs inside this one lock: the counts a reply prints are then one consistent
// snapshot of the live heap, not a torn read.
// snapshot of the live heap, not a torn read. A burn tick only reads, so it stays outside: the
// sampler stops a thread at its next safe point, and a lock taken on every tick drew the samples
// of the loop it guarded, which left `priceAll` under the hot-function threshold.
// Profiling fixture for [PROFILER-SHARED-LIBRARY-VSCODE] and lspkit's [PROF-TESTING].

module FsOrderLedger.Gate

/// The one process-wide lock. Hold it for a whole command, a whole drip burst, or one burn call.
/// The one process-wide lock. Hold it for a whole command or a whole drip burst.
let sync = obj ()
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ let private dripTick () : unit =
placeOne () |> ignore)
Thread.Sleep 50

/// One burn tick: the gate is held for exactly one `priceAll` call, never across ticks.
let private burnTick () : unit = lock Gate.sync (fun () -> Ledger.priceAll () |> ignore)
/// One burn tick: one `priceAll` call. It only reads, under the ledger's own lock, so it takes no gate.
let private burnTick () : unit = Ledger.priceAll () |> ignore

let private workloadLoop () =
while not (Volatile.Read(&stopping)) do
Expand Down
Loading