Donner SVG 0.8.0-pre
SVG editor and embeddable C⁠+⁠+⁠20 engine.
Loading...
Searching...
No Matches
AsyncifySuspendProbe.h File Reference

Per-frame attribution of ASYNCIFY suspend cost (the single-canvas presenter work). More...

#include <chrono>
#include <cstdint>
Include dependency graph for AsyncifySuspendProbe.h:

Classes

struct  donner::FrameSuspendTotals
 Suspend totals accumulated between BeginSuspendFrame and EndSuspendFrame on the calling thread. More...
class  donner::ScopedSuspendPoint
 Brackets one call that may suspend the wasm stack under ASYNCIFY. More...

Namespaces

namespace  donner
 Top-level Donner namespace, which is split into different sub-namespaces such as donner::svg and donner::css.

Enumerations

enum class  donner::SuspendKind : std::uint8_t {
  donner::TileYield = 0 ,
  donner::GpuReadback = 1 ,
  donner::DeviceWait = 2 ,
  donner::Startup = 3
}
 Why a call site can suspend. Kept small and stable: it is published to the stats surface as an array index. More...

Functions

void donner::BeginSuspendFrame ()
 Start a new attribution window on the calling thread, discarding whatever was accumulated since the previous EndSuspendFrame.
FrameSuspendTotals donner::EndSuspendFrame ()
 Close the attribution window opened by BeginSuspendFrame and return what it accumulated. Safe to call without a matching begin; the totals are then everything since the thread's last reset.
FrameSuspendTotals donner::PeekSuspendFrame ()
 Read the current window's totals without closing it.
FrameSuspendTotals donner::LifetimeSuspendTotals ()
 Lifetime totals for the calling thread, never reset by frame boundaries. Used by the boot report, where there is no frame to attribute against.

Variables

constexpr std::size_t donner::kSuspendKindCount = 4
 Number of distinct SuspendKind values.

Detailed Description

Per-frame attribution of ASYNCIFY suspend cost (the single-canvas presenter work).

The browser editor links with whole-module -sASYNCIFY. Any call that reaches a JS-side Asyncify.handleSleep / handleAsync unwinds the wasm stack, returns to the browser event loop, and rewinds when the awaited promise settles. With the whole application on one thread those suspend points sit directly under the UI frame, so their wall cost is UI frame cost and has to be attributed before a frame budget means anything.

There is no wasm-visible Asyncify counter to read, and the JS runtime's Asyncify object is a closure-renamed module-scope binding, so the probe instead brackets the call sites that can suspend. Bracketing is exact for wall time because Asyncify preserves the shadow stack across an unwind: entering a ScopedSuspendPoint runs its constructor once before the call, the unwind returns out of the enclosing function without running destructors, the rewind restores the stack pointer and locals, and the destructor finally runs once on the normal path out. The measured interval therefore spans the whole suspend, not just the pre-suspend CPU work.

Counters are thread-local: each thread attributes the suspends it performs, and the frame publisher reads only its own thread's totals.


Class Documentation

◆ donner::FrameSuspendTotals

struct donner::FrameSuspendTotals

Suspend totals accumulated between BeginSuspendFrame and EndSuspendFrame on the calling thread.

Class Members
uint32_t count = 0 Suspend points entered during the frame.
uint32_t countByKind[kSuspendKindCount] = {} Per-kind entry counts, indexed by SuspendKind.
double longestMs = 0.0 Longest single suspend during the frame, in milliseconds.
double msByKind[kSuspendKindCount] = {} Per-kind wall time in milliseconds, indexed by SuspendKind.
double totalMs = 0.0 Summed wall time inside those suspend points, in milliseconds.