From 892b694f56de6c104d4c548a7b3379bc98375f3b Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Thu, 9 Sep 2021 23:26:04 +0800 Subject: [PATCH 1/9] Support Linux on MIPS and ARMv5TE These platforms does not provide `std::sync::atomic::AtomicU64`. - Add a cargo feature "quanta" and make it a default feature. - Add a module `common::time` and conditionally choose the source file from "time_quanta.rs" or "time_compat.rs". - time_quanta.rs file has type aliases to `quanta::{Instant, Clock, Mock}`. It also has `AtomicInstant` holding an `AtomicU64`. - time_compat.rs file has `Instant` based on `std::time::Instant` and quanta-like `Clock` and `Mock`. It also has `AtomicInstant` holding a `RwLock>`. - Update Cache, etc. modules to use `common::time` instead of `quanta`. - Add a step to GitHub Actions to test future feature without quanta/ `AtomicU64`. --- .github/workflows/CI.yml | 9 ++++ .vscode/settings.json | 2 + Cargo.toml | 4 +- src/common.rs | 20 ++++---- src/common/time_compat.rs | 105 ++++++++++++++++++++++++++++++++++++++ src/common/time_quanta.rs | 42 +++++++++++++++ src/future/cache.rs | 5 +- src/sync.rs | 57 ++++++++++----------- src/sync/base_cache.rs | 53 ++++++++++--------- src/sync/cache.rs | 5 +- src/sync/invalidator.rs | 4 +- src/sync/segment.rs | 4 +- src/unsync.rs | 3 +- src/unsync/cache.rs | 7 ++- 14 files changed, 240 insertions(+), 80 deletions(-) create mode 100644 src/common/time_compat.rs create mode 100644 src/common/time_quanta.rs diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7c2f70d2..5c2b962a 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -68,6 +68,15 @@ jobs: env: RUSTFLAGS: '--cfg skeptic' + - name: Run tests (future, without quanta) + uses: actions-rs/cargo@v1 + if: ${{ matrix.rust != '1.45.2' }} + with: + command: test + args: --release --no-default-features --features future + env: + RUSTFLAGS: '--cfg skeptic' + - name: Run UI tests (future, trybuild) uses: actions-rs/cargo@v1 if: ${{ matrix.rust == 'stable' }} diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ba3d92d..d133ad48 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,13 @@ { "rust-analyzer.cargo.features": ["future"], "cSpell.words": [ + "aarch", "actix", "ahash", "benmanes", "CLFU", "clippy", + "compat", "cpus", "deqs", "Deque", diff --git a/Cargo.toml b/Cargo.toml index 3c8d9d2f..60f8eb40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ build = "build.rs" features = ["future"] [features] -default = [] +default = ["quanta"] future = ["async-io", "async-lock"] [dependencies] @@ -29,7 +29,6 @@ moka-cht = "0.4.2" num_cpus = "1.13" once_cell = "1.7" parking_lot = "0.11" -quanta = "0.9" scheduled-thread-pool = "0.2" thiserror = "1.0" uuid = { version = "0.8", features = ["v4"] } @@ -37,6 +36,7 @@ uuid = { version = "0.8", features = ["v4"] } # Optional dependencies async-io = { version = "1.4", optional = true } async-lock = { version = "2.4", optional = true } +quanta = { version = "0.9", optional = true } [dev-dependencies] actix-rt2 = { package = "actix-rt", version = "2", default-features = false } diff --git a/src/common.rs b/src/common.rs index 12c4793b..d1703945 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,4 +1,4 @@ -use quanta::Instant; +// use quanta::Instant; pub(crate) mod deque; pub(crate) mod error; @@ -6,17 +6,19 @@ pub(crate) mod frequency_sketch; pub(crate) mod thread_pool; pub(crate) mod unsafe_weak_pointer; +// targe_has_atomic is more convenient but yet unstable (Rust 1.55) +// https://github.com/rust-lang/rust/issues/32976 +// #[cfg_attr(target_has_atomic = "64", path = "common/time_quanta.rs")] + +#[cfg_attr(all(feature = "quanta", not(target_arch = "mips")), path = "common/time_quanta.rs")] +#[cfg_attr(any(not(feature = "quanta"), target_arch = "mips"), path = "common/time_compat.rs")] +pub(crate) mod time; + +use time::Instant; + pub(crate) trait AccessTime { fn last_accessed(&self) -> Option; fn set_last_accessed(&mut self, timestamp: Instant); fn last_modified(&self) -> Option; fn set_last_modified(&mut self, timestamp: Instant); } - -pub(crate) fn u64_to_instant(ts: u64) -> Option { - if ts == u64::MAX { - None - } else { - Some(unsafe { std::mem::transmute(ts) }) - } -} diff --git a/src/common/time_compat.rs b/src/common/time_compat.rs new file mode 100644 index 00000000..4d4a7b31 --- /dev/null +++ b/src/common/time_compat.rs @@ -0,0 +1,105 @@ +use std::{ + cmp::Ordering, + ops::Add, + sync::Arc, + time::{Duration, Instant as StdInstant}, +}; + +use parking_lot::RwLock; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) struct Instant(StdInstant); + +impl Instant { + pub(crate) fn now() -> Self { + Self(StdInstant::now()) + } +} + +impl Add for Instant { + type Output = Instant; + + fn add(self, other: Duration) -> Self::Output { + let instant = self + .0 + .checked_add(other) + .expect("overflow when adding duration to instant"); + Self(instant) + } +} + +impl PartialOrd for Instant { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Instant { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +pub(crate) struct AtomicInstant { + instant: RwLock>, +} + +impl Default for AtomicInstant { + fn default() -> Self { + Self { + instant: RwLock::new(None), + } + } +} + +impl AtomicInstant { + pub(crate) fn reset(&self) { + *self.instant.write() = None; + } + + pub(crate) fn is_set(&self) -> bool { + self.instant.read().is_some() + } + + pub(crate) fn instant(&self) -> Option { + *self.instant.read() + } + + pub(crate) fn set_instant(&self, instant: Instant) { + *self.instant.write() = Some(instant); + } +} + +pub(crate) struct Clock(Arc); + +impl Clock { + #[cfg(test)] + pub(crate) fn mock() -> (Clock, Arc) { + let mock = Arc::new(Mock::default()); + let clock = Clock(Arc::clone(&mock)); + (clock, mock) + } + + pub(crate) fn now(&self) -> Instant { + Instant(*self.0.now.read()) + } +} + +pub(crate) struct Mock { + now: RwLock, +} + +impl Default for Mock { + fn default() -> Self { + Self { + now: RwLock::new(StdInstant::now()), + } + } +} + +#[cfg(test)] +impl Mock { + pub(crate) fn increment(&self, amount: Duration) { + *self.now.write() += amount; + } +} diff --git a/src/common/time_quanta.rs b/src/common/time_quanta.rs new file mode 100644 index 00000000..50c8686d --- /dev/null +++ b/src/common/time_quanta.rs @@ -0,0 +1,42 @@ +use std::sync::atomic::{AtomicU64, Ordering}; + +pub(crate) type Instant = quanta::Instant; +pub(crate) type Clock = quanta::Clock; + +#[cfg(test)] +pub(crate) type Mock = quanta::Mock; + +pub(crate) struct AtomicInstant { + instant: AtomicU64, +} + +impl Default for AtomicInstant { + fn default() -> Self { + Self { + instant: AtomicU64::new(std::u64::MAX), + } + } +} + +impl AtomicInstant { + pub(crate) fn reset(&self) { + self.instant.store(std::u64::MAX, Ordering::Release); + } + + pub(crate) fn is_set(&self) -> bool { + self.instant.load(Ordering::Acquire) != u64::MAX + } + + pub(crate) fn instant(&self) -> Option { + let ts = self.instant.load(Ordering::Acquire); + if ts == u64::MAX { + None + } else { + Some(unsafe { std::mem::transmute(ts) }) + } + } + + pub(crate) fn set_instant(&self, instant: Instant) { + self.instant.store(instant.as_u64(), Ordering::Release); + } +} diff --git a/src/future/cache.rs b/src/future/cache.rs index 76c94162..178f058f 100644 --- a/src/future/cache.rs +++ b/src/future/cache.rs @@ -749,7 +749,7 @@ where self.base.reconfigure_for_testing(); } - fn set_expiration_clock(&self, clock: Option) { + fn set_expiration_clock(&self, clock: Option) { self.base.set_expiration_clock(clock); } } @@ -758,10 +758,9 @@ where #[cfg(test)] mod tests { use super::{Cache, ConcurrentCacheExt}; - use crate::future::CacheBuilder; + use crate::{common::time::Clock, future::CacheBuilder}; use async_io::Timer; - use quanta::Clock; use std::time::Duration; #[tokio::test] diff --git a/src/sync.rs b/src/sync.rs index 7fa63299..03999861 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -1,13 +1,16 @@ //! Provides thread-safe, blocking cache implementations. -use crate::common::{deque::DeqNode, u64_to_instant, AccessTime}; +use crate::common::{ + deque::DeqNode, + time::{AtomicInstant, Instant}, + AccessTime, +}; use parking_lot::Mutex; -use quanta::Instant; use std::{ ptr::NonNull, sync::{ - atomic::{AtomicBool, AtomicU64, Ordering}, + atomic::{AtomicBool, Ordering}, Arc, }, }; @@ -63,27 +66,27 @@ impl Clone for KeyHash { pub(crate) struct KeyDate { pub(crate) key: Arc, - pub(crate) timestamp: Arc, + pub(crate) timestamp: Arc, } impl KeyDate { - pub(crate) fn new(key: Arc, timestamp: Arc) -> Self { + pub(crate) fn new(key: Arc, timestamp: Arc) -> Self { Self { key, timestamp } } pub(crate) fn timestamp(&self) -> Option { - u64_to_instant(self.timestamp.load(Ordering::Acquire)) + self.timestamp.instant() } } pub(crate) struct KeyHashDate { pub(crate) key: Arc, pub(crate) hash: u64, - pub(crate) timestamp: Arc, + pub(crate) timestamp: Arc, } impl KeyHashDate { - pub(crate) fn new(kh: KeyHash, timestamp: Arc) -> Self { + pub(crate) fn new(kh: KeyHash, timestamp: Arc) -> Self { Self { key: kh.key, hash: kh.hash, @@ -109,8 +112,8 @@ unsafe impl Send for DeqNodes {} pub(crate) struct ValueEntry { pub(crate) value: V, is_admitted: Arc, - last_accessed: Arc, - last_modified: Arc, + last_accessed: Arc, + last_modified: Arc, nodes: Mutex>, } @@ -119,8 +122,8 @@ impl ValueEntry { Self { value, is_admitted: Arc::new(AtomicBool::new(false)), - last_accessed: Arc::new(AtomicU64::new(std::u64::MAX)), - last_modified: Arc::new(AtomicU64::new(std::u64::MAX)), + last_accessed: Default::default(), + last_modified: Default::default(), nodes: Mutex::new(DeqNodes { access_order_q_node: None, write_order_q_node: None, @@ -141,8 +144,8 @@ impl ValueEntry { // To prevent this updated ValueEntry from being evicted by a expiration policy, // set the max value to the timestamps. They will be replaced with the real // timestamps when applying writes. - last_accessed.store(std::u64::MAX, Ordering::Release); - last_modified.store(std::u64::MAX, Ordering::Release); + last_accessed.reset(); + last_modified.reset(); Self { value, is_admitted: Arc::clone(&other.is_admitted), @@ -160,11 +163,11 @@ impl ValueEntry { self.is_admitted.store(value, Ordering::Release); } - pub(crate) fn raw_last_accessed(&self) -> Arc { + pub(crate) fn raw_last_accessed(&self) -> Arc { Arc::clone(&self.last_accessed) } - pub(crate) fn raw_last_modified(&self) -> Arc { + pub(crate) fn raw_last_modified(&self) -> Arc { Arc::clone(&self.last_modified) } @@ -202,24 +205,22 @@ impl ValueEntry { impl AccessTime for Arc> { #[inline] fn last_accessed(&self) -> Option { - u64_to_instant(self.last_accessed.load(Ordering::Acquire)) + self.last_accessed.instant() } #[inline] fn set_last_accessed(&mut self, timestamp: Instant) { - self.last_accessed - .store(timestamp.as_u64(), Ordering::Release); + self.last_accessed.set_instant(timestamp); } #[inline] fn last_modified(&self) -> Option { - u64_to_instant(self.last_modified.load(Ordering::Acquire)) + self.last_modified.instant() } #[inline] fn set_last_modified(&mut self, timestamp: Instant) { - self.last_modified - .store(timestamp.as_u64(), Ordering::Release); + self.last_modified.set_instant(timestamp); } } @@ -236,28 +237,24 @@ impl AccessTime for DeqNode> { #[inline] fn last_modified(&self) -> Option { - u64_to_instant(self.element.timestamp.load(Ordering::Acquire)) + self.element.timestamp.instant() } #[inline] fn set_last_modified(&mut self, timestamp: Instant) { - self.element - .timestamp - .store(timestamp.as_u64(), Ordering::Release); + self.element.timestamp.set_instant(timestamp); } } impl AccessTime for DeqNode> { #[inline] fn last_accessed(&self) -> Option { - u64_to_instant(self.element.timestamp.load(Ordering::Acquire)) + self.element.timestamp.instant() } #[inline] fn set_last_accessed(&mut self, timestamp: Instant) { - self.element - .timestamp - .store(timestamp.as_u64(), Ordering::Release); + self.element.timestamp.set_instant(timestamp); } #[inline] diff --git a/src/sync/base_cache.rs b/src/sync/base_cache.rs index ab87c1a9..f4ab0465 100644 --- a/src/sync/base_cache.rs +++ b/src/sync/base_cache.rs @@ -8,6 +8,7 @@ use crate::{ common::{ deque::{CacheRegion, DeqNode, Deque}, frequency_sketch::FrequencySketch, + time::{AtomicInstant, Clock, Instant}, AccessTime, }, PredicateError, @@ -15,7 +16,6 @@ use crate::{ use crossbeam_channel::{Receiver, Sender, TrySendError}; use parking_lot::{Mutex, RwLock}; -use quanta::{Clock, Instant}; use std::{ borrow::Borrow, collections::hash_map::RandomState, @@ -24,7 +24,7 @@ use std::{ ptr::NonNull, rc::Rc, sync::{ - atomic::{AtomicBool, AtomicU64, AtomicU8, Ordering}, + atomic::{AtomicBool, AtomicU8, Ordering}, Arc, }, time::Duration, @@ -140,7 +140,7 @@ where } Some((arc_key, entry)) => { let i = &self.inner; - let (ttl, tti, va) = (&i.time_to_live(), &i.time_to_idle(), i.valid_after()); + let (ttl, tti, va) = (&i.time_to_live(), &i.time_to_idle(), &i.valid_after()); let now = i.current_time_from_expiration_clock(); if is_expired_entry_wo(ttl, va, &entry, now) @@ -339,7 +339,7 @@ where } } - pub(crate) fn set_expiration_clock(&self, clock: Option) { + pub(crate) fn set_expiration_clock(&self, clock: Option) { self.inner.set_expiration_clock(clock); } } @@ -358,7 +358,7 @@ pub(crate) struct Inner { write_op_ch: Receiver>, time_to_live: Option, time_to_idle: Option, - valid_after: AtomicU64, + valid_after: AtomicInstant, invalidator_enabled: bool, invalidator: RwLock>>, has_expiration_clock: AtomicBool, @@ -411,7 +411,7 @@ where write_op_ch, time_to_live, time_to_idle, - valid_after: AtomicU64::new(0), + valid_after: AtomicInstant::default(), invalidator_enabled, // When enabled, this field will be set later via the set_invalidator method. invalidator: RwLock::new(None), @@ -478,20 +478,23 @@ where } #[inline] - fn valid_after(&self) -> Instant { - let ts = self.valid_after.load(Ordering::Acquire); - unsafe { std::mem::transmute(ts) } + fn valid_after(&self) -> Option { + // let ts = self.valid_after.load(Ordering::Acquire); + // unsafe { std::mem::transmute(ts) } + self.valid_after.instant() } #[inline] fn set_valid_after(&self, timestamp: Instant) { - self.valid_after - .store(timestamp.as_u64(), Ordering::Release); + // self.valid_after + // .store(timestamp.as_u64(), Ordering::Release); + self.valid_after.set_instant(timestamp); } #[inline] fn has_valid_after(&self) -> bool { - self.valid_after.load(Ordering::Acquire) > 0 + // self.valid_after.load(Ordering::Acquire) > 0 + self.valid_after.is_set() } #[inline] @@ -749,8 +752,8 @@ where &self, kh: KeyHash, entry: &Arc>, - raw_last_accessed: Arc, - raw_last_modified: Arc, + raw_last_accessed: Arc, + raw_last_modified: Arc, deqs: &mut Deques, ) { let key = Arc::clone(&kh.key); @@ -822,7 +825,7 @@ where now: Instant, ) { let tti = &self.time_to_idle; - let va = self.valid_after(); + let va = &self.valid_after(); for _ in 0..batch_size { // Peek the front node of the deque and check if it is expired. let (key, _ts) = deq @@ -881,7 +884,7 @@ where #[inline] fn remove_expired_wo(&self, deqs: &mut Deques, batch_size: usize, now: Instant) { let ttl = &self.time_to_live; - let va = self.valid_after(); + let va = &self.valid_after(); for _ in 0..batch_size { let (key, _ts) = deqs .write_order @@ -1019,7 +1022,7 @@ where .unwrap_or(0) } - fn set_expiration_clock(&self, clock: Option) { + fn set_expiration_clock(&self, clock: Option) { let mut exp_clock = self.expiration_clock.write(); if let Some(clock) = clock { *exp_clock = Some(clock); @@ -1037,13 +1040,15 @@ where #[inline] fn is_expired_entry_ao( time_to_idle: &Option, - valid_after: Instant, + valid_after: &Option, entry: &impl AccessTime, now: Instant, ) -> bool { if let Some(ts) = entry.last_accessed() { - if ts < valid_after { - return true; + if let Some(va) = valid_after { + if ts < *va { + return true; + } } if let Some(tti) = time_to_idle { return ts + *tti <= now; @@ -1055,13 +1060,15 @@ fn is_expired_entry_ao( #[inline] fn is_expired_entry_wo( time_to_live: &Option, - valid_after: Instant, + valid_after: &Option, entry: &impl AccessTime, now: Instant, ) -> bool { if let Some(ts) = entry.last_modified() { - if ts < valid_after { - return true; + if let Some(va) = valid_after { + if ts < *va { + return true; + } } if let Some(ttl) = time_to_live { return ts + *ttl <= now; diff --git a/src/sync/cache.rs b/src/sync/cache.rs index d045556b..d95aa9c0 100644 --- a/src/sync/cache.rs +++ b/src/sync/cache.rs @@ -648,7 +648,7 @@ where self.base.reconfigure_for_testing(); } - pub(crate) fn set_expiration_clock(&self, clock: Option) { + pub(crate) fn set_expiration_clock(&self, clock: Option) { self.base.set_expiration_clock(clock); } } @@ -657,9 +657,8 @@ where #[cfg(test)] mod tests { use super::{Cache, ConcurrentCacheExt}; - use crate::sync::CacheBuilder; + use crate::{common::time::Clock, sync::CacheBuilder}; - use quanta::Clock; use std::time::Duration; #[test] diff --git a/src/sync/invalidator.rs b/src/sync/invalidator.rs index 8f3985fe..e0d77d9b 100644 --- a/src/sync/invalidator.rs +++ b/src/sync/invalidator.rs @@ -3,6 +3,7 @@ use crate::{ common::{ thread_pool::{PoolName, ThreadPool, ThreadPoolRegistry}, + time::Instant, unsafe_weak_pointer::UnsafeWeakPointer, AccessTime, }, @@ -12,13 +13,12 @@ use crate::{ use super::{base_cache::Inner, PredicateId, PredicateIdStr, ValueEntry}; use parking_lot::{Mutex, RwLock}; -use quanta::Instant; use std::{ collections::HashMap, hash::{BuildHasher, Hash}, marker::PhantomData, sync::{ - atomic::{AtomicBool, AtomicU64, Ordering}, + atomic::{AtomicBool, Ordering}, Arc, Weak, }, time::Duration, diff --git a/src/sync/segment.rs b/src/sync/segment.rs index 67c6bd77..42554ad4 100644 --- a/src/sync/segment.rs +++ b/src/sync/segment.rs @@ -324,7 +324,7 @@ where let mut exp_clock = MockExpirationClock::default(); for segment in self.inner.segments.iter() { - let (clock, mock) = quanta::Clock::mock(); + let (clock, mock) = crate::common::time::Clock::mock(); segment.set_expiration_clock(Some(clock)); exp_clock.mocks.push(mock); } @@ -337,7 +337,7 @@ where #[cfg(test)] #[derive(Default)] struct MockExpirationClock { - mocks: Vec>, + mocks: Vec>, } #[cfg(test)] diff --git a/src/unsync.rs b/src/unsync.rs index b3dd871c..64dd284e 100644 --- a/src/unsync.rs +++ b/src/unsync.rs @@ -8,9 +8,8 @@ use std::{ptr::NonNull, rc::Rc}; pub use builder::CacheBuilder; pub use cache::Cache; -use quanta::Instant; -use crate::common::{deque::DeqNode, AccessTime}; +use crate::common::{deque::DeqNode, time::Instant, AccessTime}; pub(crate) struct KeyDate { pub(crate) key: Rc, diff --git a/src/unsync/cache.rs b/src/unsync/cache.rs index 94d0ac03..96a0df5b 100644 --- a/src/unsync/cache.rs +++ b/src/unsync/cache.rs @@ -2,10 +2,10 @@ use super::{deques::Deques, KeyDate, KeyHashDate, ValueEntry}; use crate::common::{ deque::{CacheRegion, DeqNode, Deque}, frequency_sketch::FrequencySketch, + time::{Clock, Instant}, AccessTime, }; -use quanta::{Clock, Instant}; use std::{ borrow::Borrow, collections::{hash_map::RandomState, HashMap}, @@ -581,7 +581,7 @@ where K: Hash + Eq, S: BuildHasher + Clone, { - fn set_expiration_clock(&mut self, clock: Option) { + fn set_expiration_clock(&mut self, clock: Option) { self.expiration_clock = clock; } } @@ -590,9 +590,8 @@ where #[cfg(test)] mod tests { use super::Cache; - use crate::unsync::CacheBuilder; + use crate::{common::time::Clock, unsync::CacheBuilder}; - use quanta::Clock; use std::time::Duration; #[test] From df2eb3f64ec37e6f24065f0f166a56611b6f0a63 Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Thu, 9 Sep 2021 23:30:20 +0800 Subject: [PATCH 2/9] cargo fmt --- src/common.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/common.rs b/src/common.rs index d1703945..02461874 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,5 +1,3 @@ -// use quanta::Instant; - pub(crate) mod deque; pub(crate) mod error; pub(crate) mod frequency_sketch; @@ -10,8 +8,14 @@ pub(crate) mod unsafe_weak_pointer; // https://github.com/rust-lang/rust/issues/32976 // #[cfg_attr(target_has_atomic = "64", path = "common/time_quanta.rs")] -#[cfg_attr(all(feature = "quanta", not(target_arch = "mips")), path = "common/time_quanta.rs")] -#[cfg_attr(any(not(feature = "quanta"), target_arch = "mips"), path = "common/time_compat.rs")] +#[cfg_attr( + all(feature = "quanta", not(target_arch = "mips")), + path = "common/time_quanta.rs" +)] +#[cfg_attr( + any(not(feature = "quanta"), target_arch = "mips"), + path = "common/time_compat.rs" +)] pub(crate) mod time; use time::Instant; From b05f62cc175b846e8372c9ddbe2559eaea73153c Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Fri, 10 Sep 2021 08:16:36 +0800 Subject: [PATCH 3/9] Support Linux on MIPS and ARMv5TE Remove some old codes. --- src/sync/base_cache.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sync/base_cache.rs b/src/sync/base_cache.rs index f4ab0465..b1a54718 100644 --- a/src/sync/base_cache.rs +++ b/src/sync/base_cache.rs @@ -479,21 +479,16 @@ where #[inline] fn valid_after(&self) -> Option { - // let ts = self.valid_after.load(Ordering::Acquire); - // unsafe { std::mem::transmute(ts) } self.valid_after.instant() } #[inline] fn set_valid_after(&self, timestamp: Instant) { - // self.valid_after - // .store(timestamp.as_u64(), Ordering::Release); self.valid_after.set_instant(timestamp); } #[inline] fn has_valid_after(&self) -> bool { - // self.valid_after.load(Ordering::Acquire) > 0 self.valid_after.is_set() } From e93fc1e3f553e361b68894e3681946948ac8cd38 Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Fri, 10 Sep 2021 08:19:03 +0800 Subject: [PATCH 4/9] Support Linux on MIPS and ARMv5TE Remove target_arch = "mips" from the cfg_attr for the time module. (It will not work when quanta feature is still enabled it Cargo.toml) --- src/common.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/common.rs b/src/common.rs index 02461874..20d84088 100644 --- a/src/common.rs +++ b/src/common.rs @@ -8,14 +8,8 @@ pub(crate) mod unsafe_weak_pointer; // https://github.com/rust-lang/rust/issues/32976 // #[cfg_attr(target_has_atomic = "64", path = "common/time_quanta.rs")] -#[cfg_attr( - all(feature = "quanta", not(target_arch = "mips")), - path = "common/time_quanta.rs" -)] -#[cfg_attr( - any(not(feature = "quanta"), target_arch = "mips"), - path = "common/time_compat.rs" -)] +#[cfg_attr(feature = "quanta", path = "common/time_quanta.rs")] +#[cfg_attr(not(feature = "quanta"), path = "common/time_compat.rs")] pub(crate) mod time; use time::Instant; From 2ef09a7caf92b72de0c7a78a980aebfd2edcd4cd Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Fri, 10 Sep 2021 08:19:21 +0800 Subject: [PATCH 5/9] Support Linux on MIPS and ARMv5TE Add a section to the README to explain that `default-features = false` will be needed on these platforms. --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0e646fd7..bdbd4d70 100644 --- a/README.md +++ b/README.md @@ -319,10 +319,11 @@ This crate's minimum supported Rust versions (MSRV) are the followings: | Enabled Feature | MSRV | |:---------------------|:------------| -| no feature (default) | Rust 1.45.2 | +| no feature | Rust 1.45.2 | +| `quanta` (default) | Rust 1.45.2 | | `future` | Rust 1.46.0 | -If no feature is enabled, MSRV will be updated conservatively. When using other +If only the default features are enabled, MSRV will be updated conservatively. When using other features, like `future`, MSRV might be updated more frequently, up to the latest stable. In both cases, increasing MSRV is _not_ considered a semver-breaking change. @@ -334,6 +335,50 @@ change. --> +## Resolving Compile Errors on Some 32-bit Platforms + +On some 32-bit target platforms including the followings, you may encounter compile +errors in quanta crate and Moka itself. + +- `armv5te-unknown-linux-musleabi` +- `mips-unknown-linux-musl` +- `mipsel-unknown-linux-musl` + +```console +error[E0599]: no method named `fetch_add` found for struct `Arc>` in the current scope + --> ... /quanta-0.9.2/src/mock.rs:48:21 + | +48 | self.offset.fetch_add(amount.into_nanos()); + | ^^^^^^^^^ method not found in `Arc>` +``` + +```console +error[E0432]: unresolved import `std::sync::atomic::AtomicU64` + --> ... /moka-0.5.3/src/sync.rs:10:30 + | +10 | atomic::{AtomicBool, AtomicU64, Ordering}, + | ^^^^^^^^^ + | | + | no `AtomicU64` in `sync::atomic` +``` + +These errors occur because `std::sync::atomic::AtomicU64` type is not provided on +these platforms but both quanta and Moka use it. + +You can resolve the errors by disabling the default features of Moka. Edit your +Cargo.toml to add `default-features = false` option to the dependency declaration. + +```toml:Cargo.toml +[dependencies] +moka = { version = "0.5", default-feautures = false } +# Or +moka = { version = "0.5", default-feautures = false, features = ["future"] } +``` + +This will remove quanta from the dependencies and enable a fall-back implementation +in Moka, so it will compile. + + ## Developing Moka **Running All Tests** From 4407e87ecfde260ed39ddded5ee9b517c0757fa5 Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Fri, 10 Sep 2021 08:31:40 +0800 Subject: [PATCH 6/9] Update the change log --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d9cf54..c57d1784 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Moka — Change Log +## Version 0.5.3 + +### Added + +- Add support for some 32-bit platforms where `std::sync::atomic::AtomicU64` is not + provided. (e.g. `armv5te-unknown-linux-musleabi`, `mips-unknown-linux-musl`) + - On these platforms, you will need to disable the default features of Moka. + See [the relevant section][resolving-error-on-32bit] of the README. + + ## Version 0.5.2 ### Fixed @@ -104,6 +114,9 @@ [caffeine-git]: https://github.com/ben-manes/caffeine +[resolving-error-on-32bit]: https://github.com/moka-rs/moka#resolving-compile-errors-on-some-32-bit-platforms + +[gh-issue-0038]: https://github.com/moka-rs/moka/issues/38/ [gh-pull-0033]: https://github.com/moka-rs/moka/pull/33/ [gh-issue-0031]: https://github.com/moka-rs/moka/issues/31/ [gh-pull-0030]: https://github.com/moka-rs/moka/pull/30/ From 515c8b6e9a7e807538e080eeca5d4da861ca0178 Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Fri, 10 Sep 2021 19:42:20 +0800 Subject: [PATCH 7/9] Support Linux on MIPS and ARMv5TE - Rename the Cargo feature "quanta" to "atomic64". - Update the change log and readme. - Update a comment in the yml for GitHub Actions. --- .github/workflows/CI.yml | 2 +- CHANGELOG.md | 2 +- Cargo.toml | 10 +++++++++- README.md | 18 +++++++++--------- src/common.rs | 4 ++-- src/lib.rs | 19 ++++++++++--------- 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5c2b962a..2cd07db0 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -68,7 +68,7 @@ jobs: env: RUSTFLAGS: '--cfg skeptic' - - name: Run tests (future, without quanta) + - name: Run tests (future, without atomic64) uses: actions-rs/cargo@v1 if: ${{ matrix.rust != '1.45.2' }} with: diff --git a/CHANGELOG.md b/CHANGELOG.md index c57d1784..59dfff33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Added - Add support for some 32-bit platforms where `std::sync::atomic::AtomicU64` is not - provided. (e.g. `armv5te-unknown-linux-musleabi`, `mips-unknown-linux-musl`) + provided. (e.g. `armv5te-unknown-linux-musleabi` or `mips-unknown-linux-musl`) - On these platforms, you will need to disable the default features of Moka. See [the relevant section][resolving-error-on-32bit] of the README. diff --git a/Cargo.toml b/Cargo.toml index 60f8eb40..6b98d800 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,9 +20,17 @@ build = "build.rs" features = ["future"] [features] -default = ["quanta"] +default = ["atomic64"] + +# Enable this feature to use `moka::future::Cache`. future = ["async-io", "async-lock"] +# This feature is enabled by default. Disable it when the target platform does not +# support `std::sync::atomic::AtomicU64`. (e.g. `armv5te-unknown-linux-musleabi` +# or `mips-unknown-linux-musl`) +# https://github.com/moka-rs/moka#resolving-compile-errors-on-some-32-bit-platforms +atomic64 = ["quanta"] + [dependencies] crossbeam-channel = "0.5" moka-cht = "0.4.2" diff --git a/README.md b/README.md index bdbd4d70..77ef0edd 100644 --- a/README.md +++ b/README.md @@ -317,15 +317,15 @@ available on crates.io, such as the [aHash][ahash-crate] crate. This crate's minimum supported Rust versions (MSRV) are the followings: -| Enabled Feature | MSRV | -|:---------------------|:------------| -| no feature | Rust 1.45.2 | -| `quanta` (default) | Rust 1.45.2 | -| `future` | Rust 1.46.0 | - -If only the default features are enabled, MSRV will be updated conservatively. When using other -features, like `future`, MSRV might be updated more frequently, up to the latest -stable. In both cases, increasing MSRV is _not_ considered a semver-breaking +| Feature | Enabled by default? | MSRV | +|:-----------|:-------------------:|:-----------:| +| no feature | | Rust 1.45.2 | +| `atomic64` | yes | Rust 1.45.2 | +| `future` | | Rust 1.46.0 | + +If only the default features are enabled, MSRV will be updated conservatively. When +using other features, like `future`, MSRV might be updated more frequently, up to the +latest stable. In both cases, increasing MSRV is _not_ considered a semver-breaking change.