Skip to content

Commit b60bc74

Browse files
authored
Merge pull request #39 from moka-rs/mips-armv5
Support Linux on MIPS and ARMv5TE
2 parents 988816a + 13263a2 commit b60bc74

18 files changed

+323
-100
lines changed

.github/workflows/CI.yml

+9
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ jobs:
6868
env:
6969
RUSTFLAGS: '--cfg skeptic'
7070

71+
- name: Run tests (future, without atomic64)
72+
uses: actions-rs/cargo@v1
73+
if: ${{ matrix.rust != '1.45.2' }}
74+
with:
75+
command: test
76+
args: --release --no-default-features --features future
77+
env:
78+
RUSTFLAGS: '--cfg skeptic'
79+
7180
- name: Run UI tests (future, trybuild)
7281
uses: actions-rs/cargo@v1
7382
if: ${{ matrix.rust == 'stable' }}

.vscode/settings.json

+2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
{
22
"rust-analyzer.cargo.features": ["future"],
33
"cSpell.words": [
4+
"aarch",
45
"actix",
56
"ahash",
67
"benmanes",
78
"CLFU",
89
"clippy",
10+
"compat",
911
"cpus",
1012
"deqs",
1113
"Deque",

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Moka — Change Log
22

3+
## Version 0.5.3
4+
5+
### Added
6+
7+
- Add support for some 32-bit platforms where `std::sync::atomic::AtomicU64` is not
8+
provided. (e.g. `armv5te-unknown-linux-musleabi` or `mips-unknown-linux-musl`)
9+
- On these platforms, you will need to disable the default features of Moka.
10+
See [the relevant section][resolving-error-on-32bit] of the README.
11+
12+
313
## Version 0.5.2
414

515
### Fixed
@@ -104,6 +114,9 @@
104114

105115
[caffeine-git]: https://github.com/ben-manes/caffeine
106116

117+
[resolving-error-on-32bit]: https://github.com/moka-rs/moka#resolving-compile-errors-on-some-32-bit-platforms
118+
119+
[gh-issue-0038]: https://github.com/moka-rs/moka/issues/38/
107120
[gh-pull-0033]: https://github.com/moka-rs/moka/pull/33/
108121
[gh-issue-0031]: https://github.com/moka-rs/moka/issues/31/
109122
[gh-pull-0030]: https://github.com/moka-rs/moka/pull/30/

Cargo.toml

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "moka"
3-
version = "0.5.2"
3+
version = "0.5.3"
44
authors = ["Tatsuya Kawano <tatsuya@hibaridb.org>"]
55
edition = "2018"
66

@@ -20,23 +20,31 @@ build = "build.rs"
2020
features = ["future"]
2121

2222
[features]
23-
default = []
23+
default = ["atomic64"]
24+
25+
# Enable this feature to use `moka::future::Cache`.
2426
future = ["async-io", "async-lock"]
2527

28+
# This feature is enabled by default. Disable it when the target platform does not
29+
# support `std::sync::atomic::AtomicU64`. (e.g. `armv5te-unknown-linux-musleabi`
30+
# or `mips-unknown-linux-musl`)
31+
# https://github.com/moka-rs/moka#resolving-compile-errors-on-some-32-bit-platforms
32+
atomic64 = ["quanta"]
33+
2634
[dependencies]
2735
crossbeam-channel = "0.5"
2836
moka-cht = "0.4.2"
2937
num_cpus = "1.13"
3038
once_cell = "1.7"
3139
parking_lot = "0.11"
32-
quanta = "0.9"
3340
scheduled-thread-pool = "0.2"
3441
thiserror = "1.0"
3542
uuid = { version = "0.8", features = ["v4"] }
3643

3744
# Optional dependencies
3845
async-io = { version = "1.4", optional = true }
3946
async-lock = { version = "2.4", optional = true }
47+
quanta = { version = "0.9", optional = true }
4048

4149
[dev-dependencies]
4250
actix-rt2 = { package = "actix-rt", version = "2", default-features = false }

README.md

+53-8
Original file line numberDiff line numberDiff line change
@@ -317,14 +317,15 @@ available on crates.io, such as the [aHash][ahash-crate] crate.
317317

318318
This crate's minimum supported Rust versions (MSRV) are the followings:
319319

320-
| Enabled Feature | MSRV |
321-
|:---------------------|:------------|
322-
| no feature (default) | Rust 1.45.2 |
323-
| `future` | Rust 1.46.0 |
324-
325-
If no feature is enabled, MSRV will be updated conservatively. When using other
326-
features, like `future`, MSRV might be updated more frequently, up to the latest
327-
stable. In both cases, increasing MSRV is _not_ considered a semver-breaking
320+
| Feature | Enabled by default? | MSRV |
321+
|:-----------|:-------------------:|:-----------:|
322+
| no feature | | Rust 1.45.2 |
323+
| `atomic64` | yes | Rust 1.45.2 |
324+
| `future` | | Rust 1.46.0 |
325+
326+
If only the default features are enabled, MSRV will be updated conservatively. When
327+
using other features, like `future`, MSRV might be updated more frequently, up to the
328+
latest stable. In both cases, increasing MSRV is _not_ considered a semver-breaking
328329
change.
329330

330331
<!--
@@ -334,6 +335,50 @@ change.
334335
-->
335336

336337

338+
## Resolving Compile Errors on Some 32-bit Platforms
339+
340+
On some 32-bit target platforms including the followings, you may encounter compile
341+
errors in quanta crate and Moka itself.
342+
343+
- `armv5te-unknown-linux-musleabi`
344+
- `mips-unknown-linux-musl`
345+
- `mipsel-unknown-linux-musl`
346+
347+
```console
348+
error[E0599]: no method named `fetch_add` found for struct `Arc<AtomicCell<u64>>` in the current scope
349+
--> ... /quanta-0.9.2/src/mock.rs:48:21
350+
|
351+
48 | self.offset.fetch_add(amount.into_nanos());
352+
| ^^^^^^^^^ method not found in `Arc<AtomicCell<u64>>`
353+
```
354+
355+
```console
356+
error[E0432]: unresolved import `std::sync::atomic::AtomicU64`
357+
--> ... /moka-0.5.3/src/sync.rs:10:30
358+
|
359+
10 | atomic::{AtomicBool, AtomicU64, Ordering},
360+
| ^^^^^^^^^
361+
| |
362+
| no `AtomicU64` in `sync::atomic`
363+
```
364+
365+
These errors occur because `std::sync::atomic::AtomicU64` type is not provided on
366+
these platforms but both quanta and Moka use it.
367+
368+
You can resolve the errors by disabling the default features of Moka. Edit your
369+
Cargo.toml to add `default-features = false` option to the dependency declaration.
370+
371+
```toml:Cargo.toml
372+
[dependencies]
373+
moka = { version = "0.5", default-feautures = false }
374+
# Or
375+
moka = { version = "0.5", default-feautures = false, features = ["future"] }
376+
```
377+
378+
This will remove quanta from the dependencies and enable a fall-back implementation
379+
in Moka, so it will compile.
380+
381+
337382
## Developing Moka
338383

339384
**Running All Tests**

src/common.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
use quanta::Instant;
2-
31
pub(crate) mod deque;
42
pub(crate) mod error;
53
pub(crate) mod frequency_sketch;
64
pub(crate) mod thread_pool;
75
pub(crate) mod unsafe_weak_pointer;
86

7+
// targe_has_atomic is more convenient but yet unstable (Rust 1.55)
8+
// https://github.com/rust-lang/rust/issues/32976
9+
// #[cfg_attr(target_has_atomic = "64", path = "common/time_quanta.rs")]
10+
11+
#[cfg_attr(feature = "atomic64", path = "common/time_quanta.rs")]
12+
#[cfg_attr(not(feature = "atomic64"), path = "common/time_compat.rs")]
13+
pub(crate) mod time;
14+
15+
use time::Instant;
16+
917
pub(crate) trait AccessTime {
1018
fn last_accessed(&self) -> Option<Instant>;
1119
fn set_last_accessed(&mut self, timestamp: Instant);
1220
fn last_modified(&self) -> Option<Instant>;
1321
fn set_last_modified(&mut self, timestamp: Instant);
1422
}
15-
16-
pub(crate) fn u64_to_instant(ts: u64) -> Option<Instant> {
17-
if ts == u64::MAX {
18-
None
19-
} else {
20-
Some(unsafe { std::mem::transmute(ts) })
21-
}
22-
}

src/common/thread_pool.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ impl ThreadPoolRegistry {
5353
// and insert a new pool.
5454
let mut pools = REGISTRY.pools.write();
5555
pools.entry(name).or_insert_with(|| {
56-
let num_threads = num_cpus::get();
56+
// Some platforms may return 0. In that case, use 1.
57+
// https://github.com/moka-rs/moka/pull/39#issuecomment-916888859
58+
// https://github.com/rust-lang/futures-rs/pull/1835
59+
let num_threads = num_cpus::get().max(1);
5760
let pool =
5861
ScheduledThreadPool::with_name(name.thread_name_template(), num_threads);
5962
let t_pool = ThreadPool {

src/common/time_compat.rs

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use std::{
2+
cmp::Ordering,
3+
ops::Add,
4+
sync::Arc,
5+
time::{Duration, Instant as StdInstant},
6+
};
7+
8+
use parking_lot::RwLock;
9+
10+
#[derive(Clone, Copy, PartialEq, Eq)]
11+
pub(crate) struct Instant(StdInstant);
12+
13+
impl Instant {
14+
pub(crate) fn now() -> Self {
15+
Self(StdInstant::now())
16+
}
17+
}
18+
19+
impl Add<Duration> for Instant {
20+
type Output = Instant;
21+
22+
fn add(self, other: Duration) -> Self::Output {
23+
let instant = self
24+
.0
25+
.checked_add(other)
26+
.expect("overflow when adding duration to instant");
27+
Self(instant)
28+
}
29+
}
30+
31+
impl PartialOrd for Instant {
32+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
33+
Some(self.cmp(other))
34+
}
35+
}
36+
37+
impl Ord for Instant {
38+
fn cmp(&self, other: &Self) -> Ordering {
39+
self.0.cmp(&other.0)
40+
}
41+
}
42+
43+
pub(crate) struct AtomicInstant {
44+
instant: RwLock<Option<Instant>>,
45+
}
46+
47+
impl Default for AtomicInstant {
48+
fn default() -> Self {
49+
Self {
50+
instant: RwLock::new(None),
51+
}
52+
}
53+
}
54+
55+
impl AtomicInstant {
56+
pub(crate) fn reset(&self) {
57+
*self.instant.write() = None;
58+
}
59+
60+
pub(crate) fn is_set(&self) -> bool {
61+
self.instant.read().is_some()
62+
}
63+
64+
pub(crate) fn instant(&self) -> Option<Instant> {
65+
*self.instant.read()
66+
}
67+
68+
pub(crate) fn set_instant(&self, instant: Instant) {
69+
*self.instant.write() = Some(instant);
70+
}
71+
}
72+
73+
pub(crate) struct Clock(Arc<Mock>);
74+
75+
impl Clock {
76+
#[cfg(test)]
77+
pub(crate) fn mock() -> (Clock, Arc<Mock>) {
78+
let mock = Arc::new(Mock::default());
79+
let clock = Clock(Arc::clone(&mock));
80+
(clock, mock)
81+
}
82+
83+
pub(crate) fn now(&self) -> Instant {
84+
Instant(*self.0.now.read())
85+
}
86+
}
87+
88+
pub(crate) struct Mock {
89+
now: RwLock<StdInstant>,
90+
}
91+
92+
impl Default for Mock {
93+
fn default() -> Self {
94+
Self {
95+
now: RwLock::new(StdInstant::now()),
96+
}
97+
}
98+
}
99+
100+
#[cfg(test)]
101+
impl Mock {
102+
pub(crate) fn increment(&self, amount: Duration) {
103+
*self.now.write() += amount;
104+
}
105+
}

src/common/time_quanta.rs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use std::sync::atomic::{AtomicU64, Ordering};
2+
3+
pub(crate) type Instant = quanta::Instant;
4+
pub(crate) type Clock = quanta::Clock;
5+
6+
#[cfg(test)]
7+
pub(crate) type Mock = quanta::Mock;
8+
9+
pub(crate) struct AtomicInstant {
10+
instant: AtomicU64,
11+
}
12+
13+
impl Default for AtomicInstant {
14+
fn default() -> Self {
15+
Self {
16+
instant: AtomicU64::new(std::u64::MAX),
17+
}
18+
}
19+
}
20+
21+
impl AtomicInstant {
22+
pub(crate) fn reset(&self) {
23+
self.instant.store(std::u64::MAX, Ordering::Release);
24+
}
25+
26+
pub(crate) fn is_set(&self) -> bool {
27+
self.instant.load(Ordering::Acquire) != u64::MAX
28+
}
29+
30+
pub(crate) fn instant(&self) -> Option<Instant> {
31+
let ts = self.instant.load(Ordering::Acquire);
32+
if ts == u64::MAX {
33+
None
34+
} else {
35+
Some(unsafe { std::mem::transmute(ts) })
36+
}
37+
}
38+
39+
pub(crate) fn set_instant(&self, instant: Instant) {
40+
self.instant.store(instant.as_u64(), Ordering::Release);
41+
}
42+
}

src/future/cache.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ where
749749
self.base.reconfigure_for_testing();
750750
}
751751

752-
fn set_expiration_clock(&self, clock: Option<quanta::Clock>) {
752+
fn set_expiration_clock(&self, clock: Option<crate::common::time::Clock>) {
753753
self.base.set_expiration_clock(clock);
754754
}
755755
}
@@ -758,10 +758,9 @@ where
758758
#[cfg(test)]
759759
mod tests {
760760
use super::{Cache, ConcurrentCacheExt};
761-
use crate::future::CacheBuilder;
761+
use crate::{common::time::Clock, future::CacheBuilder};
762762

763763
use async_io::Timer;
764-
use quanta::Clock;
765764
use std::time::Duration;
766765

767766
#[tokio::test]

0 commit comments

Comments
 (0)