-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit dc98c47
Showing
15 changed files
with
707 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# EditorConfig is awesome: https://EditorConfig.org | ||
|
||
# top-most EditorConfig file | ||
root = true | ||
|
||
[*] | ||
indent_style = tab | ||
indent_size = 4 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[*.yaml] | ||
indent_style = space | ||
indent_size = 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
pull_request: | ||
|
||
jobs: | ||
test: | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
os: [ ubuntu, windows ] | ||
runs-on: ${{ matrix.os }}-latest | ||
name: Test on ${{ matrix.os }}-latest | ||
timeout-minutes: 15 | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: dtolnay/rust-toolchain@stable | ||
- run: cargo test | ||
|
||
doc: | ||
name: Docs | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 15 | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: dtolnay/rust-toolchain@nightly | ||
- uses: dtolnay/install@cargo-docs-rs | ||
- run: cargo docs-rs | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/.vscode | ||
/Cargo.lock | ||
/target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
name = "rcurs" | ||
version = "0.1.0" | ||
edition = "2021" | ||
description = "An oxidized RCU implementation" | ||
license = "MIT" | ||
repository = "https://github.com/threadexio/rcurs" | ||
readme = "README.md" | ||
keywords = ["rcu", "synchronization", "concurrent", "parallel", "lock-free"] | ||
categories = ["concurrency"] | ||
|
||
[dependencies] | ||
|
||
[features] | ||
default = ["std"] | ||
|
||
std = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2024 1337 | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[`core::hint::spin_loop()`]: https://doc.rust-lang.org/stable/core/hint/fn.spin_loop.html | ||
[`Condvar`]: https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html | ||
|
||
# rcurs | ||
|
||
A simple [RCU](https://en.wikipedia.org/wiki/Read-copy-update) with an oxidized interface. Read more at the [docs](https://docs.rs/rcurs). | ||
|
||
The crate supports running both with or without the `std` library but has a hard dependency on `alloc`. If your environment allows, you should try to keep the `std` feature enabled as that contains typically more efficient implementations of blocking primitives. | ||
|
||
Without the `std` feature, the only way to block is to spin in place using whatever optimization [`core::hint::spin_loop()`] can provide. But with the standard library, blocking is done using [`Condvar`]s. [`Condvar`]s call out to the kernel for blocking. The kernel can then choose what is best, spin itself, or usually give control back to the scheduler to run other processes. | ||
|
||
## Features | ||
|
||
- `std`: Enable use of primitives in the standard library |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
[toolchain] | ||
channel = "stable" | ||
profile = "minimal" | ||
components = ["clippy", "rustfmt"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
max_width = 70 | ||
hard_tabs = true | ||
tab_spaces = 4 | ||
newline_style = "Unix" | ||
use_small_heuristics = "Off" | ||
fn_call_width = 60 | ||
attr_fn_like_width = 60 | ||
struct_lit_width = 60 | ||
struct_variant_width = 60 | ||
array_width = 60 | ||
chain_width = 60 | ||
single_line_if_else_max_width = 0 | ||
reorder_imports = true | ||
reorder_modules = true | ||
remove_nested_parens = true | ||
match_arm_leading_pipes = "Never" | ||
fn_params_layout = "Tall" | ||
match_block_trailing_comma = true | ||
edition = "2021" | ||
merge_derives = true | ||
use_try_shorthand = true | ||
use_field_init_shorthand = true | ||
force_explicit_abi = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
macro_rules! cfg_std { | ||
($($item:item)*) => { | ||
$( | ||
#[cfg(feature = "std")] | ||
$item | ||
)* | ||
}; | ||
} | ||
|
||
pub(crate) use cfg_std; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
//! [`Rcu`] or Read-Copy-Update is a mechanism that allows sharing and updating | ||
//! a piece of data while multiple parallel code blocks have access to it in a | ||
//! lock-free manner. A better explanation is available at the [kernel docs](https://www.kernel.org/doc/html/latest/RCU/whatisRCU.html) | ||
//! or even [Wikipedia](https://en.wikipedia.org/wiki/Read-copy-update). | ||
//! | ||
//! Internally, an RCU is composed of an _atomic_ pointer (the atomic part | ||
//! is important) to some data along with the number of currently active | ||
//! references to it. Something like a garbage-collector (but obviously way | ||
//! simpler). So when you want to get at the actual data, you simply increment | ||
//! the counter of references by one and decrement it when you are done. This | ||
//! is the easy part, now how do we update the value without locking and | ||
//! without messing up anyone already working? Well, each reference does not | ||
//! keep a pointer to the RCU, but instead copies the value of the atomic | ||
//! pointer when it is created. The key is that the pointer to the data is | ||
//! atomic. So we simply create a new copy of data, make our changes to that | ||
//! copy, and then atomically update the pointer. This way we ensure that a | ||
//! reference that is created at the same time as we update it uses either: | ||
//! the old value or the new value. In any case, it does not end up with an | ||
//! invalid pointer. Because we are a model programmer, we must also not | ||
//! forget to clean up the old data which is effectively outdated and useless. | ||
//! But we can't just clean up the old data: there might be references | ||
//! to it from before we did the update. Ah, so we will wait for the code | ||
//! with old references to drop them, and because we swapped the pointer before, | ||
//! no new references can get access to the old data. After waiting and ensuring | ||
//! there are no remaining references to the old data, we can now safely free | ||
//! it without worry. | ||
//! | ||
//! # Example | ||
//! | ||
//! ```rust,no_run | ||
//! use std::thread; | ||
//! use std::time::Duration; | ||
//! | ||
//! type Rcu<T> = rcurs::Rcu<T, rcurs::Spin>; | ||
//! | ||
//! #[derive(Debug, Clone, PartialEq, Eq)] | ||
//! struct User { | ||
//! uid: i32, | ||
//! gid: i32, | ||
//! } | ||
//! | ||
//! fn setugid(user: &Rcu<User>, uid: i32, gid: i32) { | ||
//! let mut new = user.get().clone(); | ||
//! | ||
//! if new.uid == uid && new.gid == gid { | ||
//! return; | ||
//! } | ||
//! | ||
//! new.uid = uid; | ||
//! new.gid = gid; | ||
//! | ||
//! user.update(new); | ||
//! } | ||
//! | ||
//! // Basically a `sleep`` function that holds onto `user` and prints it after | ||
//! // `sec` seconds have passed. | ||
//! fn compute(user: &Rcu<User>, id: &str, sec: u64) { | ||
//! let user = user.get(); | ||
//! thread::sleep(Duration::from_secs(sec)); | ||
//! println!("compute[{id}]: finish work for {}:{}", user.uid, user.gid); | ||
//! } | ||
//! | ||
//! fn thread_1(user: &Rcu<User>) { | ||
//! compute(user, "1", 3); | ||
//! | ||
//! // This call will update `user`. | ||
//! setugid(user, 1000, 1000); | ||
//! | ||
//! compute(user, "3", 4); | ||
//! } | ||
//! | ||
//! fn thread_2(user: &Rcu<User>) { | ||
//! // The following compute call will always see the `User { uid: 0, gid: 0}` | ||
//! // as the `setugid` call happens 3 seconds after it has started executing. | ||
//! compute(user, "2", 5); | ||
//! } | ||
//! | ||
//! fn main() { | ||
//! let user = Rcu::new(User { uid: 0, gid: 0 }); | ||
//! | ||
//! thread::scope(|scope| { | ||
//! scope.spawn(|| thread_1(&user)); | ||
//! scope.spawn(|| thread_2(&user)); | ||
//! }); | ||
//! } | ||
//! ``` | ||
#![deny(missing_docs)] | ||
#![warn( | ||
clippy::all, | ||
clippy::correctness, | ||
clippy::pedantic, | ||
clippy::cargo, | ||
clippy::nursery, | ||
clippy::perf, | ||
clippy::style | ||
)] | ||
#![allow( | ||
clippy::missing_panics_doc, | ||
clippy::significant_drop_tightening, | ||
clippy::needless_lifetimes | ||
)] | ||
#![cfg_attr(not(feature = "std"), no_std)] | ||
|
||
mod cfg; | ||
|
||
mod notify; | ||
mod rcu; | ||
|
||
#[doc(inline)] | ||
pub use self::notify::*; | ||
|
||
#[doc(inline)] | ||
pub use self::rcu::{Guard, Rcu}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
use super::Notify; | ||
|
||
use std::sync::{Condvar, Mutex}; | ||
|
||
/// A [`Notify`] backend that uses a [`Condvar`] to achieve true blocking. | ||
pub struct Blocking { | ||
/* Keep track of both whether notify was called and how many waiter there are. | ||
* This way, the waiter who wakes up last can know to reset the `notified` flag | ||
* again to prepare for the next `notify`. | ||
*/ | ||
lock: Mutex<(bool, u8)>, | ||
var: Condvar, | ||
} | ||
|
||
impl Notify for Blocking { | ||
fn new() -> Self { | ||
Self { | ||
lock: Mutex::new((false, 0)), | ||
var: Condvar::new(), | ||
} | ||
} | ||
|
||
fn wait(&self) { | ||
let mut guard = self.lock.lock().unwrap(); | ||
guard.1 += 1; | ||
|
||
let mut guard = self | ||
.var | ||
.wait_while(guard, |(notified, _)| !*notified) | ||
.unwrap(); | ||
guard.1 -= 1; | ||
|
||
if guard.1 == 0 { | ||
guard.0 = false; | ||
} | ||
} | ||
|
||
fn notify(&self) { | ||
let mut guard = self.lock.lock().unwrap(); | ||
guard.0 = true; | ||
self.var.notify_all(); | ||
} | ||
} | ||
|
||
impl Default for Blocking { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} |
Oops, something went wrong.