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}