1use std::any::TypeId;
2use std::cell::{Cell, RefCell};
3use std::fmt;
4use std::marker::PhantomData;
5use std::panic::{AssertUnwindSafe, catch_unwind};
6use std::ptr::NonNull;
7use std::rc::{Rc, Weak};
8
9use thiserror::Error;
10
11use crate::render::RendererConsumerCapability;
12
13use super::binding::{self, ContextId, ContextLifecycle, ContextState};
14use super::core::Context;
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18#[non_exhaustive]
19pub enum ContextAttachmentPhase {
20 Quiesce,
22 RendererResources,
24 PlatformWindows,
26}
27
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30#[non_exhaustive]
31pub enum ContextAttachmentRole {
32 Extension,
34 Renderer,
36 Platform,
38}
39
40#[derive(Clone, Debug, Eq, Error, PartialEq)]
46#[error("{message}")]
47pub struct ContextAttachmentTeardownError {
48 message: String,
49}
50
51impl ContextAttachmentTeardownError {
52 pub fn new(message: impl Into<String>) -> Self {
54 Self {
55 message: message.into(),
56 }
57 }
58}
59
60#[derive(Clone, Debug, Eq, Error, PartialEq)]
66#[non_exhaustive]
67pub enum ContextPlatformWindowTeardownError {
68 #[error("Dear ImGui context teardown is in progress")]
70 ContextDropping,
71 #[error("platform-window teardown cannot be reentered")]
73 Reentrant,
74 #[error("platform attachment rejected platform-window teardown: {0}")]
76 AttachmentPreflight(#[source] ContextAttachmentTeardownError),
77 #[error("platform attachment could not complete platform-window teardown: {0}")]
79 AttachmentPostflight(#[source] ContextAttachmentTeardownError),
80 #[error("platform attachment panicked before platform-window teardown")]
82 BeginPanicked,
83 #[error("platform attachment panicked after platform-window teardown")]
85 EndPanicked,
86}
87
88pub trait ContextAttachment {
95 fn begin_platform_window_teardown(
101 &self,
102 _context: &ContextPlatformWindowTeardown<'_>,
103 ) -> Result<(), ContextAttachmentTeardownError> {
104 Ok(())
105 }
106
107 fn end_platform_window_teardown(
113 &self,
114 _context: &ContextPlatformWindowTeardown<'_>,
115 ) -> Result<(), ContextAttachmentTeardownError> {
116 Ok(())
117 }
118
119 fn quiesce(
121 &self,
122 _context: &ContextTeardown<'_>,
123 ) -> Result<(), ContextAttachmentTeardownError> {
124 Ok(())
125 }
126
127 fn release_renderer_resources(
129 &self,
130 _context: &ContextTeardown<'_>,
131 ) -> Result<(), ContextAttachmentTeardownError> {
132 Ok(())
133 }
134
135 fn release_platform_windows(
137 &self,
138 _context: &ContextTeardown<'_>,
139 ) -> Result<(), ContextAttachmentTeardownError> {
140 Ok(())
141 }
142
143 fn context_destroyed(&self, _context: ContextDestroyed) {}
145}
146
147#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
149#[non_exhaustive]
150pub enum ContextAttachmentError {
151 #[error("an attachment with this marker type is already registered")]
153 DuplicateAttachment,
154 #[error("the {0:?} attachment role is already occupied")]
156 RoleOccupied(ContextAttachmentRole),
157 #[error("a renderer attachment requires an active platform attachment")]
159 MissingPlatform,
160 #[error("Dear ImGui context teardown has already started")]
162 ContextDropping,
163}
164
165#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
171#[non_exhaustive]
172pub enum ContextAttachmentDetachError {
173 #[error("platform attachment release is already in progress")]
175 ReleaseInProgress,
176 #[error("the platform attachment cannot be detached while a renderer attachment is active")]
178 RendererActive,
179}
180
181#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
187#[non_exhaustive]
188pub enum ContextPlatformAttachmentReleaseError {
189 #[error("Dear ImGui context teardown is in progress")]
191 ContextDropping,
192 #[error("the platform attachment generation is no longer active")]
194 AttachmentInactive,
195 #[error("the supplied attachment does not own the platform role")]
197 NotPlatform,
198 #[error("the supplied attachment is not the active platform generation for this Context")]
200 PlatformGenerationMismatch,
201 #[error("platform attachment release is already in progress")]
203 ReleaseInProgress,
204 #[error("the platform attachment cannot be released while a renderer attachment is active")]
206 RendererActive,
207}
208
209pub struct ContextTeardown<'a> {
211 owner: NonNull<Context>,
212 phase: ContextAttachmentPhase,
213 renderer_texture_reset_active: Cell<bool>,
214 _exclusive_owner: PhantomData<&'a mut Context>,
215}
216
217impl fmt::Debug for ContextTeardown<'_> {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 f.debug_struct("ContextTeardown")
220 .field("id", &self.id())
221 .field("phase", &self.phase)
222 .finish_non_exhaustive()
223 }
224}
225
226impl ContextTeardown<'_> {
227 fn new<'owner>(
228 owner: &'owner mut Context,
229 phase: ContextAttachmentPhase,
230 ) -> ContextTeardown<'owner> {
231 ContextTeardown {
232 owner: NonNull::from(owner),
233 phase,
234 renderer_texture_reset_active: Cell::new(false),
235 _exclusive_owner: PhantomData,
236 }
237 }
238
239 fn state(&self) -> &ContextState {
240 unsafe { self.owner.as_ref().state.as_ref() }
243 }
244
245 pub fn id(&self) -> ContextId {
247 self.state().id()
248 }
249
250 pub fn phase(&self) -> ContextAttachmentPhase {
252 self.phase
253 }
254
255 pub fn with_bound_context<R>(&self, f: impl FnOnce() -> R) -> R {
259 assert_eq!(
260 self.state().lifecycle(),
261 ContextLifecycle::Dropping,
262 "ContextTeardown used outside pre-destroy teardown"
263 );
264 let raw = self.state().raw_during_teardown();
265 assert!(
266 !raw.is_null(),
267 "ContextTeardown used after native Context destruction"
268 );
269
270 binding::with_bound_context(raw, f)
271 }
272
273 pub fn with_renderer_texture_reset(
285 &self,
286 consumer: &impl RendererConsumerCapability,
287 release: impl FnOnce() -> Result<(), ContextAttachmentTeardownError>,
288 ) -> Result<(), ContextAttachmentTeardownError> {
289 if self.phase != ContextAttachmentPhase::RendererResources {
290 return Err(ContextAttachmentTeardownError::new(format!(
291 "renderer texture reset requires the RendererResources phase, not {:?}",
292 self.phase
293 )));
294 }
295 if self.state().lifecycle() != ContextLifecycle::Dropping {
296 return Err(ContextAttachmentTeardownError::new(
297 "renderer texture reset requires active Context teardown",
298 ));
299 }
300 if self.renderer_texture_reset_active.replace(true) {
301 return Err(ContextAttachmentTeardownError::new(
302 "renderer texture reset cannot be reentered",
303 ));
304 }
305 let _active = RendererTextureResetInvocation {
306 active: &self.renderer_texture_reset_active,
307 };
308
309 let watermark = unsafe { &mut *self.owner.as_ptr() }
313 .prepare_renderer_texture_reset_during_teardown(consumer)
314 .map_err(|error| {
315 ContextAttachmentTeardownError::new(format!(
316 "renderer texture reset preflight failed: {error}"
317 ))
318 })?;
319
320 release()?;
321
322 unsafe { &mut *self.owner.as_ptr() }
326 .commit_renderer_texture_reset_during_teardown(watermark);
327 Ok(())
328 }
329
330 #[cfg(test)]
331 pub(super) fn as_raw_for_test(&self) -> *mut crate::sys::ImGuiContext {
332 self.state().raw_during_teardown()
333 }
334}
335
336pub struct ContextPlatformWindowTeardown<'a> {
341 state: &'a ContextState,
342 _exclusive_owner: PhantomData<&'a mut Context>,
343}
344
345impl fmt::Debug for ContextPlatformWindowTeardown<'_> {
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 f.debug_struct("ContextPlatformWindowTeardown")
348 .field("id", &self.id())
349 .finish_non_exhaustive()
350 }
351}
352
353impl<'a> ContextPlatformWindowTeardown<'a> {
354 #[cfg(feature = "multi-viewport")]
355 pub(super) fn new(state: &'a ContextState) -> Self {
356 Self {
357 state,
358 _exclusive_owner: PhantomData,
359 }
360 }
361
362 pub fn id(&self) -> ContextId {
364 self.state.id()
365 }
366
367 pub fn with_bound_context<R>(&self, f: impl FnOnce() -> R) -> R {
373 assert_eq!(
374 self.state.lifecycle(),
375 ContextLifecycle::Alive,
376 "ContextPlatformWindowTeardown used outside a live Context"
377 );
378 let raw = self.state.raw_during_teardown();
379 assert!(
380 !raw.is_null(),
381 "ContextPlatformWindowTeardown used after native Context destruction"
382 );
383 binding::with_bound_context(raw, f)
384 }
385}
386
387struct RendererTextureResetInvocation<'a> {
388 active: &'a Cell<bool>,
389}
390
391impl Drop for RendererTextureResetInvocation<'_> {
392 fn drop(&mut self) {
393 self.active.set(false);
394 }
395}
396
397#[derive(Clone, Copy, Debug, Eq, PartialEq)]
399pub struct ContextDestroyed {
400 id: ContextId,
401}
402
403impl ContextDestroyed {
404 pub fn id(self) -> ContextId {
406 self.id
407 }
408}
409
410#[derive(Clone, Copy, Debug, Eq, PartialEq)]
411enum AttachmentState {
412 Active,
413 ReleasePrepared,
414 Teardown,
415 Complete,
416 Detached,
417}
418
419#[derive(Default)]
420struct AttachmentRoleState {
421 renderer_active: Cell<bool>,
422}
423
424pub(super) struct AttachmentControl {
425 marker: TypeId,
426 role: ContextAttachmentRole,
427 state: Cell<AttachmentState>,
428 attachment: RefCell<Option<Rc<dyn ContextAttachment>>>,
429 roles: Rc<AttachmentRoleState>,
430}
431
432impl AttachmentControl {
433 fn detach(&self) -> Result<bool, ContextAttachmentDetachError> {
434 match self.state.get() {
435 AttachmentState::Active => {}
436 AttachmentState::ReleasePrepared => {
437 return Err(ContextAttachmentDetachError::ReleaseInProgress);
438 }
439 AttachmentState::Teardown | AttachmentState::Complete | AttachmentState::Detached => {
440 return Ok(false);
441 }
442 }
443 if self.role == ContextAttachmentRole::Platform && self.roles.renderer_active.get() {
444 return Err(ContextAttachmentDetachError::RendererActive);
445 }
446 self.state.set(AttachmentState::Detached);
447 if self.role == ContextAttachmentRole::Renderer {
448 self.roles.renderer_active.set(false);
449 }
450 let attachment = self.attachment.borrow_mut().take();
451 drop(attachment);
452 Ok(true)
453 }
454
455 fn prepare_platform_release(&self) {
456 debug_assert_eq!(self.role, ContextAttachmentRole::Platform);
457 debug_assert_eq!(self.state.get(), AttachmentState::Active);
458 self.state.set(AttachmentState::ReleasePrepared);
459 }
460
461 fn abandon_platform_release(&self) {
462 if self.state.get() == AttachmentState::ReleasePrepared {
463 self.state.set(AttachmentState::Active);
464 }
465 }
466
467 fn commit_platform_release(&self) -> Option<Rc<dyn ContextAttachment>> {
468 debug_assert_eq!(self.role, ContextAttachmentRole::Platform);
469 debug_assert_eq!(self.state.get(), AttachmentState::ReleasePrepared);
470 debug_assert!(!self.roles.renderer_active.get());
471 self.state.set(AttachmentState::Detached);
472 self.attachment.borrow_mut().take()
473 }
474}
475
476impl fmt::Debug for AttachmentControl {
477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 f.debug_struct("AttachmentControl")
479 .field("marker", &self.marker)
480 .field("role", &self.role)
481 .field("state", &self.state.get())
482 .finish_non_exhaustive()
483 }
484}
485
486#[derive(Debug)]
491#[must_use = "retain the lease for explicit detach, or defer cleanup to Context teardown"]
492pub struct ContextAttachmentLease {
493 control: Weak<AttachmentControl>,
494 _not_send_or_sync: PhantomData<Rc<()>>,
495}
496
497impl ContextAttachmentLease {
498 pub fn handle(&self) -> ContextAttachmentHandle {
503 ContextAttachmentHandle {
504 control: self.control.clone(),
505 _not_send_or_sync: PhantomData,
506 }
507 }
508
509 pub fn detach(&mut self) -> Result<bool, ContextAttachmentDetachError> {
517 self.control
518 .upgrade()
519 .map_or(Ok(false), |control| control.detach())
520 }
521
522 pub fn is_attached(&self) -> bool {
524 self.control.upgrade().is_some_and(|control| {
525 matches!(
526 control.state.get(),
527 AttachmentState::Active | AttachmentState::ReleasePrepared
528 )
529 })
530 }
531
532 pub fn defer_to_context(mut self) {
538 self.control = Weak::new();
539 }
540}
541
542impl Drop for ContextAttachmentLease {
543 fn drop(&mut self) {
544 let _ = self.detach();
545 }
546}
547
548#[derive(Clone, Debug)]
553pub struct ContextAttachmentHandle {
554 control: Weak<AttachmentControl>,
555 _not_send_or_sync: PhantomData<Rc<()>>,
556}
557
558impl ContextAttachmentHandle {
559 pub fn is_attached(&self) -> bool {
561 self.control.upgrade().is_some_and(|control| {
562 matches!(
563 control.state.get(),
564 AttachmentState::Active | AttachmentState::ReleasePrepared
565 )
566 })
567 }
568
569 pub fn has_active_renderer_dependency(&self) -> bool {
575 self.control.upgrade().is_some_and(|control| {
576 control.role == ContextAttachmentRole::Platform
577 && matches!(
578 control.state.get(),
579 AttachmentState::Active | AttachmentState::ReleasePrepared
580 )
581 && control.roles.renderer_active.get()
582 })
583 }
584}
585
586#[must_use = "dropping the permit abandons platform detachment and keeps the attachment active"]
594pub struct ContextPlatformAttachmentRelease<'a> {
595 context: &'a mut Context,
596 control: Rc<AttachmentControl>,
597 committed: bool,
598}
599
600impl fmt::Debug for ContextPlatformAttachmentRelease<'_> {
601 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
602 formatter
603 .debug_struct("ContextPlatformAttachmentRelease")
604 .field("attachment", &self.control)
605 .field("committed", &self.committed)
606 .finish_non_exhaustive()
607 }
608}
609
610impl<'a> ContextPlatformAttachmentRelease<'a> {
611 pub(super) fn new(context: &'a mut Context, control: Rc<AttachmentControl>) -> Self {
612 Self {
613 context,
614 control,
615 committed: false,
616 }
617 }
618
619 pub fn context_mut(&mut self) -> &mut Context {
621 self.context
622 }
623
624 pub fn commit(mut self) {
631 let attachment = self.control.commit_platform_release();
632 self.committed = true;
633 drop(attachment);
634 }
635}
636
637impl Drop for ContextPlatformAttachmentRelease<'_> {
638 fn drop(&mut self) {
639 if !self.committed {
640 self.control.abandon_platform_release();
641 }
642 }
643}
644
645#[derive(Default)]
646pub(super) struct AttachmentRegistry {
647 controls: Vec<Rc<AttachmentControl>>,
648 roles: Rc<AttachmentRoleState>,
649 tearing_down: bool,
650 platform_window_teardown_active: Cell<bool>,
651}
652
653impl fmt::Debug for AttachmentRegistry {
654 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655 f.debug_struct("AttachmentRegistry")
656 .field("controls", &self.controls)
657 .field("tearing_down", &self.tearing_down)
658 .field(
659 "platform_window_teardown_active",
660 &self.platform_window_teardown_active.get(),
661 )
662 .finish()
663 }
664}
665
666impl AttachmentRegistry {
667 pub(super) fn preflight_register<Marker: 'static>(
668 &self,
669 lifecycle: ContextLifecycle,
670 role: ContextAttachmentRole,
671 ) -> Result<(), ContextAttachmentError> {
672 if lifecycle != ContextLifecycle::Alive || self.tearing_down {
673 return Err(ContextAttachmentError::ContextDropping);
674 }
675
676 let marker = TypeId::of::<Marker>();
677 if self.controls.iter().any(|control| {
678 control.marker == marker && control.state.get() != AttachmentState::Detached
679 }) {
680 return Err(ContextAttachmentError::DuplicateAttachment);
681 }
682 if role == ContextAttachmentRole::Renderer
683 && !self.role_is_operational(ContextAttachmentRole::Platform)
684 {
685 return Err(ContextAttachmentError::MissingPlatform);
686 }
687 if role != ContextAttachmentRole::Extension && self.role_is_active(role) {
688 return Err(ContextAttachmentError::RoleOccupied(role));
689 }
690 Ok(())
691 }
692
693 pub(super) fn register<Marker: 'static>(
694 &mut self,
695 lifecycle: ContextLifecycle,
696 role: ContextAttachmentRole,
697 attachment: Rc<dyn ContextAttachment>,
698 ) -> Result<ContextAttachmentLease, ContextAttachmentError> {
699 self.preflight_register::<Marker>(lifecycle, role)?;
700 self.controls
701 .retain(|control| control.state.get() != AttachmentState::Detached);
702 let marker = TypeId::of::<Marker>();
703
704 let control = Rc::new(AttachmentControl {
705 marker,
706 role,
707 state: Cell::new(AttachmentState::Active),
708 attachment: RefCell::new(Some(attachment)),
709 roles: Rc::clone(&self.roles),
710 });
711 if role == ContextAttachmentRole::Renderer {
712 self.roles.renderer_active.set(true);
713 }
714 let lease = ContextAttachmentLease {
715 control: Rc::downgrade(&control),
716 _not_send_or_sync: PhantomData,
717 };
718 self.controls.push(control);
719 Ok(lease)
720 }
721
722 fn role_is_active(&self, role: ContextAttachmentRole) -> bool {
723 self.controls.iter().any(|control| {
724 control.role == role
725 && matches!(
726 control.state.get(),
727 AttachmentState::Active | AttachmentState::ReleasePrepared
728 )
729 })
730 }
731
732 fn role_is_operational(&self, role: ContextAttachmentRole) -> bool {
733 self.controls
734 .iter()
735 .any(|control| control.role == role && control.state.get() == AttachmentState::Active)
736 }
737
738 pub(super) fn prepare_platform_release(
739 &self,
740 handle: &ContextAttachmentHandle,
741 ) -> Result<Rc<AttachmentControl>, ContextPlatformAttachmentReleaseError> {
742 if self.tearing_down {
743 return Err(ContextPlatformAttachmentReleaseError::ContextDropping);
744 }
745 let control = handle
746 .control
747 .upgrade()
748 .ok_or(ContextPlatformAttachmentReleaseError::AttachmentInactive)?;
749 if control.role != ContextAttachmentRole::Platform {
750 return Err(ContextPlatformAttachmentReleaseError::NotPlatform);
751 }
752 match control.state.get() {
753 AttachmentState::Active => {}
754 AttachmentState::ReleasePrepared => {
755 return Err(ContextPlatformAttachmentReleaseError::ReleaseInProgress);
756 }
757 AttachmentState::Teardown | AttachmentState::Complete | AttachmentState::Detached => {
758 return Err(ContextPlatformAttachmentReleaseError::AttachmentInactive);
759 }
760 }
761 let owns_active_generation = self.controls.iter().any(|candidate| {
762 Rc::ptr_eq(candidate, &control)
763 && candidate.role == ContextAttachmentRole::Platform
764 && candidate.state.get() == AttachmentState::Active
765 });
766 if !owns_active_generation {
767 return Err(ContextPlatformAttachmentReleaseError::PlatformGenerationMismatch);
768 }
769 if self.roles.renderer_active.get() {
770 return Err(ContextPlatformAttachmentReleaseError::RendererActive);
771 }
772 control.prepare_platform_release();
773 Ok(control)
774 }
775
776 #[cfg(feature = "multi-viewport")]
777 pub(super) fn begin_platform_window_teardown(
778 &self,
779 context: &ContextPlatformWindowTeardown<'_>,
780 ) -> Result<PlatformWindowTeardownInvocation<'_>, ContextPlatformWindowTeardownError> {
781 if self.tearing_down {
782 return Err(ContextPlatformWindowTeardownError::ContextDropping);
783 }
784 if self.platform_window_teardown_active.get() {
785 return Err(ContextPlatformWindowTeardownError::Reentrant);
786 }
787 self.platform_window_teardown_active.set(true);
788 let invocation = PlatformWindowTeardownInvocation {
789 attachment: self
790 .controls
791 .iter()
792 .find(|control| {
793 control.role == ContextAttachmentRole::Platform
794 && matches!(
795 control.state.get(),
796 AttachmentState::Active | AttachmentState::ReleasePrepared
797 )
798 })
799 .and_then(|control| control.attachment.borrow().clone()),
800 active: &self.platform_window_teardown_active,
801 };
802 invocation.begin(context)?;
803 Ok(invocation)
804 }
805
806 pub(super) fn begin_teardown(&mut self) -> Vec<Rc<AttachmentControl>> {
807 self.tearing_down = true;
808 let controls = std::mem::take(&mut self.controls);
809 controls
810 .into_iter()
811 .filter(|control| {
812 if !matches!(
813 control.state.get(),
814 AttachmentState::Active | AttachmentState::ReleasePrepared
815 ) {
816 return false;
817 }
818 control.state.set(AttachmentState::Teardown);
819 true
820 })
821 .collect()
822 }
823}
824
825#[cfg(feature = "multi-viewport")]
826pub(super) struct PlatformWindowTeardownInvocation<'a> {
827 attachment: Option<Rc<dyn ContextAttachment>>,
828 active: &'a Cell<bool>,
829}
830
831#[cfg(feature = "multi-viewport")]
832impl PlatformWindowTeardownInvocation<'_> {
833 fn begin(
834 &self,
835 context: &ContextPlatformWindowTeardown<'_>,
836 ) -> Result<(), ContextPlatformWindowTeardownError> {
837 let Some(attachment) = &self.attachment else {
838 return Ok(());
839 };
840 match catch_unwind(AssertUnwindSafe(|| {
841 attachment.begin_platform_window_teardown(context)
842 })) {
843 Ok(Ok(())) => Ok(()),
844 Ok(Err(error)) => Err(ContextPlatformWindowTeardownError::AttachmentPreflight(
845 error,
846 )),
847 Err(payload) => {
848 std::mem::forget(payload);
851 Err(ContextPlatformWindowTeardownError::BeginPanicked)
852 }
853 }
854 }
855
856 pub(super) fn finish(
857 self,
858 context: &ContextPlatformWindowTeardown<'_>,
859 ) -> Result<(), ContextPlatformWindowTeardownError> {
860 let Some(attachment) = &self.attachment else {
861 return Ok(());
862 };
863 match catch_unwind(AssertUnwindSafe(|| {
864 attachment.end_platform_window_teardown(context)
865 })) {
866 Ok(Ok(())) => Ok(()),
867 Ok(Err(error)) => Err(ContextPlatformWindowTeardownError::AttachmentPostflight(
868 error,
869 )),
870 Err(payload) => {
871 std::mem::forget(payload);
874 Err(ContextPlatformWindowTeardownError::EndPanicked)
875 }
876 }
877 }
878}
879
880#[cfg(feature = "multi-viewport")]
881impl Drop for PlatformWindowTeardownInvocation<'_> {
882 fn drop(&mut self) {
883 self.active.set(false);
884 }
885}
886
887pub(super) fn run_pre_destroy_phase(
888 controls: &[Rc<AttachmentControl>],
889 owner: &mut Context,
890 phase: ContextAttachmentPhase,
891) -> bool {
892 let context = ContextTeardown::new(owner, phase);
893 let mut completed = true;
894 for control in controls {
895 let Some(attachment) = control.attachment.borrow().clone() else {
896 continue;
897 };
898 let result = catch_unwind(AssertUnwindSafe(|| match phase {
899 ContextAttachmentPhase::Quiesce => attachment.quiesce(&context),
900 ContextAttachmentPhase::RendererResources => {
901 attachment.release_renderer_resources(&context)
902 }
903 ContextAttachmentPhase::PlatformWindows => {
904 attachment.release_platform_windows(&context)
905 }
906 }));
907 match result {
908 Ok(Ok(())) => {}
909 Ok(Err(error)) => {
910 completed = false;
911 std::mem::forget(error);
912 }
913 Err(payload) => {
914 completed = false;
915 std::mem::forget(payload);
919 }
920 }
921 }
922 completed
923}
924
925pub(super) fn run_post_destroy(
926 controls: Vec<Rc<AttachmentControl>>,
927 context_id: ContextId,
928) -> bool {
929 let context = ContextDestroyed { id: context_id };
930 let mut completed = true;
931 for control in controls {
932 if let Some(attachment) = control.attachment.borrow().clone() {
933 if let Err(payload) =
934 catch_unwind(AssertUnwindSafe(|| attachment.context_destroyed(context)))
935 {
936 completed = false;
937 std::mem::forget(payload);
938 }
939 }
940 control.state.set(AttachmentState::Complete);
941 let attachment = control.attachment.borrow_mut().take();
942 if let Err(payload) = catch_unwind(AssertUnwindSafe(move || drop(attachment))) {
943 completed = false;
944 std::mem::forget(payload);
945 }
946 }
947 completed
948}