synchronized/lib.rs
1//Copyright 2022 #UlinProject Denis Kotlyarov (Денис Котляров)
2
3//Licensed under the Apache License, Version 2.0 (the "License");
4//you may not use this file except in compliance with the License.
5//You may obtain a copy of the License at
6
7// http://www.apache.org/licenses/LICENSE-2.0
8
9//Unless required by applicable law or agreed to in writing, software
10//distributed under the License is distributed on an "AS IS" BASIS,
11//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//See the License for the specific language governing permissions and
13// limitations under the License.
14
15// #Ulin Project 2022
16//
17
18/*!
19
20Convenient and simple macro for code synchronization in multithreading.
21
22# Use
23
24### 1. easy/sync
25
26```rust
27use synchronized::synchronized;
28
29/*
30 Quick implementation examples of blocking anonymous code.
31*/
32
33fn main() {
34 // #1 Anonymous inter-threaded synchronized code,
35 // in the case of multi-threading, one thread will wait for the completion of another.
36 synchronized! {
37 println!("1");
38 }
39
40 // #2 Anonymous inter-threaded synchronized code,
41 // in the case of multi-threading, one thread will wait for the completion of another.
42 synchronized!( println!("1"); );
43}
44```
45
46### 2. sync_static
47
48```rust
49use std::thread::spawn;
50use synchronized::synchronized;
51
52/*
53 A more illustrative example of code blocking implementation
54 for SAFE mutability of two or more static variables.
55
56 The code creates 5 threads that try to change static variables at the same
57 time without synchronization, but the code synchronization block
58 does not allow this code to execute at the same time, which makes
59 the implementation of changing static variables SAFE and even somewhat
60 easier and more beneficial in terms of use and performance.
61*/
62
63fn main() {
64 // An array of handles to wait for all threads to complete.
65 let mut join_all = Vec::new();
66
67 // Creation of 5 threads to implement a multi-threaded environment.
68 for thread_id in 0..5 {
69 let join = spawn(move || {
70 // Print the thread number and print the
71 // result of the sync_fn function.
72 println!("#[id: {}] {}", thread_id, sync_fn());
73 });
74
75 join_all.push(join);
76 }
77
78 // We just wait for all threads to finish and look at stdout.
79 for tjoin in join_all {
80 let _e = tjoin.join();
81 }
82}
83
84fn sync_fn() -> usize {
85 // Create anonymous synchronized code.
86 //
87 // The code will never run at the same time. If one thread is executing
88 // this code, the second thread will wait for this code to finish executing.
89 let result = synchronized! {
90 static mut POINT0: usize = 0;
91 static mut POINT1: usize = 0;
92
93 unsafe {
94 POINT1 = POINT0;
95 POINT0 += 1;
96
97 POINT1
98 }
99 };
100
101 result
102}
103```
104
105### 3. sync_let
106
107```rust
108use std::thread::spawn;
109use synchronized::synchronized;
110
111/*
112 An example that describes how to quickly create an anonymous
113 sync with a mutable variable.
114
115 This code creates 5 threads, each of which tries to update
116 the `sync_let` variable with data while executing the synchronized anonymous code.
117*/
118
119fn main() {
120 // An array of handles to wait for all threads to complete.
121 let mut join_all = Vec::new();
122
123 // Creation of 5 threads to implement a multi-threaded environment.
124 for thread_id in 0..5 {
125 let join = spawn(move || {
126 // Create anonymous synchronized code with one mutable variable `sync_let` and `count`.
127 let result = synchronized!(
128 (sync_let: String = String::new(), count: usize = 0) {
129 // If it's the first thread,
130 // then theoretically `sync_let` is String::new().
131 if thread_id == 0 {
132 assert_eq!(sync_let.is_empty(), true);
133 assert_eq!(count, &0);
134 }
135
136 // We fill the variable `sync_let` and `count` with data.
137 sync_let.push_str(&thread_id.to_string());
138 sync_let.push_str(" ");
139
140 *count += 1;
141
142 sync_let.clone()
143 }
144 );
145
146 // Outputting debug information.
147 println!("#[id: {}] {}", thread_id, result);
148 });
149
150 // In order for our `assert_eq!(sync_let.is_empty());` code to
151 // always run correctly, the first thread should always run first
152 // (this is just for the stability of this example).
153 if thread_id == 0 {
154 let _e = join.join();
155 continue;
156 }
157
158 join_all.push(join);
159 }
160
161 // We just wait for all threads to finish and look at stdout.
162 for tjoin in join_all {
163 let _e = tjoin.join();
164 }
165}
166```
167
168### 4. point
169
170```rust
171/*
172 An example implementation of synchronized code with
173 one non-anonymous synchronization point.
174
175 This example creates a set of anonymous sync codes associated with a
176 single named sync point. Each synchronization code executes in the same
177 way as ordinary anonymous code, but execution occurs simultaneously in a
178 multi-threaded environment in only one of them.
179
180 !!! In this example, the assembly requires the `point` feature to be active.
181*/
182
183#[cfg( feature = "point" )]
184use synchronized::synchronized_point;
185#[cfg( feature = "point" )]
186use synchronized::synchronized;
187
188#[cfg( not(feature = "point") )]
189macro_rules! synchronized_point {
190 [ $($unk:tt)* ] => {
191 println!("!!! This example requires support for the `point` feature. Run the example with `cargo run --example point --all-features`.");
192 };
193}
194
195fn main() {
196 // A sync point named `COMB_SYNC` to group anonymous code syncs by name.
197 synchronized_point! {(COMB_SYNC) {
198 static mut POINT: usize = 0;
199 println!("GeneralSyncPoint, name_point: {}", COMB_SYNC.get_sync_point_name());
200
201 // #1 Anonymous synchronized code that operates on a
202 // single named synchronization point.
203 //
204 // This code is not executed concurrently in a multi-threaded environment,
205 // one thread is waiting for someone else's code to execute in this part of the code.
206 let result0 = synchronized! ((->COMB_SYNC) {
207 println!("SyncCode, name_point: {}", COMB_SYNC.get_sync_point_name());
208 unsafe {
209 POINT += 1;
210
211 POINT
212 }
213 });
214
215 // This line of code is not synchronized and can run concurrently on all threads.
216 println!("Unsynchronized code");
217
218 // #2 Anonymous synchronized code that operates on a
219 // single named synchronization point.
220 //
221 // Note that `result0` and `result1` cannot be calculated at the same time,
222 // this does not happen because `result0` or `result1` are calculated in
223 // synchronized code with a single sync point of the same name.
224 let result1 = synchronized! ((->COMB_SYNC) {
225 println!("SyncCode, name_point: {}", COMB_SYNC.get_sync_point_name());
226 unsafe {
227 POINT += 1;
228
229 POINT
230 }
231 });
232
233 // Display debug information.
234 println!("result, res0: {:?}, res1: {:?}", result0, result1);
235 }}
236}
237```
238
239# Connection
240
241This section only describes how to choose the default synchronization method for a `synchronized` macro.
242
243### 1. PlugAndPlay (minimal, sync, std)
244
245For a `synchronized` macro, use the primitives implemented by the default `std` library.
246
247```rust,ignore
248[dependencies.synchronized]
249version = "1.0.4"
250default-features = false
251features = [
252 "std",
253 #"point",
254 #"get_point_name"
255]
256```
257
258### 2. PlugAndPlay (minimal, sync, parking_lot)
259
260For a `synchronized` macro, use the primitives implemented by the default `parking_lot` library.
261
262```rust,ignore
263[dependencies.synchronized]
264version = "1.0.4"
265default-features = false
266features = [
267 "parking_lot",
268 #"point",
269 #"get_point_name"
270]
271```
272
273### 3. PlugAndPlay (minimal, async, tokio+parking_lot+async_trait)
274
275For a `synchronized` macro, use the primitives implemented by the default `tokio` library.
276
277```rust,ignore
278[dependencies.synchronized]
279version = "1.0.4"
280default-features = false
281features = [
282 "async",
283 #"point",
284 #"get_point_name"
285]
286```
287
288# Additionally inf
289
2901. The macro is an alternative to the `synchronized` keyword from the Java programming language for the Rust programming language with all sorts of extensions.
291
2922. 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).
293
294*/
295
296#![cfg_attr(docsrs, feature(doc_cfg))]
297#![cfg_attr(not(feature = "std"), no_std)]
298
299#[macro_use]
300mod async_core;
301pub mod core;
302#[cfg( feature = "point" )]
303#[cfg_attr(docsrs, doc(cfg(feature = "point")))]
304mod point;
305
306
307mod macro_features;
308
309/// Various synchronization primitives used in the `synchronized` macro.
310pub mod beh {
311 #[cfg_attr(docsrs, doc(cfg( feature = "parking_lot" )))]
312 #[cfg( feature = "parking_lot" )]
313 pub mod pl;
314
315 #[cfg_attr(docsrs, doc(cfg( feature = "std" )))]
316 #[cfg( feature = "std" )]
317 pub mod std;
318
319 #[cfg_attr(docsrs, doc(cfg( feature = "async" )))]
320 #[cfg( feature = "async" )]
321 //cfg_only_async! {
322 pub mod r#async;
323 //}
324
325 // If locking is not selected, then select `std` by default.
326 #[cfg(
327 any(
328 all(
329 not(feature = "parking_lot"),
330 not(feature = "std"),
331 not(feature = "async")
332 ),
333 /*all(
334 feature = "parking_lot",
335 feature = "std",
336 feature = "async"
337 )*/
338 )
339 )]
340 pub mod std;
341}
342
343/// Convenient and simple macro for code synchronization in multithreading.
344///
345/// ### 1. Anonymous code is synchronized in multi-threaded mode.
346/// ```rust
347/// use synchronized::synchronized;
348///
349/// synchronized! {
350/// static mut POINT0: usize = 0;
351/// static mut POINT1: usize = 0;
352///
353/// unsafe {
354/// POINT1 = POINT0;
355/// POINT0 += 1;
356///
357/// POINT1
358/// }
359/// };
360/// ```
361///
362/// ### 2. Create anonymous synchronized code with one variable to change `sync_let` and `count`.
363/// ```rust
364/// use synchronized::synchronized;
365///
366/// let result = synchronized!((sync_let: String = String::new(), count: usize = 0) {
367/// // We fill the variable `sync_let` with data.
368/// sync_let.push_str("1 ");
369///
370/// *count += 1;
371/// sync_let.clone()
372/// });
373/// ```
374#[macro_export]
375macro_rules! synchronized {
376 {
377 // Named `$sync_point_name` synchronized block with mutable value
378 // of synchronized name `$v_point_name`, type `$ty` and value when
379 // `$expr` is created.
380 // (Use only with `synchronized_point`.)
381 ->$sync_point_name:ident ( $($v_point_name: ident),* $(,)? ) $($all:tt)*
382 } => {{ // synchronized point
383 $crate::__synchronized_beh!(#new_lock(__lock): $sync_point_name);
384
385 let ( $(ref mut $v_point_name),* ) = *__lock;
386 let result = {
387 $($all)*
388 };
389 $(
390 drop($v_point_name);
391 )*
392
393 $crate::__synchronized_beh!(#drop_lock(__lock): $sync_point_name);
394
395 result
396 }};
397
398 {
399 // Named `$sync_point_name` synchronized block with mutable value
400 // of synchronized name `$v_point_name`, type `$ty` and value when
401 // `$expr` is created.
402 $sync_point_name:ident ( $v_point_name: ident: $ty: ty = $expr:expr $(,)? ) $($all:tt)*
403 } => {{
404 $crate::__synchronized_beh!(#new_point<$ty: [$expr]>: $sync_point_name);
405 $crate::synchronized! {
406 ->$sync_point_name ($v_point_name) $($all)*
407 }
408 }};
409
410 {
411 // Named sync block $sync_point_name with mutable values written
412 // comma-separated sync name $v_point_name, type $ty and value when
413 // $expr was created.
414 $sync_point_name:ident ( $($v_point_name: ident: $ty: ty = $expr:expr),* $(,)? ) $($all:tt)*
415 } => {{
416 $crate::__synchronized_beh!(#new_point<($($ty),*): [($($expr),*)]>: $sync_point_name);
417 $crate::synchronized! {
418 ->$sync_point_name ( $($v_point_name),* ) $($all)*
419 }
420 }};
421
422 {
423 // Named sync block named `$v_point_name`.
424 // (Use only with `synchronized_point`.)
425 (->$v_point_name: ident) $($all:tt)*
426 } => {{ // sync point
427 $crate::synchronized! {
428 ->$v_point_name (__empty_value) $($all)*
429 }
430 }};
431
432 {
433 // Named sync block named `$v_point_name`.
434 ($v_point_name: ident) $($all:tt)*
435 } => {{
436 $crate::synchronized! {
437 $v_point_name (__empty_value: () = ()) $($all)*
438 }
439 }};
440 {
441 // Anonymous synchronized block with mutable synchronized name value
442 // `$v_point_name`, type `$ty` and value when `$expr` is created.
443 ( $($v_point_name: ident: $ty: ty = $expr:expr),* $(,)? ) $($all:tt)*
444 } => {{ // sync value
445 $crate::synchronized! {
446 __ANONYMOUS_SYNC_POINT ( $($v_point_name: $ty = $expr),* ) $($all)*
447 }
448 }};
449
450 {
451 // COMPILE_ERROR
452 $(->$_ident1:ident)? /* OR */ $($_ident2:ident)? ($($unk_in:tt)*) $($unk:tt)+
453 } => {
454 compile_error!(concat!(
455 "Error writing macro `synchronized`, incode: ",
456 $(stringify!(->$_ident1),)?
457 $(stringify!($_ident2),)?
458
459 stringify!(($($unk_in)*)),
460
461 stringify!($($unk)+),
462 ));
463 };
464
465 {
466 // Anonymous synchronized block
467 $($all:tt)*
468 } => {{ // nohead synchronized block
469 $crate::synchronized! {
470 (__empty_value: () = ()) $($all)*
471 }
472 }};
473
474 [] => {}
475}
476
477/// Describes the selected default lock for the `synchronized` macro. Currently it is `
478#[doc = crate::__synchronized_beh!( #name )]
479/// `.
480pub const CURRENT_DEF_BEH: &'static str = crate::__synchronized_beh!( #name );
481
482/// Whether `get_point_name` was enabled in this build.
483///
484/// The `get_point_name` feature determines whether the connection
485/// label name can be determined at run time.
486pub const IS_GET_POINT_NAME_SUPPORT: bool = {
487 #[cfg( not(feature = "get_point_name") )] {
488 false
489 }
490
491 #[cfg( feature = "get_point_name" )] {
492 true
493 }
494};
495
496/// Whether synchronized `point` was enabled in this build.
497///
498/// The `synchronized_point` macro allows you to create shared synchronization
499/// points that prevent certain code from being executed at the same time.
500pub const IS_SYNC_POINT_SUPPORT: bool = {
501 #[cfg( not(feature = "point") )] {
502 false
503 }
504
505 #[cfg( feature = "point" )] {
506 true
507 }
508};
509
510cfg_only_async! {
511 /// Determines if the library code and its locks are fully asynchronous.
512 /// Currently it is `true`.
513 pub const IS_ALWAYS_ASYNC: bool = true;
514}
515
516cfg_not_only_async! {
517 /// Determines if the library code and its locks are fully asynchronous.
518 /// Currently it is `false`.
519 pub const IS_ALWAYS_ASYNC: bool = false;
520}