├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── lib.rs ├── sync_parking_lot.rs └── sync_std.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "watch" 3 | version = "0.2.3" 4 | authors = ["Alice Ryhl "] 5 | edition = "2018" 6 | license = "MIT" 7 | readme = "README.md" 8 | documentation = "https://docs.rs/watch/0.2.3/watch/" 9 | repository = "https://github.com/Darksonn/watch" 10 | description = """ 11 | A synchronous message passing channel that only retains the most recent value. 12 | """ 13 | categories = ["data-structures", "concurrency"] 14 | keywords = ["channel", "watch"] 15 | 16 | [dependencies] 17 | parking_lot = { version = "0.12", optional = true } 18 | 19 | [package.metadata.docs.rs] 20 | all-features = true 21 | 22 | [package.metadata.playground] 23 | features = ["parking_lot"] 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Alice Ryhl 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # watch 2 | 3 | This crate provides a synchronous message passing channel that only retains the 4 | most recent value. 5 | 6 | The crate is passively maintained. This means that I consider it feature 7 | complete, and that I am therefore not actively adding new features. However, if 8 | you have suggestions for new features or have found a bug, please open an issue. 9 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides a synchronous message passing channel that only 2 | //! retains the most recent value. 3 | //! 4 | //! This crate provides a `parking_lot` feature. When enabled, the crate will 5 | //! use the mutex from the `parking_lot` crate rather than the one from std. 6 | use std::{ 7 | sync::Arc, 8 | time::{Duration, Instant}, 9 | }; 10 | 11 | #[cfg(not(feature = "parking_lot"))] 12 | mod sync_std; 13 | #[cfg(not(feature = "parking_lot"))] 14 | use sync_std::{Condvar, Mutex}; 15 | 16 | #[cfg(feature = "parking_lot")] 17 | mod sync_parking_lot; 18 | #[cfg(feature = "parking_lot")] 19 | use sync_parking_lot::{Condvar, Mutex}; 20 | 21 | /// The sender for the watch channel. 22 | /// 23 | /// The sender can be cloned to obtain multiple senders for the same channel. 24 | pub struct WatchSender { 25 | shared: Arc>, 26 | } 27 | 28 | /// The receiver for the watch channel. 29 | /// 30 | /// The receiver can be cloned. Each clone will yield a new receiver that 31 | /// receives the same messages. 32 | pub struct WatchReceiver { 33 | shared: Arc>, 34 | last_seen_version: u64, 35 | } 36 | 37 | impl Clone for WatchSender { 38 | fn clone(&self) -> WatchSender { 39 | WatchSender { 40 | shared: self.shared.clone(), 41 | } 42 | } 43 | } 44 | impl Clone for WatchReceiver { 45 | fn clone(&self) -> WatchReceiver { 46 | WatchReceiver { 47 | shared: self.shared.clone(), 48 | last_seen_version: self.last_seen_version, 49 | } 50 | } 51 | } 52 | 53 | struct Shared { 54 | lock: Mutex>, 55 | on_update: Condvar, 56 | } 57 | struct SharedValue { 58 | value: T, 59 | version: u64, 60 | } 61 | 62 | /// Creates a new watch channel. 63 | /// 64 | /// The starting value in the channel is not initially considered seen by the receiver. 65 | pub fn channel(value: T) -> (WatchSender, WatchReceiver) { 66 | let shared = Arc::new(Shared { 67 | lock: Mutex::new(SharedValue { value, version: 1 }), 68 | on_update: Condvar::new(), 69 | }); 70 | ( 71 | WatchSender { 72 | shared: shared.clone(), 73 | }, 74 | WatchReceiver { 75 | shared, 76 | last_seen_version: 0, 77 | }, 78 | ) 79 | } 80 | 81 | impl WatchSender { 82 | /// Send a new message and notify all receivers currently waiting for a 83 | /// message. 84 | pub fn send(&self, mut value: T) { 85 | { 86 | let mut lock = self.shared.lock.lock(); 87 | std::mem::swap(&mut lock.value, &mut value); 88 | lock.version = lock.version.wrapping_add(1); 89 | } 90 | self.shared.on_update.notify_all(); 91 | 92 | // Destroy old value after releasing lock. 93 | drop(value); 94 | } 95 | 96 | /// Update the message by a closure and notify all receivers currently waiting for a message. 97 | pub fn update(&self, f: F) 98 | where 99 | F: FnOnce(&mut T), 100 | { 101 | { 102 | let mut lock = self.shared.lock.lock(); 103 | f(&mut lock.value); 104 | lock.version = lock.version.wrapping_add(1); 105 | } 106 | self.shared.on_update.notify_all(); 107 | } 108 | 109 | /// Create a new receiver for the channel. 110 | /// 111 | /// Any messages sent before this method was called are considered seen by 112 | /// the new receiver. 113 | pub fn subscribe(&self) -> WatchReceiver { 114 | let version = { 115 | let lock = self.shared.lock.lock(); 116 | lock.version 117 | }; 118 | 119 | WatchReceiver { 120 | shared: self.shared.clone(), 121 | last_seen_version: version, 122 | } 123 | } 124 | } 125 | 126 | impl WatchReceiver { 127 | /// Get a clone of the latest value sent on the channel. 128 | pub fn get(&mut self) -> T { 129 | let lock = self.shared.lock.lock(); 130 | self.last_seen_version = lock.version; 131 | lock.value.clone() 132 | } 133 | 134 | /// Get a clone of the latest value if that value has not previously been 135 | /// seen by this receiver. 136 | pub fn get_if_new(&mut self) -> Option { 137 | let lock = self.shared.lock.lock(); 138 | if self.last_seen_version == lock.version { 139 | return None; 140 | } 141 | self.last_seen_version = lock.version; 142 | Some(lock.value.clone()) 143 | } 144 | 145 | /// This method waits until a new value becomes available and return a clone 146 | /// of it. 147 | pub fn wait(&mut self) -> T { 148 | let mut lock = self.shared.lock.lock(); 149 | 150 | while lock.version == self.last_seen_version { 151 | lock = self.shared.on_update.wait(lock); 152 | } 153 | 154 | self.last_seen_version = lock.version; 155 | lock.value.clone() 156 | } 157 | 158 | /// This method waits until a new value becomes available and return a clone 159 | /// of it, timing out after specified duration. 160 | pub fn wait_timeout(&mut self, duration: Duration) -> Option { 161 | let mut lock = self.shared.lock.lock(); 162 | 163 | let deadline = Instant::now() + duration; 164 | 165 | while lock.version == self.last_seen_version { 166 | let timeout = deadline.saturating_duration_since(Instant::now()); 167 | 168 | lock = self.shared.on_update.wait_timeout(lock, timeout)?; 169 | 170 | // Note: checking after `on_update.wait_timeout` to call it at least once, 171 | // event when `duration` was zero. 172 | if timeout.is_zero() && lock.version == self.last_seen_version { 173 | return None; 174 | } 175 | } 176 | 177 | self.last_seen_version = lock.version; 178 | Some(lock.value.clone()) 179 | } 180 | } 181 | 182 | impl WatchReceiver { 183 | /// Create a new sender for this channel. 184 | pub fn new_sender(&self) -> WatchSender { 185 | WatchSender { 186 | shared: self.shared.clone(), 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/sync_parking_lot.rs: -------------------------------------------------------------------------------- 1 | use parking_lot::MutexGuard; 2 | use std::time::Duration; 3 | 4 | pub struct Mutex { 5 | inner: parking_lot::Mutex, 6 | } 7 | 8 | impl Mutex { 9 | pub fn new(value: T) -> Self { 10 | Self { 11 | inner: parking_lot::Mutex::new(value), 12 | } 13 | } 14 | 15 | pub fn lock(&self) -> MutexGuard<'_, T> { 16 | self.inner.lock() 17 | } 18 | } 19 | 20 | pub struct Condvar { 21 | inner: parking_lot::Condvar, 22 | } 23 | impl Condvar { 24 | pub fn new() -> Self { 25 | Self { 26 | inner: parking_lot::Condvar::new(), 27 | } 28 | } 29 | 30 | pub fn wait<'a, T>(&self, mut guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { 31 | self.inner.wait(&mut guard); 32 | guard 33 | } 34 | 35 | pub fn wait_timeout<'a, T>( 36 | &self, 37 | mut guard: MutexGuard<'a, T>, 38 | duration: Duration, 39 | ) -> Option> { 40 | if self.inner.wait_for(&mut guard, duration).timed_out() { 41 | None 42 | } else { 43 | Some(guard) 44 | } 45 | } 46 | 47 | pub fn notify_all(&self) { 48 | self.inner.notify_all(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/sync_std.rs: -------------------------------------------------------------------------------- 1 | use std::{sync::MutexGuard, time::Duration}; 2 | 3 | pub struct Mutex { 4 | inner: std::sync::Mutex, 5 | } 6 | 7 | impl Mutex { 8 | pub fn new(value: T) -> Self { 9 | Self { 10 | inner: std::sync::Mutex::new(value), 11 | } 12 | } 13 | 14 | pub fn lock(&self) -> MutexGuard<'_, T> { 15 | match self.inner.lock() { 16 | Ok(guard) => guard, 17 | Err(poison) => poison.into_inner(), 18 | } 19 | } 20 | } 21 | 22 | pub struct Condvar { 23 | inner: std::sync::Condvar, 24 | } 25 | impl Condvar { 26 | pub fn new() -> Self { 27 | Self { 28 | inner: std::sync::Condvar::new(), 29 | } 30 | } 31 | 32 | pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { 33 | match self.inner.wait(guard) { 34 | Ok(guard) => guard, 35 | Err(poison) => poison.into_inner(), 36 | } 37 | } 38 | 39 | pub fn wait_timeout<'a, T>( 40 | &self, 41 | guard: MutexGuard<'a, T>, 42 | duration: Duration, 43 | ) -> Option> { 44 | match self.inner.wait_timeout(guard, duration) { 45 | Ok((guard, _)) => Some(guard), 46 | Err(_) => None, 47 | } 48 | } 49 | pub fn notify_all(&self) { 50 | self.inner.notify_all(); 51 | } 52 | } 53 | --------------------------------------------------------------------------------