diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff5bd7d21..3abbfefde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: pull_request: push: - branches: + branches: - master env: @@ -45,7 +45,11 @@ jobs: run: cargo fmt --all -- --check - name: check clippy if: ${{ matrix.rust == 'stable' && matrix.os == 'ubuntu-latest' }} - run: cargo clippy --all --all-targets -- -D clippy::all && cargo clippy --no-default-features --features prost-codec -- -D clippy::all + run: cargo clippy --all --all-targets -- -D clippy::all && cargo clippy --no-default-features --features prost-codec --features std -- -D clippy::all + - name: run tests with no-std enabled + # No-std implementation uses feature that can only be compiled by nightly version + if: ${{ matrix.rust == 'nightly' }} + run: cargo test --all --no-default-features --features=protobuf-codec --features=default-logger -- --nocapture - run: cargo test --all -- --nocapture # Validate benches still work. - run: cargo bench --all -- --test @@ -53,4 +57,4 @@ jobs: # Only package harness has failpoints tests. - run: cargo test --tests --features failpoints --package harness -- --nocapture # TODO: There is a bug in protobuf-build that cached size is not supported yet, so do not test harness. - - run: cargo test --no-default-features --features prost-codec -- --nocapture + - run: cargo test --no-default-features --features prost-codec --features std -- --nocapture diff --git a/Cargo.toml b/Cargo.toml index 2bcf72720..018b35723 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,14 +9,15 @@ readme = "README.md" homepage = "https://github.com/tikv/raft-rs" documentation = "https://docs.rs/raft" description = "The rust language implementation of Raft algorithm." -categories = ["algorithms", "database-implementations"] +categories = ["algorithms", "database-implementations", "no-std"] edition = "2021" [workspace] members = ["proto", "harness", "datadriven"] [features] -default = ["protobuf-codec", "default-logger"] +default = ["protobuf-codec", "default-logger", "std"] +std = [] # Enable failpoints failpoints = ["fail/failpoints"] protobuf-codec = ["raft-proto/protobuf-codec", "bytes"] @@ -37,6 +38,8 @@ slog = "2.2" slog-envlogger = { version = "2.1.0", optional = true } slog-stdlog = { version = "4", optional = true } slog-term = { version = "2.4.0", optional = true } +spin = { version = "0.9.8"} +hashbrown = { version = "0.14.0"} [dev-dependencies] criterion = "0.3" diff --git a/harness/Cargo.toml b/harness/Cargo.toml index 5010d761a..1852b91f3 100644 --- a/harness/Cargo.toml +++ b/harness/Cargo.toml @@ -12,7 +12,7 @@ categories = [] edition = "2018" [features] -default = ["protobuf-codec", "raft/default-logger"] +default = ["protobuf-codec", "raft/default-logger", "raft/std"] # Enable failpoints failpoints = ["fail/failpoints"] protobuf-codec = ["raft/protobuf-codec"] diff --git a/proto/src/lib.rs b/proto/src/lib.rs index 56ed74a73..3e29dbdeb 100644 --- a/proto/src/lib.rs +++ b/proto/src/lib.rs @@ -48,8 +48,8 @@ pub mod util { { fn from((voters, learners): (Iter1, Iter2)) -> Self { let mut conf_state = ConfState::default(); - conf_state.mut_voters().extend(voters.into_iter()); - conf_state.mut_learners().extend(learners.into_iter()); + conf_state.mut_voters().extend(voters); + conf_state.mut_learners().extend(learners); conf_state } } diff --git a/src/confchange/changer.rs b/src/confchange/changer.rs index 32e231708..cca676481 100644 --- a/src/confchange/changer.rs +++ b/src/confchange/changer.rs @@ -1,5 +1,10 @@ // Copyright 2020 TiKV Project Authors. Licensed under Apache-2.0. +use alloc::borrow::ToOwned; +use alloc::format; +use alloc::vec; +use alloc::vec::Vec; + use crate::eraftpb::{ConfChangeSingle, ConfChangeType}; use crate::tracker::{Configuration, ProgressMap, ProgressTracker}; use crate::{Error, Result}; diff --git a/src/confchange/datadriven_test.rs b/src/confchange/datadriven_test.rs index 96b9dcc70..aa9b2c97a 100644 --- a/src/confchange/datadriven_test.rs +++ b/src/confchange/datadriven_test.rs @@ -1,4 +1,6 @@ -use std::fmt::Write; +use alloc::string::String; +use alloc::string::ToString; +use core::fmt::Write; use crate::{default_logger, Changer, ProgressTracker}; use datadriven::{run_test, walk}; diff --git a/src/confchange/restore.rs b/src/confchange/restore.rs index 7dbb7f5df..18dd32eab 100644 --- a/src/confchange/restore.rs +++ b/src/confchange/restore.rs @@ -3,6 +3,8 @@ // TODO: remove following line #![allow(dead_code)] +use alloc::vec::Vec; + use super::changer::Changer; use crate::eraftpb::{ConfChangeSingle, ConfChangeType, ConfState}; use crate::tracker::ProgressTracker; diff --git a/src/config.rs b/src/config.rs index 4d6aa73d2..e258ba326 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,6 +14,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use alloc::borrow::ToOwned; +use alloc::format; + pub use super::read_only::{ReadOnlyOption, ReadState}; use super::util::NO_LIMIT; use super::{ diff --git a/src/errors.rs b/src/errors.rs index 599da8c19..907b7a71b 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,32 +1,29 @@ // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. -use thiserror::Error; + +use crate::StdError; +use alloc::boxed::Box; +use alloc::string::String; +use core::fmt; /// The base error type for raft -#[derive(Debug, Error)] +#[derive(Debug)] pub enum Error { /// An IO error occurred - #[error("{0}")] - Io(#[from] std::io::Error), + #[cfg(feature = "std")] + Io(std::io::Error), /// A storage error occurred. - #[error("{0}")] - Store(#[from] StorageError), + Store(StorageError), /// Raft cannot step the local message. - #[error("raft: cannot step raft local message")] StepLocalMsg, /// The raft peer is not found and thus cannot step. - #[error("raft: cannot step as peer not found")] StepPeerNotFound, /// The proposal of changes was dropped. - #[error("raft: proposal dropped")] ProposalDropped, /// The configuration is invalid. - #[error("{0}")] ConfigInvalid(String), /// A protobuf message codec failed in some manner. - #[error("protobuf codec error {0:?}")] - CodecError(#[from] protobuf::ProtobufError), + CodecError(protobuf::ProtobufError), /// The node exists, but should not. - #[error("The node {id} already exists in the {set} set.")] Exists { /// The node id. id: u64, @@ -34,7 +31,6 @@ pub enum Error { set: &'static str, }, /// The node does not exist, but should. - #[error("The node {id} is not in the {set} set.")] NotExists { /// The node id. id: u64, @@ -42,13 +38,65 @@ pub enum Error { set: &'static str, }, /// ConfChange proposal is invalid. - #[error("{0}")] ConfChangeError(String), /// The request snapshot is dropped. - #[error("raft: request snapshot dropped")] RequestSnapshotDropped, } +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + #[cfg(feature = "std")] + Error::Io(ref e) => write!(f, "{}", e), + Error::Store(ref e) => write!(f, "{}", e), + Error::StepLocalMsg => write!(f, "raft: cannot step raft local message"), + Error::StepPeerNotFound => write!(f, "raft: cannot step as peer not found"), + Error::ProposalDropped => write!(f, "raft: proposal dropped"), + Error::ConfigInvalid(ref m) => write!(f, "{}", m), + Error::CodecError(ref e) => write!(f, "protobuf codec error {0:?}", e), + Error::Exists { id, set } => { + write!(f, "The node {id} already exists in the {set} set.") + } + Error::NotExists { id, set } => { + write!(f, "The node {id} is not in the {set} set.") + } + Error::ConfChangeError(ref m) => write!(f, "{}", m), + Error::RequestSnapshotDropped => write!(f, "raft: request snapshot dropped"), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match *self { + #[cfg(feature = "std")] + Error::Io(ref e) => Some(e), + Error::Store(ref e) => Some(e), + Error::CodecError(ref e) => Some(e), + _ => None, + } + } +} + +#[cfg(feature = "std")] +impl From for Error { + fn from(err: std::io::Error) -> Error { + Error::Io(err) + } +} + +impl From for Error { + fn from(err: StorageError) -> Error { + Error::Store(err) + } +} + +impl From for Error { + fn from(err: protobuf::ProtobufError) -> Error { + Error::CodecError(err) + } +} + impl PartialEq for Error { #[cfg_attr(feature = "cargo-clippy", allow(clippy::match_same_arms))] fn eq(&self, other: &Error) -> bool { @@ -56,6 +104,7 @@ impl PartialEq for Error { (Error::StepPeerNotFound, Error::StepPeerNotFound) => true, (Error::ProposalDropped, Error::ProposalDropped) => true, (Error::Store(ref e1), Error::Store(ref e2)) => e1 == e2, + #[cfg(feature = "std")] (Error::Io(ref e1), Error::Io(ref e2)) => e1.kind() == e2.kind(), (Error::StepLocalMsg, Error::StepLocalMsg) => true, (Error::ConfigInvalid(ref e1), Error::ConfigInvalid(ref e2)) => e1 == e2, @@ -67,26 +116,41 @@ impl PartialEq for Error { } /// An error with the storage. -#[derive(Debug, Error)] +#[derive(Debug)] pub enum StorageError { /// The storage was compacted and not accessible - #[error("log compacted")] Compacted, /// The log is not available. - #[error("log unavailable")] Unavailable, /// The log is being fetched. - #[error("log is temporarily unavailable")] LogTemporarilyUnavailable, /// The snapshot is out of date. - #[error("snapshot out of date")] SnapshotOutOfDate, /// The snapshot is being created. - #[error("snapshot is temporarily unavailable")] SnapshotTemporarilyUnavailable, /// Some other error occurred. - #[error("unknown error {0}")] - Other(#[from] Box), + Other(Box), +} + +impl fmt::Display for StorageError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + StorageError::Compacted => write!(f, "log compacted"), + StorageError::Unavailable => write!(f, "log unavailable"), + StorageError::LogTemporarilyUnavailable => write!(f, "log is temporarily unavailable"), + StorageError::SnapshotOutOfDate => write!(f, "snapshot out of date"), + StorageError::SnapshotTemporarilyUnavailable => { + write!(f, "snapshot is temporarily unavailable") + } + StorageError::Other(ref e) => write!(f, "unknown error {}", e), + } + } +} + +impl StdError for StorageError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + None + } } impl PartialEq for StorageError { @@ -113,13 +177,12 @@ impl PartialEq for StorageError { } /// A result type that wraps up the raft errors. -pub type Result = std::result::Result; +pub type Result = core::result::Result; #[allow(clippy::eq_op)] #[cfg(test)] mod tests { use super::*; - use std::io; #[test] fn test_error_equal() { @@ -128,14 +191,6 @@ mod tests { Error::Store(StorageError::Compacted), Error::Store(StorageError::Compacted) ); - assert_eq!( - Error::Io(io::Error::new(io::ErrorKind::UnexpectedEof, "oh no!")), - Error::Io(io::Error::new(io::ErrorKind::UnexpectedEof, "oh yes!")) - ); - assert_ne!( - Error::Io(io::Error::new(io::ErrorKind::NotFound, "error")), - Error::Io(io::Error::new(io::ErrorKind::BrokenPipe, "error")) - ); assert_eq!(Error::StepLocalMsg, Error::StepLocalMsg); assert_eq!( Error::ConfigInvalid(String::from("config error")), @@ -145,16 +200,31 @@ mod tests { Error::ConfigInvalid(String::from("config error")), Error::ConfigInvalid(String::from("other error")) ); - assert_eq!( - Error::from(io::Error::new(io::ErrorKind::Other, "oh no!")), - Error::from(io::Error::new(io::ErrorKind::Other, "oh yes!")) - ); assert_ne!( Error::StepPeerNotFound, Error::Store(StorageError::Compacted) ); } + #[cfg(feature = "std")] + #[test] + fn test_error_equal_std() { + use std::io; + + assert_eq!( + Error::Io(io::Error::new(io::ErrorKind::UnexpectedEof, "oh no!")), + Error::Io(io::Error::new(io::ErrorKind::UnexpectedEof, "oh yes!")) + ); + assert_ne!( + Error::Io(io::Error::new(io::ErrorKind::NotFound, "error")), + Error::Io(io::Error::new(io::ErrorKind::BrokenPipe, "error")) + ); + assert_eq!( + Error::from(io::Error::new(io::ErrorKind::Other, "oh no!")), + Error::from(io::Error::new(io::ErrorKind::Other, "oh yes!")) + ); + } + #[test] fn test_storage_error_equal() { assert_eq!(StorageError::Compacted, StorageError::Compacted); diff --git a/src/lib.rs b/src/lib.rs index f04850429..303a0d8cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -486,6 +486,8 @@ before taking old, removed peers offline. // We use `default` method a lot to be support prost and rust-protobuf at the // same time. And reassignment can be optimized by compiler. #![allow(clippy::field_reassign_with_default)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(error_in_core))] macro_rules! fatal { ($logger:expr, $msg:expr) => {{ @@ -572,7 +574,7 @@ pub mod prelude { /// The default logger we fall back to when passed `None` in external facing constructors. /// /// Currently, this is a `log` adaptor behind a `Once` to ensure there is no clobbering. -#[cfg(any(test, feature = "default-logger"))] +#[cfg(all(feature = "std", any(test, feature = "default-logger")))] pub fn default_logger() -> slog::Logger { use slog::{o, Drain}; use std::sync::{Mutex, Once}; @@ -599,6 +601,35 @@ pub fn default_logger() -> slog::Logger { } } -type DefaultHashBuilder = std::hash::BuildHasherDefault; +/// The default logger we fall back to when passed `None` in external facing constructors. +#[cfg(all(not(feature = "std"), any(test, feature = "default-logger")))] +pub fn default_logger() -> slog::Logger { + use slog::o; + + slog::Logger::root(slog::Discard, o!()) +} + +extern crate alloc; + +type DefaultHashBuilder = core::hash::BuildHasherDefault; + +#[cfg(feature = "std")] +use { + std::error::Error as StdError, std::sync::RwLock, std::sync::RwLockReadGuard, + std::sync::RwLockWriteGuard, +}; +#[cfg(feature = "std")] type HashMap = std::collections::HashMap; +#[cfg(feature = "std")] type HashSet = std::collections::HashSet; +#[cfg(feature = "std")] +type HashSetIter<'a, K> = std::collections::hash_set::Iter<'a, K>; + +#[cfg(not(feature = "std"))] +use {core::error::Error as StdError, spin::RwLock, spin::RwLockReadGuard, spin::RwLockWriteGuard}; +#[cfg(not(feature = "std"))] +type HashMap = hashbrown::HashMap; +#[cfg(not(feature = "std"))] +type HashSet = hashbrown::HashSet; +#[cfg(not(feature = "std"))] +type HashSetIter<'a, K> = hashbrown::hash_set::Iter<'a, K>; diff --git a/src/log_unstable.rs b/src/log_unstable.rs index e27b9cf68..399518165 100644 --- a/src/log_unstable.rs +++ b/src/log_unstable.rs @@ -16,6 +16,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use alloc::vec; +use alloc::vec::Vec; + use crate::eraftpb::{Entry, Snapshot}; use crate::util::entry_approximate_size; use slog::Logger; @@ -215,6 +218,7 @@ mod test { use crate::eraftpb::{Entry, Snapshot, SnapshotMetadata}; use crate::log_unstable::Unstable; use crate::util::entry_approximate_size; + use alloc::vec; fn new_entry(index: u64, term: u64) -> Entry { let mut e = Entry::default(); diff --git a/src/quorum.rs b/src/quorum.rs index 24c6d6a82..1a9999f2f 100644 --- a/src/quorum.rs +++ b/src/quorum.rs @@ -4,8 +4,8 @@ pub mod datadriven_test; pub mod joint; pub mod majority; -use std::collections::HashMap; -use std::fmt::{self, Debug, Display, Formatter}; +use crate::HashMap; +use core::fmt::{self, Debug, Display, Formatter}; /// VoteResult indicates the outcome of a vote. #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/src/quorum/datadriven_test.rs b/src/quorum/datadriven_test.rs index df18452f4..e3ddd0421 100644 --- a/src/quorum/datadriven_test.rs +++ b/src/quorum/datadriven_test.rs @@ -1,7 +1,11 @@ +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; + use crate::quorum::{AckIndexer, AckedIndexer, Index}; use crate::{default_logger, HashMap, HashSet, JointConfig, MajorityConfig}; +use core::fmt::Write; use datadriven::{run_test, TestData}; -use std::fmt::Write; fn test_quorum(data: &TestData) -> String { // Two majority configs. The first one is always used (though it may diff --git a/src/quorum/joint.rs b/src/quorum/joint.rs index 679509f02..eb3312003 100644 --- a/src/quorum/joint.rs +++ b/src/quorum/joint.rs @@ -4,7 +4,7 @@ use super::{AckedIndexer, VoteResult}; use crate::util::Union; use crate::HashSet; use crate::MajorityConfig; -use std::cmp; +use core::cmp; /// A configuration of two groups of (possibly overlapping) majority configurations. /// Decisions require the support of both majorities. @@ -92,7 +92,7 @@ impl Configuration { /// Describe returns a (multi-line) representation of the commit indexes for the /// given lookuper. #[cfg(test)] - pub(crate) fn describe(&self, l: &impl AckedIndexer) -> String { + pub(crate) fn describe(&self, l: &impl AckedIndexer) -> alloc::string::String { MajorityConfig::new(self.ids().iter().collect()).describe(l) } } diff --git a/src/quorum/majority.rs b/src/quorum/majority.rs index 5fcd103e9..d4a2a27b0 100644 --- a/src/quorum/majority.rs +++ b/src/quorum/majority.rs @@ -1,13 +1,16 @@ // Copyright 2020 TiKV Project Authors. Licensed under Apache-2.0. +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec::Vec; + use super::{AckedIndexer, Index, VoteResult}; -use crate::{DefaultHashBuilder, HashSet}; +use crate::{DefaultHashBuilder, HashSet, HashSetIter}; -use std::collections::hash_set::Iter; -use std::fmt::Formatter; -use std::mem::MaybeUninit; -use std::ops::{Deref, DerefMut}; -use std::{cmp, slice, u64}; +use core::fmt::Formatter; +use core::mem::MaybeUninit; +use core::ops::{Deref, DerefMut}; +use core::{cmp, slice, u64}; /// A set of IDs that uses majority quorums to make decisions. #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -15,8 +18,8 @@ pub struct Configuration { voters: HashSet, } -impl std::fmt::Display for Configuration { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for Configuration { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!( f, "({})", @@ -43,7 +46,7 @@ impl Configuration { } /// Returns an iterator over voters. - pub fn ids(&self) -> Iter<'_, u64> { + pub fn ids(&self) -> HashSetIter<'_, u64> { self.voters.iter() } @@ -169,7 +172,9 @@ impl Configuration { /// ``` #[cfg(test)] pub(crate) fn describe(&self, l: &impl AckedIndexer) -> String { - use std::fmt::Write; + #[cfg(not(feature = "std"))] + use alloc::format; + use core::fmt::Write; let n = self.voters.len(); if n == 0 { diff --git a/src/raft.rs b/src/raft.rs index 5af1eca47..1b836a128 100644 --- a/src/raft.rs +++ b/src/raft.rs @@ -14,9 +14,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::cmp; -use std::convert::TryFrom; -use std::ops::{Deref, DerefMut}; +use alloc::vec::Vec; +use core::cmp; +use core::convert::TryFrom; +use core::ops::{Deref, DerefMut}; use crate::eraftpb::{ ConfChange, ConfChangeV2, ConfState, Entry, EntryType, HardState, Message, MessageType, @@ -874,7 +875,7 @@ impl Raft { pub fn inflight_buffers_size(&self) -> usize { let mut total_size = 0; for (_, pr) in self.prs().iter() { - total_size += pr.ins.buffer_capacity() * std::mem::size_of::(); + total_size += pr.ins.buffer_capacity() * core::mem::size_of::(); } total_size } diff --git a/src/raft_log.rs b/src/raft_log.rs index fcfcf32e7..f975c8fa2 100644 --- a/src/raft_log.rs +++ b/src/raft_log.rs @@ -14,7 +14,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::cmp; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; + +use alloc::format; +use core::cmp; use slog::warn; use slog::Logger; @@ -662,17 +668,12 @@ impl RaftLog { #[cfg(test)] mod test { - use std::{ - cmp, - panic::{self, AssertUnwindSafe}, - }; + use alloc::vec; use crate::default_logger; use crate::eraftpb; - use crate::errors::{Error, StorageError}; - use crate::raft_log::{self, RaftLog}; + use crate::raft_log::RaftLog; use crate::storage::{GetEntriesContext, MemStorage}; - use protobuf::Message as PbMessage; fn new_entry(index: u64, term: u64) -> eraftpb::Entry { let mut e = eraftpb::Entry::default(); @@ -1151,8 +1152,14 @@ mod test { } } + #[cfg(feature = "std")] #[test] fn test_slice() { + use crate::errors::{Error, StorageError}; + use crate::raft_log; + use protobuf::Message as PbMessage; + use std::panic::{self, AssertUnwindSafe}; + let (offset, num) = (100u64, 100u64); let (last, half) = (offset + num, offset + num / 2); let halfe = new_entry(half, half); @@ -1291,8 +1298,12 @@ mod test { /// 2. Append any new entries not already in the log /// If the given (index, term) does not match with the existing log: /// return false + #[cfg(feature = "std")] #[test] fn test_log_maybe_append() { + use core::cmp; + use std::panic::{self, AssertUnwindSafe}; + let l = default_logger(); let previous_ents = vec![new_entry(1, 1), new_entry(2, 2), new_entry(3, 3)]; let (last_index, last_term, commit, persist) = (3u64, 3u64, 1u64, 3u64); @@ -1512,8 +1523,11 @@ mod test { } } + #[cfg(feature = "std")] #[test] fn test_commit_to() { + use std::panic::{self, AssertUnwindSafe}; + let l = default_logger(); let previous_ents = vec![new_entry(1, 1), new_entry(2, 2), new_entry(3, 3)]; let previous_commit = 2u64; @@ -1540,8 +1554,11 @@ mod test { } // TestCompaction ensures that the number of log entries is correct after compactions. + #[cfg(feature = "std")] #[test] fn test_compaction() { + use std::panic::{self, AssertUnwindSafe}; + let l = default_logger(); let tests = vec![ // out of upper bound @@ -1583,8 +1600,12 @@ mod test { } } + #[cfg(feature = "std")] #[test] fn test_is_outofbounds() { + use crate::errors::{Error, StorageError}; + use std::panic::{self, AssertUnwindSafe}; + let (offset, num) = (100u64, 100u64); let store = MemStorage::new(); store @@ -1637,8 +1658,11 @@ mod test { } } + #[cfg(feature = "std")] #[test] fn test_restore_snap() { + use std::panic::{self, AssertUnwindSafe}; + let store = MemStorage::new(); store.wl().apply_snapshot(new_snapshot(100, 1)).expect(""); let mut raft_log = RaftLog::new(store, default_logger()); diff --git a/src/raw_node.rs b/src/raw_node.rs index f88c3b3bf..01471cfda 100644 --- a/src/raw_node.rs +++ b/src/raw_node.rs @@ -20,7 +20,10 @@ //! nodes but not the raft consensus itself. Generally, you'll interact with the //! RawNode first and use it to access the inner workings of the consensus protocol. -use std::{collections::VecDeque, mem}; +use alloc::collections::VecDeque; +use alloc::vec; +use alloc::vec::Vec; +use core::mem; use protobuf::Message as PbMessage; use raft_proto::ConfChangeI; @@ -798,6 +801,7 @@ impl RawNode { #[cfg(test)] mod test { use crate::eraftpb::MessageType; + use alloc::vec; use super::is_local_msg; diff --git a/src/read_only.rs b/src/read_only.rs index 6ae37282a..597f9f98a 100644 --- a/src/read_only.rs +++ b/src/read_only.rs @@ -14,7 +14,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::VecDeque; +use alloc::collections::VecDeque; +use alloc::vec; +use alloc::vec::Vec; use slog::Logger; diff --git a/src/storage.rs b/src/storage.rs index ae58f566e..f2c015ac7 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -20,8 +20,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::cmp; -use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use crate::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::cmp; use crate::eraftpb::*; @@ -120,7 +122,7 @@ pub trait Storage { /// Storage should check context.can_async() first and decide whether to fetch entries asynchronously /// based on its own implementation. If the entries are fetched asynchronously, storage should return /// LogTemporarilyUnavailable, and application needs to call `on_entries_fetched(context)` to trigger - /// re-fetch of the entries after the storage finishes fetching the entries. + /// re-fetch of the entries after the storage finishes fetching the entries. /// /// # Panics /// @@ -423,15 +425,31 @@ impl MemStorage { /// Opens up a read lock on the storage and returns a guard handle. Use this /// with functions that don't require mutation. + #[cfg(feature = "std")] pub fn rl(&self) -> RwLockReadGuard<'_, MemStorageCore> { self.core.read().unwrap() } /// Opens up a write lock on the storage and returns guard handle. Use this /// with functions that take a mutable reference to self. + #[cfg(feature = "std")] pub fn wl(&self) -> RwLockWriteGuard<'_, MemStorageCore> { self.core.write().unwrap() } + + /// Opens up a read lock on the storage and returns a guard handle. Use this + /// with functions that don't require mutation. + #[cfg(not(feature = "std"))] + pub fn rl(&self) -> RwLockReadGuard<'_, MemStorageCore> { + self.core.read() + } + + /// Opens up a write lock on the storage and returns guard handle. Use this + /// with functions that take a mutable reference to self. + #[cfg(not(feature = "std"))] + pub fn wl(&self) -> RwLockWriteGuard<'_, MemStorageCore> { + self.core.write() + } } impl Storage for MemStorage { @@ -521,7 +539,8 @@ impl Storage for MemStorage { #[cfg(test)] mod test { - use std::panic::{self, AssertUnwindSafe}; + use alloc::vec; + use alloc::vec::Vec; use protobuf::Message as PbMessage; @@ -734,8 +753,11 @@ mod test { } } + #[cfg(feature = "std")] #[test] fn test_storage_append() { + use std::panic::{self, AssertUnwindSafe}; + let ents = vec![new_entry(3, 3), new_entry(4, 4), new_entry(5, 5)]; let mut tests = vec![ ( diff --git a/src/tracker.rs b/src/tracker.rs index 4814d3817..25a312148 100644 --- a/src/tracker.rs +++ b/src/tracker.rs @@ -26,8 +26,8 @@ use crate::confchange::{MapChange, MapChangeType}; use crate::eraftpb::ConfState; use crate::quorum::{AckedIndexer, Index, VoteResult}; use crate::{DefaultHashBuilder, HashMap, HashSet, JointConfig}; +use core::fmt::Debug; use getset::Getters; -use std::fmt::Debug; /// Config reflects the configuration tracked in a ProgressTracker. #[derive(Clone, Debug, Default, PartialEq, Eq, Getters)] @@ -90,8 +90,14 @@ pub struct Configuration { // Display and crate::itertools used only for test #[cfg(test)] -impl std::fmt::Display for Configuration { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::fmt::Display for Configuration { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(not(feature = "std"))] + use alloc::string::String; + #[cfg(not(feature = "std"))] + use alloc::string::ToString; + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; use itertools::Itertools; if self.voters.outgoing.is_empty() { write!(f, "voters={}", self.voters.incoming)? diff --git a/src/tracker/inflights.rs b/src/tracker/inflights.rs index 7b99e1754..e7634d4bb 100644 --- a/src/tracker/inflights.rs +++ b/src/tracker/inflights.rs @@ -14,7 +14,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::cmp::Ordering; +use alloc::vec; +use alloc::vec::Vec; + +use core::cmp::Ordering; /// A buffer of inflight messages. #[derive(Debug, PartialEq, Eq, Clone)] @@ -204,6 +207,8 @@ impl Inflights { #[cfg(test)] mod tests { use super::Inflights; + use alloc::vec; + use alloc::vec::Vec; #[test] fn test_inflight_add() { diff --git a/src/tracker/progress.rs b/src/tracker/progress.rs index b154c5773..a02f4858a 100644 --- a/src/tracker/progress.rs +++ b/src/tracker/progress.rs @@ -1,7 +1,7 @@ // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. use crate::{Inflights, ProgressState, INVALID_INDEX}; -use std::cmp; +use core::cmp; /// The progress of catching up from a restart. #[derive(Debug, Clone, PartialEq, Eq)] @@ -244,6 +244,7 @@ impl Progress { #[cfg(test)] mod tests { use super::*; + use alloc::vec; fn new_progress( state: ProgressState, diff --git a/src/tracker/state.rs b/src/tracker/state.rs index 5c6b4d406..e063be065 100644 --- a/src/tracker/state.rs +++ b/src/tracker/state.rs @@ -14,8 +14,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::fmt; -use std::fmt::{Display, Formatter}; +use core::fmt; +use core::fmt::{Display, Formatter}; /// The state of the progress. #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] diff --git a/src/util.rs b/src/util.rs index 9ad603d2f..2591e8f26 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,9 +3,12 @@ // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. -use std::fmt; -use std::fmt::Write; -use std::u64; +use alloc::borrow::ToOwned; +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt; +use core::fmt::Write; +use core::u64; use slog::{OwnedKVList, Record, KV};