aboutsummaryrefslogtreecommitdiff
path: root/embassy-sync
diff options
context:
space:
mode:
authorbenjaminschlegel87 <[email protected]>2025-07-25 20:39:40 +0200
committerGitHub <[email protected]>2025-07-25 20:39:40 +0200
commitdbc1818acd69e2e15ac574356c9b07cb717df441 (patch)
tree05e6360c1946183b524a1ce82268547fe4bbcfd0 /embassy-sync
parentadb728009ceba095d2190038ff698aaee08907a9 (diff)
parent996974e313fa5ec2c7c2d9dd0998fab244c0a180 (diff)
Merge branch 'embassy-rs:main' into stm32_adc_v3_hw_oversampling_support
Diffstat (limited to 'embassy-sync')
-rw-r--r--embassy-sync/Cargo.toml3
-rw-r--r--embassy-sync/src/channel.rs29
-rw-r--r--embassy-sync/src/lazy_lock.rs7
-rw-r--r--embassy-sync/src/mutex.rs2
-rw-r--r--embassy-sync/src/once_lock.rs2
-rw-r--r--embassy-sync/src/pipe.rs8
-rw-r--r--embassy-sync/src/priority_channel.rs4
-rw-r--r--embassy-sync/src/pubsub/mod.rs8
-rw-r--r--embassy-sync/src/pubsub/publisher.rs2
-rw-r--r--embassy-sync/src/pubsub/subscriber.rs2
-rw-r--r--embassy-sync/tests/ui.rs13
-rw-r--r--embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs11
-rw-r--r--embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr24
-rw-r--r--embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs6
-rw-r--r--embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr9
-rw-r--r--embassy-sync/tests/ui/sync_impl/once_lock.rs6
-rw-r--r--embassy-sync/tests/ui/sync_impl/once_lock.stderr9
17 files changed, 127 insertions, 18 deletions
diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml
index 99962f9f6..9e5c39f5e 100644
--- a/embassy-sync/Cargo.toml
+++ b/embassy-sync/Cargo.toml
@@ -28,7 +28,7 @@ defmt = { version = "1.0.1", optional = true }
28log = { version = "0.4.14", optional = true } 28log = { version = "0.4.14", optional = true }
29 29
30futures-sink = { version = "0.3", default-features = false, features = [] } 30futures-sink = { version = "0.3", default-features = false, features = [] }
31futures-util = { version = "0.3.17", default-features = false } 31futures-core = { version = "0.3.31", default-features = false }
32critical-section = "1.1" 32critical-section = "1.1"
33heapless = "0.8" 33heapless = "0.8"
34cfg-if = "1.0.0" 34cfg-if = "1.0.0"
@@ -43,3 +43,4 @@ futures-util = { version = "0.3.17", features = [ "channel", "sink" ] }
43# Enable critical-section implementation for std, for tests 43# Enable critical-section implementation for std, for tests
44critical-section = { version = "1.1", features = ["std"] } 44critical-section = { version = "1.1", features = ["std"] }
45static_cell = { version = "2" } 45static_cell = { version = "2" }
46trybuild = "1.0.105"
diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs
index 856551417..a0e39fcb5 100644
--- a/embassy-sync/src/channel.rs
+++ b/embassy-sync/src/channel.rs
@@ -17,6 +17,31 @@
17//! messages that it can store, and if this limit is reached, trying to send 17//! messages that it can store, and if this limit is reached, trying to send
18//! another message will result in an error being returned. 18//! another message will result in an error being returned.
19//! 19//!
20//! # Example: Message passing between task and interrupt handler
21//!
22//! ```rust
23//! use embassy_sync::channel::Channel;
24//! use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
25//!
26//! static SHARED_CHANNEL: Channel<CriticalSectionRawMutex, u32, 8> = Channel::new();
27//!
28//! fn my_interrupt_handler() {
29//! // Do some work..
30//! // ...
31//! if let Err(e) = SHARED_CHANNEL.sender().try_send(42) {
32//! // Channel is full..
33//! }
34//! }
35//!
36//! async fn my_async_task() {
37//! // ...
38//! let receiver = SHARED_CHANNEL.receiver();
39//! loop {
40//! let data_from_interrupt = receiver.receive().await;
41//! // Do something with the data.
42//! }
43//! }
44//! ```
20 45
21use core::cell::RefCell; 46use core::cell::RefCell;
22use core::future::Future; 47use core::future::Future;
@@ -443,7 +468,7 @@ where
443 } 468 }
444} 469}
445 470
446impl<'ch, M, T, const N: usize> futures_util::Stream for Receiver<'ch, M, T, N> 471impl<'ch, M, T, const N: usize> futures_core::Stream for Receiver<'ch, M, T, N>
447where 472where
448 M: RawMutex, 473 M: RawMutex,
449{ 474{
@@ -962,7 +987,7 @@ where
962 } 987 }
963} 988}
964 989
965impl<M, T, const N: usize> futures_util::Stream for Channel<M, T, N> 990impl<M, T, const N: usize> futures_core::Stream for Channel<M, T, N>
966where 991where
967 M: RawMutex, 992 M: RawMutex,
968{ 993{
diff --git a/embassy-sync/src/lazy_lock.rs b/embassy-sync/src/lazy_lock.rs
index 18e3c2019..f1bd88b61 100644
--- a/embassy-sync/src/lazy_lock.rs
+++ b/embassy-sync/src/lazy_lock.rs
@@ -31,7 +31,12 @@ union Data<T, F> {
31 f: ManuallyDrop<F>, 31 f: ManuallyDrop<F>,
32} 32}
33 33
34unsafe impl<T, F> Sync for LazyLock<T, F> {} 34unsafe impl<T, F> Sync for LazyLock<T, F>
35where
36 T: Sync,
37 F: Sync,
38{
39}
35 40
36impl<T, F: FnOnce() -> T> LazyLock<T, F> { 41impl<T, F: FnOnce() -> T> LazyLock<T, F> {
37 /// Create a new uninitialized `StaticLock`. 42 /// Create a new uninitialized `StaticLock`.
diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs
index 7528a9f68..8496f34bf 100644
--- a/embassy-sync/src/mutex.rs
+++ b/embassy-sync/src/mutex.rs
@@ -23,7 +23,7 @@ struct State {
23 23
24/// Async mutex. 24/// Async mutex.
25/// 25///
26/// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex). 26/// The mutex is generic over a blocking [`RawMutex`].
27/// The raw mutex is used to guard access to the internal "is locked" flag. It 27/// The raw mutex is used to guard access to the internal "is locked" flag. It
28/// is held for very short periods only, while locking and unlocking. It is *not* held 28/// is held for very short periods only, while locking and unlocking. It is *not* held
29/// for the entire time the async Mutex is locked. 29/// for the entire time the async Mutex is locked.
diff --git a/embassy-sync/src/once_lock.rs b/embassy-sync/src/once_lock.rs
index cd05b986d..1e848685a 100644
--- a/embassy-sync/src/once_lock.rs
+++ b/embassy-sync/src/once_lock.rs
@@ -42,7 +42,7 @@ pub struct OnceLock<T> {
42 data: Cell<MaybeUninit<T>>, 42 data: Cell<MaybeUninit<T>>,
43} 43}
44 44
45unsafe impl<T> Sync for OnceLock<T> {} 45unsafe impl<T> Sync for OnceLock<T> where T: Sync {}
46 46
47impl<T> OnceLock<T> { 47impl<T> OnceLock<T> {
48 /// Create a new uninitialized `OnceLock`. 48 /// Create a new uninitialized `OnceLock`.
diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs
index 2598652d2..df3b28b45 100644
--- a/embassy-sync/src/pipe.rs
+++ b/embassy-sync/src/pipe.rs
@@ -152,7 +152,7 @@ where
152 152
153impl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {} 153impl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {}
154 154
155/// Future returned by [`Pipe::fill_buf`] and [`Reader::fill_buf`]. 155/// Future returned by [`Reader::fill_buf`].
156#[must_use = "futures do nothing unless you `.await` or poll them"] 156#[must_use = "futures do nothing unless you `.await` or poll them"]
157pub struct FillBufFuture<'p, M, const N: usize> 157pub struct FillBufFuture<'p, M, const N: usize>
158where 158where
@@ -587,7 +587,7 @@ where
587 } 587 }
588} 588}
589 589
590/// Write-only access to a [`DynamicPipe`]. 590/// Write-only access to the dynamic pipe.
591pub struct DynamicWriter<'p> { 591pub struct DynamicWriter<'p> {
592 pipe: &'p dyn DynamicPipe, 592 pipe: &'p dyn DynamicPipe,
593} 593}
@@ -657,7 +657,7 @@ where
657 } 657 }
658} 658}
659 659
660/// Read-only access to a [`DynamicPipe`]. 660/// Read-only access to a dynamic pipe.
661pub struct DynamicReader<'p> { 661pub struct DynamicReader<'p> {
662 pipe: &'p dyn DynamicPipe, 662 pipe: &'p dyn DynamicPipe,
663} 663}
@@ -742,7 +742,7 @@ where
742 } 742 }
743} 743}
744 744
745/// Future returned by [`DynamicPipe::fill_buf`] and [`DynamicReader::fill_buf`]. 745/// Future returned by [`DynamicReader::fill_buf`].
746#[must_use = "futures do nothing unless you `.await` or poll them"] 746#[must_use = "futures do nothing unless you `.await` or poll them"]
747pub struct DynamicFillBufFuture<'p> { 747pub struct DynamicFillBufFuture<'p> {
748 pipe: Option<&'p dyn DynamicPipe>, 748 pipe: Option<&'p dyn DynamicPipe>,
diff --git a/embassy-sync/src/priority_channel.rs b/embassy-sync/src/priority_channel.rs
index 623c52993..715a20e86 100644
--- a/embassy-sync/src/priority_channel.rs
+++ b/embassy-sync/src/priority_channel.rs
@@ -1,7 +1,7 @@
1//! A queue for sending values between asynchronous tasks. 1//! A queue for sending values between asynchronous tasks.
2//! 2//!
3//! Similar to a [`Channel`](crate::channel::Channel), however [`PriorityChannel`] sifts higher priority items to the front of the queue. 3//! Similar to a [`Channel`](crate::channel::Channel), however [`PriorityChannel`] sifts higher priority items to the front of the queue.
4//! Priority is determined by the `Ord` trait. Priority behavior is determined by the [`Kind`](heapless::binary_heap::Kind) parameter of the channel. 4//! Priority is determined by the `Ord` trait. Priority behavior is determined by the [`Kind`] parameter of the channel.
5 5
6use core::cell::RefCell; 6use core::cell::RefCell;
7use core::future::Future; 7use core::future::Future;
@@ -473,7 +473,7 @@ where
473/// received from the channel. 473/// received from the channel.
474/// 474///
475/// Sent data may be reordered based on their priority within the channel. 475/// Sent data may be reordered based on their priority within the channel.
476/// For example, in a [`Max`](heapless::binary_heap::Max) [`PriorityChannel`] 476/// For example, in a [`Max`] [`PriorityChannel`]
477/// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be received as `[3, 2, 1]`. 477/// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be received as `[3, 2, 1]`.
478pub struct PriorityChannel<M, T, K, const N: usize> 478pub struct PriorityChannel<M, T, K, const N: usize>
479where 479where
diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs
index 606efff0a..9206b9383 100644
--- a/embassy-sync/src/pubsub/mod.rs
+++ b/embassy-sync/src/pubsub/mod.rs
@@ -88,7 +88,7 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
88 /// Create a new subscriber. It will only receive messages that are published after its creation. 88 /// Create a new subscriber. It will only receive messages that are published after its creation.
89 /// 89 ///
90 /// If there are no subscriber slots left, an error will be returned. 90 /// If there are no subscriber slots left, an error will be returned.
91 pub fn subscriber(&self) -> Result<Subscriber<M, T, CAP, SUBS, PUBS>, Error> { 91 pub fn subscriber(&self) -> Result<Subscriber<'_, M, T, CAP, SUBS, PUBS>, Error> {
92 self.inner.lock(|inner| { 92 self.inner.lock(|inner| {
93 let mut s = inner.borrow_mut(); 93 let mut s = inner.borrow_mut();
94 94
@@ -120,7 +120,7 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
120 /// Create a new publisher 120 /// Create a new publisher
121 /// 121 ///
122 /// If there are no publisher slots left, an error will be returned. 122 /// If there are no publisher slots left, an error will be returned.
123 pub fn publisher(&self) -> Result<Publisher<M, T, CAP, SUBS, PUBS>, Error> { 123 pub fn publisher(&self) -> Result<Publisher<'_, M, T, CAP, SUBS, PUBS>, Error> {
124 self.inner.lock(|inner| { 124 self.inner.lock(|inner| {
125 let mut s = inner.borrow_mut(); 125 let mut s = inner.borrow_mut();
126 126
@@ -151,13 +151,13 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
151 151
152 /// Create a new publisher that can only send immediate messages. 152 /// Create a new publisher that can only send immediate messages.
153 /// This kind of publisher does not take up a publisher slot. 153 /// This kind of publisher does not take up a publisher slot.
154 pub fn immediate_publisher(&self) -> ImmediatePublisher<M, T, CAP, SUBS, PUBS> { 154 pub fn immediate_publisher(&self) -> ImmediatePublisher<'_, M, T, CAP, SUBS, PUBS> {
155 ImmediatePublisher(ImmediatePub::new(self)) 155 ImmediatePublisher(ImmediatePub::new(self))
156 } 156 }
157 157
158 /// Create a new publisher that can only send immediate messages. 158 /// Create a new publisher that can only send immediate messages.
159 /// This kind of publisher does not take up a publisher slot. 159 /// This kind of publisher does not take up a publisher slot.
160 pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher<T> { 160 pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher<'_, T> {
161 DynImmediatePublisher(ImmediatePub::new(self)) 161 DynImmediatePublisher(ImmediatePub::new(self))
162 } 162 }
163 163
diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs
index 7a1ab66de..52a52f926 100644
--- a/embassy-sync/src/pubsub/publisher.rs
+++ b/embassy-sync/src/pubsub/publisher.rs
@@ -75,7 +75,7 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Pub<'a, PSB, T> {
75 self.channel.is_full() 75 self.channel.is_full()
76 } 76 }
77 77
78 /// Create a [`futures::Sink`] adapter for this publisher. 78 /// Create a [`futures_sink::Sink`] adapter for this publisher.
79 #[inline] 79 #[inline]
80 pub const fn sink(&self) -> PubSink<'a, '_, PSB, T> { 80 pub const fn sink(&self) -> PubSink<'a, '_, PSB, T> {
81 PubSink { publ: self, fut: None } 81 PubSink { publ: self, fut: None }
diff --git a/embassy-sync/src/pubsub/subscriber.rs b/embassy-sync/src/pubsub/subscriber.rs
index 6ad660cb3..649382cf1 100644
--- a/embassy-sync/src/pubsub/subscriber.rs
+++ b/embassy-sync/src/pubsub/subscriber.rs
@@ -115,7 +115,7 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Unpin for Sub<'a, PSB, T> {}
115 115
116/// Warning: The stream implementation ignores lag results and returns all messages. 116/// Warning: The stream implementation ignores lag results and returns all messages.
117/// This might miss some messages without you knowing it. 117/// This might miss some messages without you knowing it.
118impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> futures_util::Stream for Sub<'a, PSB, T> { 118impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> futures_core::Stream for Sub<'a, PSB, T> {
119 type Item = T; 119 type Item = T;
120 120
121 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { 121 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
diff --git a/embassy-sync/tests/ui.rs b/embassy-sync/tests/ui.rs
new file mode 100644
index 000000000..e8b1080d8
--- /dev/null
+++ b/embassy-sync/tests/ui.rs
@@ -0,0 +1,13 @@
1#[cfg(not(miri))]
2#[test]
3fn ui() {
4 let t = trybuild::TestCases::new();
5
6 // These test cases should fail to compile since OnceLock and LazyLock should not unconditionally implement sync
7 // for all types. These tests are regression tests against the following issues:
8 // * https://github.com/embassy-rs/embassy/issues/4307
9 // * https://github.com/embassy-rs/embassy/issues/3904
10 t.compile_fail("tests/ui/sync_impl/lazy_lock_function.rs");
11 t.compile_fail("tests/ui/sync_impl/lazy_lock_type.rs");
12 t.compile_fail("tests/ui/sync_impl/once_lock.rs");
13}
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs
new file mode 100644
index 000000000..35f5587c0
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs
@@ -0,0 +1,11 @@
1use embassy_sync::lazy_lock::LazyLock;
2
3fn main() {
4 let x = 128u8;
5 let x_ptr: *const u8 = core::ptr::addr_of!(x);
6 let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) };
7
8 check_sync(LazyLock::new(closure_capturing_non_sync_variable));
9}
10
11fn check_sync<T: Sync>(_lazy_lock: T) {}
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr
new file mode 100644
index 000000000..daf79ad28
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr
@@ -0,0 +1,24 @@
1error[E0277]: `*const u8` cannot be shared between threads safely
2 --> tests/ui/sync_impl/lazy_lock_function.rs:8:16
3 |
46 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) };
5 | -- within this `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}`
67 |
78 | check_sync(LazyLock::new(closure_capturing_non_sync_variable));
8 | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const u8` cannot be shared between threads safely
9 | |
10 | required by a bound introduced by this call
11 |
12 = help: within `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}`, the trait `Sync` is not implemented for `*const u8`
13 = note: required because it appears within the type `&*const u8`
14note: required because it's used within this closure
15 --> tests/ui/sync_impl/lazy_lock_function.rs:6:47
16 |
176 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) };
18 | ^^
19 = note: required for `embassy_sync::lazy_lock::LazyLock<u8, {closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}>` to implement `Sync`
20note: required by a bound in `check_sync`
21 --> tests/ui/sync_impl/lazy_lock_function.rs:11:18
22 |
2311 | fn check_sync<T: Sync>(_lazy_lock: T) {}
24 | ^^^^ required by this bound in `check_sync`
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs
new file mode 100644
index 000000000..4e1383143
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs
@@ -0,0 +1,6 @@
1use embassy_sync::lazy_lock::LazyLock;
2
3// *mut u8 is not Sync, so LazyLock should not implement Sync for this type. This should fail to compile.
4static GLOBAL: LazyLock<*mut u8> = LazyLock::new(|| core::ptr::null_mut());
5
6fn main() {}
diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr
new file mode 100644
index 000000000..1ccc54c7a
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr
@@ -0,0 +1,9 @@
1error[E0277]: `*mut u8` cannot be shared between threads safely
2 --> tests/ui/sync_impl/lazy_lock_type.rs:4:16
3 |
44 | static GLOBAL: LazyLock<*mut u8> = LazyLock::new(|| core::ptr::null_mut());
5 | ^^^^^^^^^^^^^^^^^ `*mut u8` cannot be shared between threads safely
6 |
7 = help: the trait `Sync` is not implemented for `*mut u8`
8 = note: required for `embassy_sync::lazy_lock::LazyLock<*mut u8>` to implement `Sync`
9 = note: shared static variables must have a type that implements `Sync`
diff --git a/embassy-sync/tests/ui/sync_impl/once_lock.rs b/embassy-sync/tests/ui/sync_impl/once_lock.rs
new file mode 100644
index 000000000..8f50d583b
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/once_lock.rs
@@ -0,0 +1,6 @@
1use embassy_sync::once_lock::OnceLock;
2
3// *mut u8 is not Sync, so OnceLock should not implement Sync for this type. This should fail to compile.
4static GLOBAL: OnceLock<*mut u8> = OnceLock::new();
5
6fn main() {}
diff --git a/embassy-sync/tests/ui/sync_impl/once_lock.stderr b/embassy-sync/tests/ui/sync_impl/once_lock.stderr
new file mode 100644
index 000000000..e2419f844
--- /dev/null
+++ b/embassy-sync/tests/ui/sync_impl/once_lock.stderr
@@ -0,0 +1,9 @@
1error[E0277]: `*mut u8` cannot be shared between threads safely
2 --> tests/ui/sync_impl/once_lock.rs:4:16
3 |
44 | static GLOBAL: OnceLock<*mut u8> = OnceLock::new();
5 | ^^^^^^^^^^^^^^^^^ `*mut u8` cannot be shared between threads safely
6 |
7 = help: the trait `Sync` is not implemented for `*mut u8`
8 = note: required for `embassy_sync::once_lock::OnceLock<*mut u8>` to implement `Sync`
9 = note: shared static variables must have a type that implements `Sync`