-
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.
1. The library code has been stabilized.
2. Added implementations for working with internal synchronized variables. 3. Completely redesigned documentation. 4. Updated readme. 5. Added tests. 6. Added many examples of using the library. 7. In connection with the update of rust, support for `std` has been added to the constant macro (previously only `parking_lot` was supported). *_--
- Loading branch information
1 parent
159fd71
commit cf55a42
Showing
14 changed files
with
1,342 additions
and
75 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,40 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: [ master ] | ||
pull_request: | ||
branches: [ master ] | ||
|
||
env: | ||
CARGO_TERM_COLOR: always | ||
|
||
jobs: | ||
cargo-test: | ||
strategy: | ||
matrix: | ||
os: [ubuntu-latest] | ||
runs-on: ${{ matrix.os }} | ||
|
||
steps: | ||
- uses: actions/cache@v3 | ||
with: | ||
path: | | ||
~/.cargo/bin/ | ||
~/.cargo/registry/index/ | ||
~/.cargo/registry/cache/ | ||
~/.cargo/git/db/ | ||
target/ | ||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | ||
|
||
- uses: actions/checkout@v3 | ||
- name: Run cargo deftest | ||
run: cargo test --verbose | ||
- name: Run cargo test nodeffeatures+lib | ||
run: cargo test --no-default-features --lib --verbose | ||
- name: Run cargo alltest | ||
run: cargo test --all-features --verbose | ||
- name: Std | ||
run: cargo test --no-default-features --features std,get_point_name --lib --verbose | ||
- name: PL | ||
run: cargo test --no-default-features --features parking_lot,get_point_name --lib --verbose |
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 |
---|---|---|
@@ -1,7 +1,32 @@ | ||
[package] | ||
name = "synchronized" | ||
version = "0.1.0" | ||
version = "1.0.0" | ||
authors = ["Denis Kotlyarov (Денис Котляров) <denis2005991@gmail.com>"] | ||
repository = "https://github.com/clucompany/synchronized.git" | ||
edition = "2021" | ||
|
||
license = "Apache-2.0" | ||
readme = "README.md" | ||
|
||
description = "Convenient and simple macro for code synchronization in multithreading." | ||
|
||
# docs.rs-specific configuration | ||
[package.metadata.docs.rs] | ||
# document all features | ||
all-features = true | ||
# defines the configuration attribute `docsrs` | ||
rustdoc-args = ["--cfg", "docsrs"] | ||
|
||
[features] | ||
default = ["std", "get_point_name"] | ||
# Ability to get the name of the synchronization point. | ||
get_point_name = [] | ||
# Use synchronization primitives from `std`. Note that is_lock is not supported in it. | ||
# | ||
# Note that you can just use `parking_lot` instead of `std`. | ||
std = [] | ||
|
||
[dependencies] | ||
parking_lot = "0.12.0" | ||
# Choose between `std` or `parking_lot` as the default synchronization primitive. | ||
parking_lot = {version = "0.12.1", optional = 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 |
---|---|---|
@@ -1 +1,246 @@ | ||
# synchronized | ||
# synchronized | ||
[![CI](https://github.com/clucompany/synchronized/actions/workflows/CI.yml/badge.svg?event=push)](https://github.com/clucompany/synchronized/actions/workflows/CI.yml) | ||
[![Apache licensed](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](./LICENSE) | ||
[![crates.io](https://img.shields.io/crates/v/synchronized)](https://crates.io/crates/synchronized) | ||
[![Documentation](https://docs.rs/synchronized/badge.svg)](https://docs.rs/synchronized) | ||
|
||
Convenient and simple macro for code synchronization in multithreading. | ||
|
||
# Use | ||
|
||
### 1. easy/sync | ||
|
||
```rust | ||
use synchronized::synchronized; | ||
|
||
/* | ||
Quick implementation examples of blocking anonymous code. | ||
*/ | ||
|
||
fn main() { | ||
// #1 Anonymous inter-threaded synchronized code, | ||
// in the case of multi-threading, one thread will wait for the completion of another. | ||
synchronized! { | ||
println!("1"); | ||
} | ||
|
||
// #2 Anonymous inter-threaded synchronized code, | ||
// in the case of multi-threading, one thread will wait for the completion of another. | ||
synchronized!( println!("1"); ); | ||
} | ||
``` | ||
|
||
### 2. sync_static | ||
|
||
```rust | ||
use std::thread::spawn; | ||
use synchronized::synchronized; | ||
|
||
/* | ||
A more illustrative example of code blocking implementation | ||
for SAFE mutability of two or more static variables. | ||
The code creates 5 threads that try to change static variables at the same | ||
time without synchronization, but the code synchronization block | ||
does not allow this code to execute at the same time, which makes | ||
the implementation of changing static variables SAFE and even somewhat | ||
easier and more beneficial in terms of use and performance. | ||
*/ | ||
|
||
fn main() { | ||
// An array of handles to wait for all threads to complete. | ||
let mut join_all = Vec::new(); | ||
|
||
// Creation of 5 threads to implement a multi-threaded environment. | ||
for thread_id in 0..5 { | ||
let join = spawn(move || { | ||
// Print the thread number and print the | ||
// result of the sync_fn function. | ||
println!("#[id: {}] {}", thread_id, sync_fn()); | ||
}); | ||
|
||
join_all.push(join); | ||
} | ||
|
||
// We just wait for all threads to finish and look at stdout. | ||
for tjoin in join_all { | ||
let _e = tjoin.join(); | ||
} | ||
} | ||
|
||
fn sync_fn() -> usize { | ||
// Create anonymous synchronized code. | ||
// | ||
// The code will never run at the same time. If one thread is executing | ||
// this code, the second thread will wait for this code to finish executing. | ||
let result = synchronized! { | ||
static mut POINT0: usize = 0; | ||
static mut POINT1: usize = 0; | ||
|
||
unsafe { | ||
POINT1 = POINT0; | ||
POINT0 += 1; | ||
|
||
POINT1 | ||
} | ||
}; | ||
|
||
result | ||
} | ||
``` | ||
|
||
### 3. sync_let | ||
|
||
```rust | ||
use std::thread::spawn; | ||
use synchronized::synchronized; | ||
|
||
/* | ||
An example that describes how to quickly create an anonymous | ||
sync with a mutable variable. | ||
This code creates 5 threads, each of which tries to update | ||
the `sync_let` variable with data while executing the synchronized anonymous code. | ||
*/ | ||
|
||
fn main() { | ||
// An array of handles to wait for all threads to complete. | ||
let mut join_all = Vec::new(); | ||
|
||
// Creation of 5 threads to implement a multi-threaded environment. | ||
for thread_id in 0..5 { | ||
let join = spawn(move || { | ||
// Create anonymous synchronized code with one mutable variable `sync_let`. | ||
let result = synchronized!((sync_let: String = String::new()) { | ||
// If it's the first thread, | ||
// then theoretically `sync_let` is String::new(). | ||
if thread_id == 0 { | ||
assert_eq!(sync_let.is_empty(), true); | ||
} | ||
|
||
// We fill the variable `sync_let` with data. | ||
sync_let.push_str(&thread_id.to_string()); | ||
sync_let.push_str(" "); | ||
|
||
sync_let.clone() | ||
}); | ||
|
||
// Outputting debug information. | ||
println!("#[id: {}] {}", thread_id, result); | ||
}); | ||
|
||
// In order for our `assert_eq!(sync_let.is_empty());` code to | ||
// always run correctly, the first thread should always run first | ||
// (this is just for the stability of this example). | ||
if thread_id == 0 { | ||
let _e = join.join(); | ||
continue; | ||
} | ||
|
||
join_all.push(join); | ||
} | ||
|
||
// We just wait for all threads to finish and look at stdout. | ||
for tjoin in join_all { | ||
let _e = tjoin.join(); | ||
} | ||
} | ||
``` | ||
|
||
### 4. point | ||
|
||
```rust | ||
use synchronized::synchronized_point; | ||
use synchronized::synchronized; | ||
|
||
/* | ||
An example implementation of synchronized code with | ||
one non-anonymous synchronization point. | ||
This example creates a set of anonymous sync codes associated with a | ||
single named sync point. Each synchronization code executes in the same | ||
way as ordinary anonymous code, but execution occurs simultaneously in a | ||
multi-threaded environment in only one of them. | ||
*/ | ||
|
||
fn main() { | ||
// A sync point named `COMB_SYNC` to group anonymous code syncs by name. | ||
synchronized_point! {(COMB_SYNC) { | ||
static mut POINT: usize = 0; | ||
println!("GeneralSyncPoint, name_point: {}", COMB_SYNC.get_sync_point_name()); | ||
|
||
// #1 Anonymous synchronized code that operates on a | ||
// single named synchronization point. | ||
// | ||
// This code is not executed concurrently in a multi-threaded environment, | ||
// one thread is waiting for someone else's code to execute in this part of the code. | ||
let result0 = synchronized! ((->COMB_SYNC) { | ||
println!("SyncCode, name_point: {}", COMB_SYNC.get_sync_point_name()); | ||
unsafe { | ||
POINT+= 1; | ||
|
||
POINT | ||
} | ||
}); | ||
|
||
// This line of code is not synchronized and can run concurrently on all threads. | ||
println!("Unsynchronized code"); | ||
|
||
// #2 Anonymous synchronized code that operates on a | ||
// single named synchronization point. | ||
// | ||
// Note that `result0` and `result1` cannot be calculated at the same time, | ||
// this does not happen because `result0` or `result1` are calculated in | ||
// synchronized code with a single sync point of the same name. | ||
let result1 = synchronized! ((->COMB_SYNC) { | ||
println!("SyncCode, name_point: {}", COMB_SYNC.get_sync_point_name()); | ||
unsafe { | ||
POINT+= 1; | ||
|
||
POINT | ||
} | ||
}); | ||
|
||
// Display debug information. | ||
println!("result, res0: {:?}, res1: {:?}", result0, result1); | ||
}} | ||
} | ||
``` | ||
|
||
# Connection | ||
|
||
This section only describes how to choose the default synchronization method for a `synchronized` macro. | ||
|
||
### 1. PlugAndPlay (Minimal, Std) | ||
```rust,ignore | ||
[dependencies.synchronized] | ||
version = "1.0.0" | ||
default-features = false | ||
features = [ | ||
"std", | ||
#"get_point_name" | ||
] | ||
``` | ||
|
||
### 2. PlugAndPlay (Minimal, parking_lot) | ||
```rust,ignore | ||
[dependencies.synchronized] | ||
version = "1.0.0" | ||
default-features = false | ||
features = [ | ||
"parking_lot", | ||
#"get_point_name" | ||
] | ||
``` | ||
|
||
# Additionally inf | ||
|
||
1. The macro is an alternative to the `synchronized` keyword from the Java programming language for the Rust programming language with all sorts of extensions. | ||
|
||
2. This macro was created by an author who has not written in Java for a very long time, inspired by the memory of the Java programming language (versions 1.5-1.6). | ||
|
||
# License | ||
|
||
Copyright 2022 #UlinProject Denis Kotlyarov (Денис Котляров) | ||
|
||
Licensed under the Apache License, Version 2.0 |
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,28 @@ | ||
|
||
//! This build.rs doesn't actually do anything other than display a warning if | ||
//! `features` were not installed correctly. | ||
#[cfg( | ||
all(not(test), not(docsrs), not(feature = "doc_cfg"), feature = "parking_lot", feature = "std") | ||
)] | ||
fn main() { | ||
/// CargoWarningPrintln | ||
macro_rules! cwarning { | ||
[ @const: $a: expr ] => { | ||
println!( concat!("cargo:warning=", $a) ); | ||
}; | ||
[ $a: expr] => { | ||
println!( "cargo:warning={}", $a ); | ||
}; | ||
[ $a: expr, $a2: expr] => { | ||
println!( "cargo:warning={}: {}", $a, $a2 ); | ||
}; | ||
} | ||
|
||
cwarning!("synchronized: The choice of synchronization between `std` and `parking_lot` was expected by default for `synchronized` macro. It is not possible to use both synchronization methods at the same time, `std` is now used."); | ||
} | ||
|
||
#[cfg(not( | ||
all(not(test), not(docsrs), not(feature = "doc_cfg"), feature = "parking_lot", feature = "std") | ||
))] | ||
fn main() {} |
Oops, something went wrong.