1use std::error::Error;
2use std::fmt;
3use std::sync::Arc;
4
5use crate::model_executor::ExecutorRequestOrigin;
6
7use super::{
8 has_active, BoundExecutionResourceMaintenance, ExecutionEvent, ExecutionEventCursor,
9 ExecutionEventKind, RequestIdentity, RunId, TrustedExecutionEventContext,
10};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct ExecutionEventSinkError {
14 message: String,
15}
16
17impl ExecutionEventSinkError {
18 pub fn new(message: impl Into<String>) -> Self {
19 Self {
20 message: message.into(),
21 }
22 }
23
24 pub fn message(&self) -> &str {
25 &self.message
26 }
27}
28
29impl fmt::Display for ExecutionEventSinkError {
30 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
31 formatter.write_str(&self.message)
32 }
33}
34
35impl Error for ExecutionEventSinkError {}
36
37mod event_sink_seal {
38 pub struct Seal;
39}
40
41pub struct EventEmissionPermit {
45 event: ExecutionEvent,
46 _seal: event_sink_seal::Seal,
47}
48
49impl EventEmissionPermit {
50 pub fn event(&self) -> &ExecutionEvent {
51 &self.event
52 }
53
54 pub fn into_event(self) -> ExecutionEvent {
55 self.event
56 }
57}
58
59pub struct EventBatchEmissionPermit {
62 events: Vec<ExecutionEvent>,
63 _seal: event_sink_seal::Seal,
64}
65
66impl EventBatchEmissionPermit {
67 pub fn events(&self) -> &[ExecutionEvent] {
68 &self.events
69 }
70
71 pub fn into_events(self) -> Vec<ExecutionEvent> {
72 self.events
73 }
74}
75
76#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
77pub enum ExecutionEventCapturePolicy {
78 #[default]
79 AllFrames,
80 FirstFramePerRequest,
81 LifecycleOnly,
82}
83
84impl ExecutionEventCapturePolicy {
85 pub const fn captures_frame(self, completed_frames: u64) -> bool {
86 match self {
87 Self::AllFrames => true,
88 Self::FirstFramePerRequest | Self::LifecycleOnly => completed_frames == 0,
89 }
90 }
91
92 pub const fn records_event(self, kind: ExecutionEventKind) -> bool {
93 match self {
94 Self::AllFrames | Self::FirstFramePerRequest => true,
95 Self::LifecycleOnly => matches!(
96 kind,
97 ExecutionEventKind::RequestAccepted
98 | ExecutionEventKind::PlanBuilt
99 | ExecutionEventKind::FailureObserved
100 | ExecutionEventKind::SequenceCompleted
101 | ExecutionEventKind::SequenceAborted
102 | ExecutionEventKind::RequestCompleted
103 | ExecutionEventKind::RequestFailed
104 ),
105 }
106 }
107
108 pub const fn as_str(self) -> &'static str {
109 match self {
110 Self::AllFrames => "all_frames",
111 Self::FirstFramePerRequest => "first_frame_per_request",
112 Self::LifecycleOnly => "lifecycle_only",
113 }
114 }
115}
116
117#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
118pub enum ExecutionEventSinkEnablement {
119 None,
120 All,
121 #[default]
122 PerKind,
123}
124
125pub trait ExecutionEventSink: Send + Sync {
126 fn enablement(&self) -> ExecutionEventSinkEnablement {
132 ExecutionEventSinkEnablement::PerKind
133 }
134
135 fn is_enabled(&self, kind: ExecutionEventKind) -> bool;
136
137 fn device_timing_mode(&self) -> super::super::DeviceTimingMode {
138 super::super::DeviceTimingMode::Off
139 }
140
141 fn capture_policy(&self) -> ExecutionEventCapturePolicy {
142 ExecutionEventCapturePolicy::AllFrames
143 }
144
145 fn capture_policy_for_request(
146 &self,
147 _origin: ExecutorRequestOrigin,
148 ) -> ExecutionEventCapturePolicy {
149 self.capture_policy()
150 }
151
152 fn record_device_submission_attribution(
153 &self,
154 _attribution: &super::super::BoundDeviceSubmissionAttribution,
155 ) -> Result<(), ExecutionEventSinkError> {
156 Ok(())
157 }
158
159 fn record_physical_device_submission_timing(
160 &self,
161 _completion: &super::super::OperationCompletionReceipt,
162 ) -> Result<(), ExecutionEventSinkError> {
163 Ok(())
164 }
165
166 fn records_execution_resource_maintenance(&self) -> bool {
170 false
171 }
172
173 fn record_execution_resource_maintenance(
174 &self,
175 _maintenance: BoundExecutionResourceMaintenance,
176 ) -> Result<(), ExecutionEventSinkError> {
177 Ok(())
178 }
179
180 fn record(&self, permit: EventEmissionPermit) -> Result<(), ExecutionEventSinkError>;
181
182 fn record_batch(
185 &self,
186 permit: EventBatchEmissionPermit,
187 ) -> Result<(), ExecutionEventSinkError> {
188 for event in permit.into_events() {
189 self.record(EventEmissionPermit {
190 event,
191 _seal: event_sink_seal::Seal,
192 })?;
193 }
194 Ok(())
195 }
196}
197
198enum ExecutionEventSinkHandle<'sink, S>
199where
200 S: ExecutionEventSink + ?Sized,
201{
202 Borrowed(&'sink S),
203 Shared(Arc<S>),
204}
205
206impl<S> ExecutionEventSinkHandle<'_, S>
207where
208 S: ExecutionEventSink + ?Sized,
209{
210 #[inline]
211 fn as_sink(&self) -> &S {
212 match self {
213 Self::Borrowed(sink) => *sink,
214 Self::Shared(sink) => sink.as_ref(),
215 }
216 }
217}
218
219pub struct ExecutionEventEmitter<'sink, S = dyn ExecutionEventSink>
220where
221 S: ExecutionEventSink + ?Sized,
222{
223 sink: ExecutionEventSinkHandle<'sink, S>,
224 cursor: ExecutionEventCursor,
225 capture_policy: ExecutionEventCapturePolicy,
226 sink_enablement: ExecutionEventSinkEnablement,
227 sink_failed: bool,
228}
229
230impl<'sink, S> ExecutionEventEmitter<'sink, S>
231where
232 S: ExecutionEventSink + ?Sized,
233{
234 #[inline]
235 pub fn new(sink: &'sink S, run_id: RunId, request_id: RequestIdentity) -> Self {
236 let sink_enablement = sink.enablement();
237 let capture_policy = if sink_enablement == ExecutionEventSinkEnablement::None {
238 ExecutionEventCapturePolicy::AllFrames
239 } else {
240 sink.capture_policy()
241 };
242 Self {
243 sink: ExecutionEventSinkHandle::Borrowed(sink),
244 cursor: ExecutionEventCursor::new(run_id, request_id),
245 capture_policy,
246 sink_enablement,
247 sink_failed: false,
248 }
249 }
250}
251
252impl ExecutionEventEmitter<'static, dyn ExecutionEventSink> {
253 pub fn from_shared(
258 sink: Arc<dyn ExecutionEventSink>,
259 run_id: RunId,
260 request_id: RequestIdentity,
261 ) -> ExecutionEventEmitter<'static> {
262 let sink_enablement = sink.enablement();
263 let capture_policy = if sink_enablement == ExecutionEventSinkEnablement::None {
264 ExecutionEventCapturePolicy::AllFrames
265 } else {
266 sink.capture_policy()
267 };
268 Self::from_shared_parts(sink, run_id, request_id, capture_policy, sink_enablement)
269 }
270
271 pub fn from_shared_with_capture_policy(
272 sink: Arc<dyn ExecutionEventSink>,
273 run_id: RunId,
274 request_id: RequestIdentity,
275 capture_policy: ExecutionEventCapturePolicy,
276 ) -> ExecutionEventEmitter<'static> {
277 let sink_enablement = sink.enablement();
278 Self::from_shared_parts(sink, run_id, request_id, capture_policy, sink_enablement)
279 }
280
281 fn from_shared_parts(
282 sink: Arc<dyn ExecutionEventSink>,
283 run_id: RunId,
284 request_id: RequestIdentity,
285 capture_policy: ExecutionEventCapturePolicy,
286 sink_enablement: ExecutionEventSinkEnablement,
287 ) -> ExecutionEventEmitter<'static> {
288 ExecutionEventEmitter {
289 sink: ExecutionEventSinkHandle::Shared(sink),
290 cursor: ExecutionEventCursor::new(run_id, request_id),
291 capture_policy,
292 sink_enablement,
293 sink_failed: false,
294 }
295 }
296}
297
298impl<'sink, S> ExecutionEventEmitter<'sink, S>
299where
300 S: ExecutionEventSink + ?Sized,
301{
302 #[inline]
303 fn records_event(&self, event: &ExecutionEvent) -> bool {
304 self.capture_policy.records_event(event.kind())
305 && match self.sink_enablement {
306 ExecutionEventSinkEnablement::None => false,
307 ExecutionEventSinkEnablement::All => true,
308 ExecutionEventSinkEnablement::PerKind => {
309 self.sink.as_sink().is_enabled(event.kind())
310 }
311 }
312 }
313
314 #[inline]
315 fn validate_next(
316 cursor: &mut ExecutionEventCursor,
317 event: &ExecutionEvent,
318 context: &TrustedExecutionEventContext<'_>,
319 ) -> Result<(), ExecutionEventSinkError> {
320 match event.kind() {
321 ExecutionEventKind::FrameStarted | ExecutionEventKind::NodeStarted => context
322 .active_binding()
323 .ok_or_else(|| {
324 ExecutionEventSinkError::new(
325 "active execution emission lacks live sequence evidence",
326 )
327 })?
328 .ensure_open_for_emission()
329 .map_err(|error| ExecutionEventSinkError::new(error.to_string()))?,
330 ExecutionEventKind::OperationSubmitted
331 | ExecutionEventKind::NodeRetired
332 | ExecutionEventKind::FrameCompleted => context
333 .active_binding()
334 .ok_or_else(|| {
335 ExecutionEventSinkError::new(
336 "active execution emission lacks live sequence evidence",
337 )
338 })?
339 .ensure_live_for_emission()
340 .map_err(|error| ExecutionEventSinkError::new(error.to_string()))?,
341 ExecutionEventKind::FailureObserved if has_active(event.identity().parts()) => context
342 .active_binding()
343 .ok_or_else(|| {
344 ExecutionEventSinkError::new(
345 "active execution emission lacks live sequence evidence",
346 )
347 })?
348 .ensure_live_for_emission()
349 .map_err(|error| ExecutionEventSinkError::new(error.to_string()))?,
350 _ => {}
351 }
352 cursor
353 .observe_candidate_in_place(event, context)
354 .map_err(|error| ExecutionEventSinkError::new(error.to_string()))
355 }
356
357 #[inline]
358 pub fn emit(
359 &mut self,
360 event: ExecutionEvent,
361 context: &TrustedExecutionEventContext<'_>,
362 ) -> Result<(), ExecutionEventSinkError> {
363 if self.sink_enablement == ExecutionEventSinkEnablement::None {
364 let mut next_cursor = self.cursor.clone();
365 Self::validate_next(&mut next_cursor, &event, context)?;
366 self.cursor = next_cursor;
367 return Ok(());
368 }
369 if self.sink_failed {
370 return Err(ExecutionEventSinkError::new(
371 "execution event emitter is sealed after a sink failure",
372 ));
373 }
374 let mut next_cursor = self.cursor.clone();
375 Self::validate_next(&mut next_cursor, &event, context)?;
376 if self.records_event(&event) {
377 let permit = EventEmissionPermit {
378 event,
379 _seal: event_sink_seal::Seal,
380 };
381 if let Err(error) = self.sink.as_sink().record(permit) {
382 self.sink_failed = true;
383 return Err(error);
384 }
385 }
386 self.cursor = next_cursor;
387 Ok(())
388 }
389
390 pub fn emit_batch(
391 &mut self,
392 events: Vec<ExecutionEvent>,
393 contexts: &[TrustedExecutionEventContext<'_>],
394 ) -> Result<(), ExecutionEventSinkError> {
395 if self.sink_enablement == ExecutionEventSinkEnablement::None {
396 if events.len() != contexts.len() {
397 return Err(ExecutionEventSinkError::new(
398 "execution event batch context count differs from event count",
399 ));
400 }
401 if events.is_empty() {
402 return Ok(());
403 }
404 let mut next_cursor = self.cursor.clone();
405 for (event, context) in events.iter().zip(contexts) {
406 Self::validate_next(&mut next_cursor, event, context)?;
407 }
408 self.cursor = next_cursor;
409 return Ok(());
410 }
411 if self.sink_failed {
412 return Err(ExecutionEventSinkError::new(
413 "execution event emitter is sealed after a sink failure",
414 ));
415 }
416 if events.len() != contexts.len() {
417 return Err(ExecutionEventSinkError::new(
418 "execution event batch context count differs from event count",
419 ));
420 }
421 if events.is_empty() {
422 return Ok(());
423 }
424
425 let mut next_cursor = self.cursor.clone();
426 for (event, context) in events.iter().zip(contexts) {
427 Self::validate_next(&mut next_cursor, event, context)?;
428 }
429
430 let all_enabled = events.iter().all(|event| self.records_event(event));
431 if all_enabled {
432 let permit = EventBatchEmissionPermit {
433 events,
434 _seal: event_sink_seal::Seal,
435 };
436 if let Err(error) = self.sink.as_sink().record_batch(permit) {
437 self.sink_failed = true;
438 return Err(error);
439 }
440 } else {
441 for event in events {
442 if !self.records_event(&event) {
443 continue;
444 }
445 let permit = EventEmissionPermit {
446 event,
447 _seal: event_sink_seal::Seal,
448 };
449 if let Err(error) = self.sink.as_sink().record(permit) {
450 self.sink_failed = true;
451 return Err(error);
452 }
453 }
454 }
455 self.cursor = next_cursor;
456 Ok(())
457 }
458
459 pub fn cursor(&self) -> &ExecutionEventCursor {
460 &self.cursor
461 }
462
463 pub const fn sink_failed(&self) -> bool {
464 self.sink_failed
465 }
466}
467
468#[derive(Debug, Default)]
469pub struct DisabledExecutionEventSink;
470
471impl ExecutionEventSink for DisabledExecutionEventSink {
472 fn enablement(&self) -> ExecutionEventSinkEnablement {
473 ExecutionEventSinkEnablement::None
474 }
475
476 fn is_enabled(&self, _kind: ExecutionEventKind) -> bool {
477 false
478 }
479
480 fn record(&self, _permit: EventEmissionPermit) -> Result<(), ExecutionEventSinkError> {
481 Ok(())
482 }
483}