summaryrefslogtreecommitdiff
path: root/ext/web/hr_timer_lock.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ext/web/hr_timer_lock.rs')
-rw-r--r--ext/web/hr_timer_lock.rs67
1 files changed, 67 insertions, 0 deletions
diff --git a/ext/web/hr_timer_lock.rs b/ext/web/hr_timer_lock.rs
new file mode 100644
index 000000000..f1f588d6c
--- /dev/null
+++ b/ext/web/hr_timer_lock.rs
@@ -0,0 +1,67 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+#[cfg(target_os = "windows")]
+mod windows {
+ use std::marker::PhantomData;
+ use std::sync::atomic::AtomicU32;
+
+ pub(crate) struct HrTimerLock {
+ pub(super) _unconstructable: PhantomData<()>,
+ }
+
+ /// Decrease the reference count of the HR timer on drop.
+ impl Drop for HrTimerLock {
+ fn drop(&mut self) {
+ dec_ref();
+ }
+ }
+
+ /// Maintains the HR timer refcount. This should be more than sufficient as 2^32 timers would be
+ /// an impossible situation, and if it does somehow happen, the worst case is that we'll disable
+ /// the high-res timer when we shouldn't (and things would eventually return to proper operation).
+ static TIMER_REFCOUNT: AtomicU32 = AtomicU32::new(0);
+
+ pub(super) fn inc_ref() {
+ let old = TIMER_REFCOUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
+ // Overflow/underflow sanity check in debug mode
+ debug_assert!(old != u32::MAX);
+ if old == 0 {
+ lock_hr();
+ }
+ }
+
+ fn dec_ref() {
+ let old = TIMER_REFCOUNT.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
+ // Overflow/underflow sanity check in debug mode
+ debug_assert!(old != 0);
+ if old == 1 {
+ unlock_hr();
+ }
+ }
+
+ /// If the refcount is > 0, we ask Windows for a lower timer period once. While the underlying
+ /// Windows timeBeginPeriod/timeEndPeriod API can manage its own reference counts, we choose to
+ /// use it once per process and avoid nesting these calls.
+ fn lock_hr() {
+ // SAFETY: We just want to set the timer period here
+ unsafe { windows_sys::Win32::Media::timeBeginPeriod(1) };
+ }
+
+ fn unlock_hr() {
+ // SAFETY: We just want to set the timer period here
+ unsafe { windows_sys::Win32::Media::timeEndPeriod(1) };
+ }
+}
+
+#[cfg(target_os = "windows")]
+pub(crate) fn hr_timer_lock() -> windows::HrTimerLock {
+ windows::inc_ref();
+ windows::HrTimerLock {
+ _unconstructable: Default::default(),
+ }
+}
+
+/// No-op on other platforms.
+#[cfg(not(target_os = "windows"))]
+pub(crate) fn hr_timer_lock() -> (std::marker::PhantomData<()>,) {
+ (std::marker::PhantomData::default(),)
+}