1use std::{
2 any::Any,
3 cell::{Cell, RefCell},
4 collections::VecDeque,
5 fmt,
6 marker::PhantomData,
7 panic::AssertUnwindSafe,
8 rc::{Rc, Weak},
9};
10
11use futures::{FutureExt, future::LocalBoxFuture};
12
13use super::{
14 CancellationToken, DiagnosticAdmission, DiagnosticEvent, DiagnosticSource, EventAdmissionPlan,
15 InvocationContext, NativeAppRuntime, RuntimeFailure, await_with_generation_context,
16 diagnostics::diagnostic_operation, ensure_context_active,
17 schedule_module_supervision_after_failure,
18};
19
20pub trait EventCapability: 'static {
22 type Event: Clone + 'static;
24 const ID: &'static str;
26 const DESCRIPTOR_VERSION: &'static str;
28}
29
30#[derive(Clone, Copy, Debug, Eq, PartialEq)]
32pub enum EventAdmission {
33 Accepted,
35 Unavailable,
37 Exhausted,
39}
40
41pub type EventPublishStatus = EventAdmission;
43
44#[derive(Clone, Debug, Eq, PartialEq)]
46pub struct EventPublishResult {
47 subscriber_instance: String,
48 admission: EventAdmission,
49}
50
51impl EventPublishResult {
52 pub(crate) fn new(subscriber_instance: String, admission: EventAdmission) -> Self {
53 Self {
54 subscriber_instance,
55 admission,
56 }
57 }
58
59 pub fn subscriber_instance(&self) -> &str {
61 &self.subscriber_instance
62 }
63
64 pub fn provider_instance(&self) -> &str {
66 self.subscriber_instance()
67 }
68
69 pub const fn admission(&self) -> EventAdmission {
71 self.admission
72 }
73
74 pub const fn status(&self) -> EventPublishStatus {
76 self.admission
77 }
78}
79
80pub trait NativeEventEndpoint: fmt::Debug {
82 fn capability_id(&self) -> &'static str;
84 fn descriptor_version(&self) -> &'static str;
86 fn operations(&self) -> &'static [&'static str];
88 fn owns_event_admission(&self) -> bool {
94 false
95 }
96 fn publish(
102 &self,
103 operation: &str,
104 event: Box<dyn Any>,
105 context: InvocationContext,
106 ) -> LocalBoxFuture<'static, Result<(), RuntimeFailure>>;
107}
108
109struct QueuedEvent {
110 operation: String,
111 event: Box<dyn Any>,
112 context: InvocationContext,
113 snapshot: NativeEventEndpointSnapshot,
114}
115
116impl fmt::Debug for QueuedEvent {
117 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
118 formatter
119 .debug_struct("QueuedEvent")
120 .field("operation", &self.operation)
121 .field("request_id", &self.context.request_id())
122 .field("generation", &self.snapshot.generation)
123 .finish_non_exhaustive()
124 }
125}
126
127#[derive(Debug, Default)]
128struct NativeEventQueueState {
129 pending: VecDeque<QueuedEvent>,
130 admitted: usize,
131 draining: bool,
132}
133
134#[derive(Debug)]
136pub(crate) struct NativeEventQueue {
137 capacity: usize,
138 state: RefCell<NativeEventQueueState>,
139}
140
141impl NativeEventQueue {
142 pub(crate) fn new(admission: EventAdmissionPlan) -> Rc<Self> {
143 Self {
144 capacity: admission.capacity(),
145 state: RefCell::new(NativeEventQueueState::default()),
146 }
147 .into()
148 }
149
150 fn try_enqueue(&self, event: QueuedEvent) -> Option<bool> {
151 let mut state = self.state.borrow_mut();
152 if state.admitted >= self.capacity {
153 return None;
154 }
155 state.admitted += 1;
156 state.pending.push_back(event);
157 if state.draining {
158 Some(false)
159 } else {
160 state.draining = true;
161 Some(true)
162 }
163 }
164
165 fn pop(&self) -> Option<QueuedEvent> {
166 self.state.borrow_mut().pending.pop_front()
167 }
168
169 fn complete(&self) {
170 let mut state = self.state.borrow_mut();
171 state.admitted = state.admitted.saturating_sub(1);
172 if state.pending.is_empty() {
173 state.draining = false;
174 }
175 }
176
177 fn abort(&self) {
178 let mut state = self.state.borrow_mut();
179 state.pending.clear();
180 state.admitted = 0;
181 state.draining = false;
182 }
183}
184
185#[derive(Clone, Debug)]
186pub(crate) struct NativeEventEndpointSnapshot {
187 pub(crate) endpoint: Rc<dyn NativeEventEndpoint>,
188 pub(crate) generation: u64,
189 pub(crate) cancellation: CancellationToken,
190}
191
192#[derive(Debug)]
193pub(crate) struct NativeEventEndpointState {
194 pub(crate) capability_id: &'static str,
195 pub(crate) descriptor_version: &'static str,
196 pub(crate) operations: &'static [&'static str],
197 endpoint: RefCell<Option<Rc<dyn NativeEventEndpoint>>>,
198 generation: Cell<u64>,
199 cancellation: RefCell<CancellationToken>,
200 queues: RefCell<Vec<Weak<NativeEventQueue>>>,
201}
202
203impl NativeEventEndpointState {
204 pub(crate) fn new(endpoint: Rc<dyn NativeEventEndpoint>, generation: u64) -> Self {
205 Self {
206 capability_id: endpoint.capability_id(),
207 descriptor_version: endpoint.descriptor_version(),
208 operations: endpoint.operations(),
209 endpoint: RefCell::new(Some(endpoint)),
210 generation: Cell::new(generation),
211 cancellation: RefCell::new(CancellationToken::new()),
212 queues: RefCell::new(Vec::new()),
213 }
214 }
215
216 pub(crate) fn snapshot(&self) -> Option<NativeEventEndpointSnapshot> {
217 self.endpoint
218 .borrow()
219 .clone()
220 .map(|endpoint| NativeEventEndpointSnapshot {
221 endpoint,
222 generation: self.generation.get(),
223 cancellation: self.cancellation.borrow().clone(),
224 })
225 }
226
227 pub(crate) fn mark_unavailable(&self) {
228 self.cancellation.borrow().cancel();
229 self.endpoint.borrow_mut().take();
230 self.reset_queues();
231 }
232
233 pub(crate) fn install(&self, endpoint: Rc<dyn NativeEventEndpoint>, generation: u64) {
234 self.generation.set(generation);
235 self.cancellation.replace(CancellationToken::new());
236 self.endpoint.replace(Some(endpoint));
237 }
238
239 pub(crate) fn is_current(&self, generation: u64) -> bool {
240 self.generation.get() == generation && self.endpoint.borrow().is_some()
241 }
242
243 pub(crate) fn register_queue(&self, queue: &Rc<NativeEventQueue>) {
244 self.queues.borrow_mut().push(Rc::downgrade(queue));
245 }
246
247 fn reset_queues(&self) {
248 self.queues.borrow_mut().retain(|queue| {
249 let Some(queue) = queue.upgrade() else {
250 return false;
251 };
252 queue.abort();
253 true
254 });
255 }
256}
257
258#[derive(Clone, Debug)]
259pub(crate) struct NativeEventEndpointBinding {
260 pub(crate) module_instance: String,
261 pub(crate) state: Rc<NativeEventEndpointState>,
262 pub(crate) queue: Rc<NativeEventQueue>,
263}
264
265#[derive(Clone, Debug)]
267pub struct ModuleEventDependencyHandle {
268 pub(crate) binding: NativeEventEndpointBinding,
269 pub(crate) caller_instance: String,
270 pub(crate) runtime: Rc<RefCell<std::rc::Weak<NativeAppRuntime>>>,
271}
272
273impl ModuleEventDependencyHandle {
274 pub fn capability_id(&self) -> &'static str {
276 self.binding.state.capability_id
277 }
278
279 pub fn descriptor_version(&self) -> &'static str {
281 self.binding.state.descriptor_version
282 }
283
284 pub fn operations(&self) -> &'static [&'static str] {
286 self.binding.state.operations
287 }
288
289 pub fn typed<C: EventCapability>(&self) -> Result<NativeEventHandle<C>, RuntimeFailure> {
291 if self.capability_id() != C::ID || self.descriptor_version() != C::DESCRIPTOR_VERSION {
292 return Err(RuntimeFailure::ProtocolViolation { capability: C::ID });
293 }
294 let runtime = self
295 .runtime
296 .borrow()
297 .upgrade()
298 .ok_or(RuntimeFailure::AdmissionClosed)?;
299 Ok(NativeEventHandle::from_endpoints(
300 std::slice::from_ref(&self.binding),
301 runtime,
302 &self.caller_instance,
303 true,
304 ))
305 }
306}
307
308#[derive(Debug)]
310pub struct NativeEventHandle<C: EventCapability> {
311 endpoints: Vec<NativeEventEndpointBinding>,
312 runtime: Rc<NativeAppRuntime>,
313 caller_instance: String,
314 allow_before_ready: bool,
315 capability: PhantomData<fn() -> C>,
316}
317
318impl<C: EventCapability> NativeEventHandle<C> {
319 pub(crate) fn from_endpoints(
320 endpoints: &[NativeEventEndpointBinding],
321 runtime: Rc<NativeAppRuntime>,
322 caller_instance: &str,
323 allow_before_ready: bool,
324 ) -> Self {
325 Self {
326 endpoints: endpoints.to_vec(),
327 runtime,
328 caller_instance: caller_instance.to_owned(),
329 allow_before_ready,
330 capability: PhantomData,
331 }
332 }
333
334 pub fn binding_count(&self) -> usize {
336 self.endpoints.len()
337 }
338
339 pub async fn publish(&self, operation: &str, event: C::Event) -> Vec<EventPublishResult> {
345 self.publish_with_context(operation, self.next_context(), event)
346 .await
347 }
348
349 pub async fn publish_with_context(
351 &self,
352 operation: &str,
353 context: InvocationContext,
354 event: C::Event,
355 ) -> Vec<EventPublishResult> {
356 let context = context
357 .with_caller_instance(self.caller_instance.clone())
358 .for_target(C::ID, operation);
359 futures::future::join_all(self.endpoints.iter().map(|endpoint| {
360 self.publish_to_endpoint(endpoint, operation, context.clone(), event.clone())
361 }))
362 .await
363 }
364
365 async fn publish_to_endpoint(
366 &self,
367 endpoint: &NativeEventEndpointBinding,
368 operation: &str,
369 context: InvocationContext,
370 event: C::Event,
371 ) -> EventPublishResult {
372 let operation_name = diagnostic_operation(endpoint.state.operations, operation);
373 let was_closed = self.runtime.shutdown_started.get()
374 || (!self.allow_before_ready && self.runtime.admission.is_closed());
375 let result = self
376 .publish_to_endpoint_inner(endpoint, operation, context.clone(), event)
377 .await;
378 let outcome = match result.admission() {
379 EventAdmission::Accepted => DiagnosticAdmission::Accepted,
380 EventAdmission::Unavailable if was_closed => DiagnosticAdmission::Closed,
381 EventAdmission::Unavailable => DiagnosticAdmission::Unavailable,
382 EventAdmission::Exhausted => DiagnosticAdmission::Exhausted,
383 };
384 self.runtime.diagnostics.emit(
385 DiagnosticSource::Admission,
386 (self.runtime.driver.now)(),
387 |_| DiagnosticEvent::EventAdmission {
388 request_id: context.request_id(),
389 publisher_instance: self.caller_instance.clone(),
390 subscriber_instance: endpoint.module_instance.clone(),
391 capability: C::ID,
392 operation: operation_name,
393 outcome,
394 },
395 );
396 result
397 }
398
399 async fn publish_to_endpoint_inner(
400 &self,
401 endpoint: &NativeEventEndpointBinding,
402 operation: &str,
403 context: InvocationContext,
404 event: C::Event,
405 ) -> EventPublishResult {
406 let subscriber = endpoint.module_instance.clone();
407 let unavailable =
408 || EventPublishResult::new(subscriber.clone(), EventAdmission::Unavailable);
409 if self.runtime.shutdown_started.get()
410 || (!self.allow_before_ready && self.runtime.admission.is_closed())
411 {
412 return unavailable();
413 }
414 let Some(snapshot) = endpoint.state.snapshot() else {
415 return unavailable();
416 };
417 if !endpoint.state.operations.contains(&operation) {
418 return unavailable();
419 }
420 let queue = &endpoint.queue;
421 if !endpoint.state.is_current(snapshot.generation)
422 || ensure_context_active(&self.runtime.driver, &context).is_err()
423 {
424 return unavailable();
425 }
426 if snapshot.endpoint.owns_event_admission() {
427 let result = snapshot
432 .endpoint
433 .publish(operation, Box::new(event), context.clone())
434 .await;
435 return match result {
436 Ok(()) => EventPublishResult::new(subscriber, EventAdmission::Accepted),
437 Err(error) => {
438 let error = schedule_module_supervision_after_failure(
439 &self.runtime,
440 &endpoint.module_instance,
441 error,
442 );
443 self.runtime.diagnostics.emit_runtime_failure(
444 (self.runtime.driver.now)(),
445 Some(&endpoint.module_instance),
446 &error,
447 );
448 let admission = if matches!(error, RuntimeFailure::ResourceExhausted { .. }) {
449 EventAdmission::Exhausted
450 } else {
451 EventAdmission::Unavailable
452 };
453 EventPublishResult::new(subscriber, admission)
454 }
455 };
456 }
457 let queued = QueuedEvent {
458 operation: operation.to_owned(),
459 event: Box::new(event),
460 context,
461 snapshot,
462 };
463 let Some(should_start) = queue.try_enqueue(queued) else {
464 return EventPublishResult::new(subscriber, EventAdmission::Exhausted);
465 };
466 if should_start {
467 let Some(tasks) = self
468 .runtime
469 .modules
470 .get(&endpoint.module_instance)
471 .and_then(|module| module.generation_parts().map(|(_, tasks, _)| tasks))
472 else {
473 queue.abort();
474 return unavailable();
475 };
476 let drain = drain_event_queue(
477 queue.clone(),
478 self.runtime.clone(),
479 endpoint.module_instance.clone(),
480 C::ID,
481 );
482 if tasks.spawn_local(Box::pin(drain)).is_err() {
483 queue.abort();
484 return unavailable();
485 }
486 }
487 EventPublishResult::new(subscriber, EventAdmission::Accepted)
488 }
489
490 fn next_context(&self) -> InvocationContext {
491 InvocationContext::new(self.next_request_id(), None, CancellationToken::new())
492 .with_caller_instance(self.caller_instance.clone())
493 }
494
495 fn next_request_id(&self) -> super::RequestId {
496 let request_id = self.runtime.request_ids.get();
497 self.runtime.request_ids.set(request_id.saturating_add(1));
498 request_id
499 }
500}
501
502async fn drain_event_queue(
503 queue: Rc<NativeEventQueue>,
504 runtime: Rc<NativeAppRuntime>,
505 module_instance: String,
506 capability: &'static str,
507) {
508 while let Some(queued) = queue.pop() {
509 let result = AssertUnwindSafe(await_with_generation_context(
510 &runtime.driver,
511 &queued.context,
512 queued.snapshot.cancellation,
513 capability,
514 queued.snapshot.endpoint.publish(
515 &queued.operation,
516 queued.event,
517 queued.context.clone(),
518 ),
519 ))
520 .catch_unwind()
521 .await;
522 match result {
523 Ok(Ok(Ok(()))) => {}
524 Ok(Ok(Err(error)) | Err(error)) => {
525 runtime.diagnostics.emit_runtime_failure(
526 (runtime.driver.now)(),
527 Some(&module_instance),
528 &error,
529 );
530 let _ =
531 schedule_module_supervision_after_failure(&runtime, &module_instance, error);
532 }
533 Err(_) => {
534 let error = RuntimeFailure::ModuleFailure {
535 detail: format!("native Event subscriber `{module_instance}` panicked"),
536 };
537 runtime.diagnostics.emit_runtime_failure(
538 (runtime.driver.now)(),
539 Some(&module_instance),
540 &error,
541 );
542 let _ =
543 schedule_module_supervision_after_failure(&runtime, &module_instance, error);
544 }
545 }
546 queue.complete();
547 }
548}