Skip to content

Commit 4501558

Browse files
committed
Adding Runtime support to callbacks
One of the oversights of background execution of callbacks was ensuring the cushy runtime was present. This forced me to ensure that only one Cushy instance was installed at any given time, as the thread that executes callbacks needs to enter the runtime. If this is set up before the PendingApp is created, we need to ensure that the Cushy instance PendingApp has and the one the callbacks have are the same one. By ensuring we can only have one global instance, we meet this guarantee and allow the casual user using the default runtime can create callbacks before creating their PendingApp and everything works.
1 parent f0823cc commit 4501558

File tree

6 files changed

+103
-50
lines changed

6 files changed

+103
-50
lines changed

src/app.rs

Lines changed: 76 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,13 @@ impl PendingApp {
6161
/// [`with_tracing()`](Self::with_tracing)/[`initialize_tracing()`](Self::initialize_tracing)
6262
/// to enable Cushy's built-in trace handling.
6363
pub fn new<Runtime: AppRuntime>(runtime: Runtime) -> Self {
64+
Self::from_cushy(Cushy::registered(BoxedRuntime(Box::new(runtime))))
65+
}
66+
67+
fn from_cushy(cushy: Cushy) -> Self {
6468
let mut app = kludgine::app::PendingApp::default();
6569
app.on_unrecoverable_error(Self::unrecoverable_error);
66-
Self {
67-
app,
68-
cushy: Cushy::new(BoxedRuntime(Box::new(runtime))),
69-
}
70+
Self { app, cushy }
7071
}
7172

7273
/// Sets the error handler that is invoked when Cushy encounters an error
@@ -163,7 +164,7 @@ impl Run for PendingApp {
163164

164165
impl Default for PendingApp {
165166
fn default() -> Self {
166-
Self::new(DefaultRuntime::default()).with_tracing()
167+
Self::from_cushy(Cushy::current()).with_tracing()
167168
}
168169
}
169170

@@ -330,6 +331,12 @@ impl Clone for BoxedRuntime {
330331
}
331332
}
332333

334+
impl Default for BoxedRuntime {
335+
fn default() -> Self {
336+
Self(Box::new(DefaultRuntime::default()))
337+
}
338+
}
339+
333340
trait BoxableRuntime: Send {
334341
fn enter_runtime(&self) -> RuntimeGuard<'_>;
335342
fn cloned(&self) -> BoxedRuntime;
@@ -359,64 +366,99 @@ struct AppSettings {
359366
multi_click_threshold: Duration,
360367
}
361368

369+
static RUNNING_CUSHY: Mutex<Option<Cushy>> = const { Mutex::new(None) };
370+
362371
/// Shared resources for a GUI application.
363372
#[derive(Clone)]
364373
pub struct Cushy {
365-
pub(crate) clipboard: Option<Arc<Mutex<Clipboard>>>,
366-
pub(crate) fonts: FontCollection,
367-
settings: Arc<Mutex<AppSettings>>,
374+
pub(crate) data: Arc<CushyData>,
368375
runtime: BoxedRuntime,
369-
#[cfg(feature = "localization")]
370-
pub(crate) localizations: Localizations,
371376
}
372377

373378
impl Cushy {
374-
fn new(runtime: BoxedRuntime) -> Self {
379+
fn registered(runtime: BoxedRuntime) -> Self {
380+
let cushy = Self::unregistered(runtime);
381+
382+
assert!(
383+
RUNNING_CUSHY.lock().replace(cushy.clone()).is_none(),
384+
"A Cushy instance was already initialized."
385+
);
386+
387+
cushy
388+
}
389+
390+
fn unregistered(runtime: BoxedRuntime) -> Self {
375391
Self {
376-
clipboard: Clipboard::new()
377-
.ok()
378-
.map(|clipboard| Arc::new(Mutex::new(clipboard))),
379-
fonts: FontCollection::default(),
380-
settings: Arc::new(Mutex::new(AppSettings {
381-
multi_click_threshold: Duration::from_millis(500),
382-
})),
392+
data: Arc::new(CushyData {
393+
clipboard: Clipboard::new()
394+
.ok()
395+
.map(|clipboard| Arc::new(Mutex::new(clipboard))),
396+
fonts: FontCollection::default(),
397+
settings: Mutex::new(AppSettings {
398+
multi_click_threshold: Duration::from_millis(500),
399+
}),
400+
#[cfg(feature = "localization")]
401+
localizations: Localizations::default(),
402+
}),
383403
runtime,
384-
#[cfg(feature = "localization")]
385-
localizations: Localizations::default(),
404+
}
405+
}
406+
407+
/// Returns the current Cushy instance, if initialized.
408+
///
409+
/// There can only be one Cushy instance per process.
410+
#[must_use]
411+
pub fn try_current() -> Option<Self> {
412+
RUNNING_CUSHY.lock().clone()
413+
}
414+
415+
/// Returns the current Cushy instance, initializing using the
416+
/// [`DefaultRuntime`] if needed.
417+
///
418+
/// There can only be one Cushy instance per process.
419+
#[must_use]
420+
pub fn current() -> Self {
421+
let mut current = RUNNING_CUSHY.lock();
422+
if let Some(cushy) = &*current {
423+
cushy.clone()
424+
} else {
425+
let default = Cushy::unregistered(BoxedRuntime::default());
426+
*current = Some(default.clone());
427+
default
386428
}
387429
}
388430

389431
/// Returns the duration between two mouse clicks that should be allowed to
390432
/// elapse for the clicks to be considered separate actions.
391433
#[must_use]
392434
pub fn multi_click_threshold(&self) -> Duration {
393-
self.settings.lock().multi_click_threshold
435+
self.data.settings.lock().multi_click_threshold
394436
}
395437

396438
/// Sets the maximum time between sequential clicks that should be
397439
/// considered the same action.
398440
pub fn set_multi_click_threshold(&self, threshold: Duration) {
399-
self.settings.lock().multi_click_threshold = threshold;
441+
self.data.settings.lock().multi_click_threshold = threshold;
400442
}
401443

402444
/// Returns a locked mutex guard to the OS's clipboard, if one was able to be
403445
/// initialized when the window opened.
404446
#[must_use]
405447
pub fn clipboard_guard(&self) -> Option<MutexGuard<'_, Clipboard>> {
406-
self.clipboard.as_ref().map(|mutex| mutex.lock())
448+
self.data.clipboard.as_ref().map(|mutex| mutex.lock())
407449
}
408450

409451
/// Returns the font collection that will be loaded in all Cushy windows.
410452
#[must_use]
411453
pub fn fonts(&self) -> &FontCollection {
412-
&self.fonts
454+
&self.data.fonts
413455
}
414456

415457
/// Returns the localizations that are applied throughout the application.
416458
#[must_use]
417459
#[cfg(feature = "localization")]
418460
pub fn localizations(&self) -> &Localizations {
419-
&self.localizations
461+
&self.data.localizations
420462
}
421463

422464
/// Enters the application's runtime context.
@@ -433,10 +475,18 @@ impl Cushy {
433475

434476
impl Default for Cushy {
435477
fn default() -> Self {
436-
Self::new(BoxedRuntime(Box::<DefaultRuntime>::default()))
478+
Self::registered(BoxedRuntime::default())
437479
}
438480
}
439481

482+
pub(crate) struct CushyData {
483+
pub(crate) clipboard: Option<Arc<Mutex<Clipboard>>>,
484+
pub(crate) fonts: FontCollection,
485+
settings: Mutex<AppSettings>,
486+
#[cfg(feature = "localization")]
487+
pub(crate) localizations: Localizations,
488+
}
489+
440490
/// A type that is a Cushy application.
441491
pub trait Application: AsApplication<AppEvent<WindowCommand>> {
442492
/// Returns the shared resources for the application.

src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ impl MaybeLocalized {
8484
match self {
8585
MaybeLocalized::Text(text) => text.clone(),
8686
#[cfg(feature = "localization")]
87-
MaybeLocalized::Localized(localized) => {
88-
localized.localize(&localization::WindowTranslationContext(&app.localizations))
89-
}
87+
MaybeLocalized::Localized(localized) => localized.localize(
88+
&localization::WindowTranslationContext(&app.data.localizations),
89+
),
9090
}
9191
}
9292
}

src/reactive.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use tracing::warn;
1414

1515
use self::channel::{AnyChannel, ChannelCallbackFuture};
1616
use self::value::{CallbackDisconnected, DeadlockError, DynamicLockData};
17-
use crate::Lazy;
17+
use crate::{Cushy, Lazy};
1818

1919
pub mod channel;
2020
pub mod value;
@@ -280,6 +280,8 @@ impl CallbackExecutor {
280280

281281
fn run(mut self) {
282282
IS_EXECUTOR_THREAD.set(true);
283+
let cushy = Cushy::current();
284+
let _runtime = cushy.enter_runtime();
283285

284286
// Because this is stored in a static, this likely will never return an
285287
// error, but if it does, it's during program shutdown, and we can exit safely.

src/reactive/channel.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ use builder::Builder;
9797
use parking_lot::{Condvar, Mutex, MutexGuard};
9898
use sealed::{AnyChannelCallback, AsyncCallbackFuture, CallbackKind, ChannelCallbackError};
9999

100-
use crate::reactive::{enqueue_task, BackgroundTask, ChannelTask};
100+
use super::enqueue_task;
101+
use crate::reactive::{BackgroundTask, ChannelTask};
101102
use crate::value::CallbackDisconnected;
102103

103104
pub mod builder;

src/reactive/channel/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Builder types for Cushy [`channel`](super)qs.
1+
//! Builder types for Cushy [`channel`](super)s.
22
use std::future::Future;
33
use std::marker::PhantomData;
44

src/window.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,7 +1427,7 @@ where
14271427
self.theme_mode.get(),
14281428
&mut self.cursor,
14291429
#[cfg(feature = "localization")]
1430-
&self.app.cushy().localizations,
1430+
&self.app.cushy().data.localizations,
14311431
),
14321432
kludgine,
14331433
)
@@ -1442,7 +1442,7 @@ where
14421442
self.theme_mode.get(),
14431443
&mut self.cursor,
14441444
#[cfg(feature = "localization")]
1445-
&self.app.cushy().localizations,
1445+
&self.app.cushy().data.localizations,
14461446
),
14471447
kludgine,
14481448
)
@@ -1463,7 +1463,7 @@ where
14631463
self.theme_mode.get(),
14641464
&mut self.cursor,
14651465
#[cfg(feature = "localization")]
1466-
&self.app.cushy().localizations,
1466+
&self.app.cushy().data.localizations,
14671467
),
14681468
kludgine,
14691469
)
@@ -1498,7 +1498,7 @@ where
14981498
self.theme_mode.get(),
14991499
&mut self.cursor,
15001500
#[cfg(feature = "localization")]
1501-
&self.app.cushy().localizations,
1501+
&self.app.cushy().data.localizations,
15021502
),
15031503
graphics,
15041504
);
@@ -1651,7 +1651,7 @@ where
16511651
self.theme_mode.get(),
16521652
&mut self.cursor,
16531653
#[cfg(feature = "localization")]
1654-
&self.app.cushy().localizations,
1654+
&self.app.cushy().data.localizations,
16551655
),
16561656
kludgine,
16571657
);
@@ -1686,7 +1686,7 @@ where
16861686
self.theme_mode.get(),
16871687
&mut self.cursor,
16881688
#[cfg(feature = "localization")]
1689-
&self.app.cushy().localizations,
1689+
&self.app.cushy().data.localizations,
16901690
),
16911691
kludgine,
16921692
);
@@ -1753,7 +1753,7 @@ where
17531753
let app = settings.app.clone();
17541754
let fonts = Self::load_fonts(
17551755
&mut settings,
1756-
app.cushy().fonts.clone(),
1756+
app.cushy().data.fonts.clone(),
17571757
graphics.font_system().db_mut(),
17581758
);
17591759

@@ -1902,7 +1902,7 @@ where
19021902
self.theme_mode.get(),
19031903
&mut self.cursor,
19041904
#[cfg(feature = "localization")]
1905-
&self.app.cushy().localizations,
1905+
&self.app.cushy().data.localizations,
19061906
),
19071907
gfx: Exclusive::Owned(Graphics::new(graphics)),
19081908
};
@@ -2190,7 +2190,7 @@ where
21902190
self.theme_mode.get(),
21912191
&mut self.cursor,
21922192
#[cfg(feature = "localization")]
2193-
&self.app.cushy().localizations,
2193+
&self.app.cushy().data.localizations,
21942194
),
21952195
kludgine,
21962196
);
@@ -2253,7 +2253,7 @@ where
22532253
self.theme_mode.get(),
22542254
&mut self.cursor,
22552255
#[cfg(feature = "localization")]
2256-
&self.app.cushy().localizations,
2256+
&self.app.cushy().data.localizations,
22572257
),
22582258
kludgine,
22592259
);
@@ -2298,7 +2298,7 @@ where
22982298
self.theme_mode.get(),
22992299
&mut self.cursor,
23002300
#[cfg(feature = "localization")]
2301-
&self.app.cushy().localizations,
2301+
&self.app.cushy().data.localizations,
23022302
),
23032303
kludgine,
23042304
);
@@ -2345,7 +2345,7 @@ where
23452345
self.theme_mode.get(),
23462346
&mut self.cursor,
23472347
#[cfg(feature = "localization")]
2348-
&self.app.cushy().localizations,
2348+
&self.app.cushy().data.localizations,
23492349
),
23502350
kludgine,
23512351
)
@@ -2366,7 +2366,7 @@ where
23662366
self.theme_mode.get(),
23672367
&mut self.cursor,
23682368
#[cfg(feature = "localization")]
2369-
&self.app.cushy().localizations,
2369+
&self.app.cushy().data.localizations,
23702370
),
23712371
kludgine,
23722372
);
@@ -2408,7 +2408,7 @@ where
24082408
self.theme_mode.get(),
24092409
&mut self.cursor,
24102410
#[cfg(feature = "localization")]
2411-
&self.app.cushy().localizations,
2411+
&self.app.cushy().data.localizations,
24122412
),
24132413
kludgine,
24142414
);
@@ -2453,7 +2453,7 @@ where
24532453
self.theme_mode.get(),
24542454
&mut self.cursor,
24552455
#[cfg(feature = "localization")]
2456-
&self.app.cushy().localizations,
2456+
&self.app.cushy().data.localizations,
24572457
),
24582458
kludgine,
24592459
),
@@ -2481,7 +2481,7 @@ where
24812481
self.theme_mode.get(),
24822482
&mut self.cursor,
24832483
#[cfg(feature = "localization")]
2484-
&self.app.cushy().localizations,
2484+
&self.app.cushy().data.localizations,
24852485
),
24862486
kludgine,
24872487
)
@@ -2532,7 +2532,7 @@ where
25322532
self.theme_mode.get(),
25332533
&mut self.cursor,
25342534
#[cfg(feature = "localization")]
2535-
&self.app.cushy().localizations,
2535+
&self.app.cushy().data.localizations,
25362536
),
25372537
kludgine,
25382538
);
@@ -2983,7 +2983,7 @@ where
29832983
self.theme_mode.get(),
29842984
&mut self.cursor,
29852985
#[cfg(feature = "localization")]
2986-
&self.app.cushy().localizations,
2986+
&self.app.cushy().data.localizations,
29872987
),
29882988
kludgine,
29892989
);

0 commit comments

Comments
 (0)