├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src ├── channel.rs ├── lib.rs └── loom.rs └── tests └── fuzz.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | sudo: false 4 | 5 | env: 6 | global: 7 | - LOOM_MAX_PREEMPTIONS=2 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spmc" 3 | version = "0.3.0" 4 | description = "Simple SPMC channel" 5 | keywords = ["spmc", "channel", "queue"] 6 | authors = ["Sean McArthur "] 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/seanmonstar/spmc" 9 | documentation = "https://docs.rs/spmc" 10 | 11 | [dev-dependencies] 12 | loom = "0.2.2" 13 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2015-2016 Sean McArthur 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Sean McArthur 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spmc 2 | 3 | [![Build Status](https://travis-ci.org/seanmonstar/spmc.svg?branch=master)](https://travis-ci.org/seanmonstar/spmc) 4 | [![crates.io](https://img.shields.io/crates/v/spmc.svg)](https://crates.io/crates/spmc) 5 | 6 | Single-Producer, Multiple-Consumer channel for Rust. 7 | 8 | [Documentation](https://docs.rs/spmc) 9 | 10 | ## License 11 | 12 | Licensed under either of 13 | 14 | * [Apache License, Version 2.0](LICENSE-APACHE) 15 | * [MIT license](LICENSE-MIT) 16 | 17 | at your option. 18 | 19 | ### Contribution 20 | 21 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 22 | -------------------------------------------------------------------------------- /src/channel.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | use std::ptr; 3 | 4 | use std::sync::mpsc::{SendError, RecvError, TryRecvError}; 5 | use std::sync::atomic::Ordering; 6 | 7 | use loom::sync::{Arc, Mutex, CausalCell, Condvar}; 8 | use loom::sync::atomic::{AtomicPtr, AtomicBool, AtomicUsize}; 9 | use loom::thread; 10 | 11 | /// Create a new SPMC channel. 12 | pub fn channel() -> (Sender, Receiver) { 13 | let a = Arc::new(Inner::new()); 14 | (Sender::new(a.clone()), Receiver::new(a)) 15 | } 16 | 17 | /// The Sending side of a SPMC channel. 18 | pub struct Sender { 19 | inner: Arc>, 20 | } 21 | 22 | unsafe impl Send for Sender {} 23 | 24 | impl Sender { 25 | fn new(inner: Arc>) -> Sender { 26 | Sender { inner: inner } 27 | } 28 | 29 | /// Send a message to the receivers. 30 | /// 31 | /// Returns a SendError if there are no more receivers listening. 32 | pub fn send(&mut self, t: T) -> Result<(), SendError> { 33 | if self.inner.is_disconnected.load(Ordering::SeqCst) { 34 | Err(SendError(t)) 35 | } else { 36 | unsafe { 37 | // Only safe from a single thread... 38 | // 39 | // But we have `&mut self`, so we're good! 40 | self.inner.queue.push(t); 41 | } 42 | if self.inner.num_sleeping.load(Ordering::SeqCst) > 0 { 43 | *self.inner.sleeping_guard.lock().unwrap() = true; 44 | self.inner.sleeping_condvar.notify_one(); 45 | } 46 | Ok(()) 47 | } 48 | } 49 | } 50 | 51 | impl Drop for Sender { 52 | fn drop(&mut self) { 53 | self.inner.is_disconnected.store(true, Ordering::SeqCst); 54 | if self.inner.num_sleeping.load(Ordering::SeqCst) > 0 { 55 | *self.inner.sleeping_guard.lock().unwrap() = true; 56 | self.inner.sleeping_condvar.notify_all(); 57 | } 58 | } 59 | } 60 | 61 | /// The receiving side of a SPMC channel. 62 | /// 63 | /// There may be many of these, and the Receiver itself is Sync, so it can be 64 | /// placed in an Arc, or cloned itself. 65 | pub struct Receiver { 66 | inner: Arc>, 67 | } 68 | 69 | unsafe impl Send for Receiver {} 70 | unsafe impl Sync for Receiver {} 71 | 72 | impl Clone for Receiver { 73 | fn clone(&self) -> Receiver { 74 | Receiver { inner: self.inner.clone() } 75 | } 76 | } 77 | 78 | impl Receiver { 79 | fn new(inner: Arc>) -> Receiver { 80 | Receiver { inner: Arc::new(RecvInner { inner: inner }) } 81 | } 82 | 83 | /// Try to receive a message, without blocking. 84 | pub fn try_recv(&self) -> Result { 85 | match self.inner.queue.pop() { 86 | Some(t) => Ok(t), 87 | None => { 88 | if self.inner.is_disconnected.load(Ordering::SeqCst) { 89 | // Check that it didn't fill in a message inbetween 90 | // trying to pop and us checking is_disconnected 91 | match self.inner.queue.pop() { 92 | Some(t) => Ok(t), 93 | None => Err(TryRecvError::Disconnected), 94 | } 95 | } else { 96 | Err(TryRecvError::Empty) 97 | } 98 | } 99 | } 100 | } 101 | 102 | /// Receive a message from the channel. 103 | /// 104 | /// If no message is available, this will block the current thread until a 105 | /// message is sent. 106 | pub fn recv(&self) -> Result { 107 | match self.try_recv() { 108 | Ok(t) => return Ok(t), 109 | Err(TryRecvError::Disconnected) => return Err(RecvError), 110 | Err(TryRecvError::Empty) => {}, 111 | } 112 | 113 | 114 | let ret; 115 | let mut guard = self.inner.sleeping_guard.lock().unwrap(); 116 | self.inner.num_sleeping.fetch_add(1, Ordering::SeqCst); 117 | 118 | loop { 119 | match self.try_recv() { 120 | Ok(t) => { 121 | ret = Ok(t); 122 | break; 123 | }, 124 | Err(TryRecvError::Disconnected) => { 125 | ret = Err(RecvError); 126 | break; 127 | }, 128 | Err(TryRecvError::Empty) => {} 129 | } 130 | guard = self.inner.sleeping_condvar.wait(guard).unwrap(); 131 | } 132 | 133 | self.inner.num_sleeping.fetch_sub(1, Ordering::SeqCst); 134 | ret 135 | } 136 | } 137 | 138 | struct Inner { 139 | queue: Queue, 140 | 141 | is_disconnected: AtomicBool, 142 | 143 | // ohai there. this is all just to allow the blocking functionality 144 | // of recv(). The existance of this mutex is only because the condvar 145 | // needs one. A lock is not used elsewhere, its still a lock-free queue. 146 | sleeping_guard: Mutex, 147 | sleeping_condvar: Condvar, 148 | num_sleeping: AtomicUsize, 149 | } 150 | 151 | impl Inner { 152 | fn new() -> Inner { 153 | Inner { 154 | queue: Queue::new(), 155 | is_disconnected: AtomicBool::new(false), 156 | 157 | sleeping_guard: Mutex::new(false), 158 | sleeping_condvar: Condvar::new(), 159 | num_sleeping: AtomicUsize::new(0), 160 | } 161 | } 162 | } 163 | 164 | struct RecvInner { 165 | inner: Arc>, 166 | } 167 | 168 | impl Deref for RecvInner { 169 | type Target = Arc>; 170 | fn deref(&self) -> &Arc> { 171 | &self.inner 172 | } 173 | } 174 | 175 | impl Drop for RecvInner { 176 | fn drop(&mut self) { 177 | self.inner.is_disconnected.store(true, Ordering::SeqCst); 178 | } 179 | } 180 | 181 | pub(super) struct Queue { 182 | head: CausalCell<*mut Node>, 183 | tail: AtomicPtr>, 184 | } 185 | 186 | impl Queue { 187 | pub(super) fn new() -> Queue { 188 | let stub = Node::new(None); 189 | Queue { 190 | head: CausalCell::new(stub), 191 | tail: AtomicPtr::new(stub), 192 | } 193 | } 194 | 195 | // Not safe to call from multiple threads. 196 | pub(super) unsafe fn push(&self, t: T) { 197 | let end = Node::new(None); 198 | 199 | let node = self.head.with_mut(|p| { 200 | ::std::mem::replace(&mut *p, end) 201 | }); 202 | 203 | (*node).value = Some(t); 204 | (*node).next.store(end, Ordering::SeqCst); 205 | } 206 | 207 | pub(super) fn pop(&self) -> Option { 208 | unsafe { 209 | let mut tail = ptr::null_mut(); 210 | loop { 211 | tail = self.tail.swap(tail, Ordering::SeqCst); 212 | if tail.is_null() { 213 | thread::yield_now(); 214 | continue; 215 | } else { 216 | break; 217 | } 218 | } 219 | 220 | let mut node = Box::from_raw(tail); 221 | 222 | let next = node.next.load(Ordering::SeqCst); 223 | if !next.is_null() { 224 | self.tail.store(next, Ordering::SeqCst); 225 | return node.value.take(); 226 | } else { 227 | self.tail.store(Box::into_raw(node), Ordering::SeqCst); 228 | return None; 229 | } 230 | } 231 | } 232 | } 233 | 234 | impl Drop for Queue { 235 | fn drop(&mut self) { 236 | unsafe { 237 | let head = self.tail.swap(ptr::null_mut(), Ordering::SeqCst); 238 | if head != ptr::null_mut() { 239 | let mut node = Box::from_raw(head); 240 | loop { 241 | let next = node.next.load(Ordering::SeqCst); 242 | if !next.is_null() { 243 | node = Box::from_raw(next); 244 | } else { 245 | break; 246 | } 247 | } 248 | } 249 | } 250 | } 251 | } 252 | 253 | struct Node { 254 | value: Option, 255 | next: AtomicPtr>, 256 | } 257 | 258 | impl Node { 259 | fn new(v: Option) -> *mut Node { 260 | let b = Box::new(Node { 261 | value: v, 262 | next: AtomicPtr::new(ptr::null_mut()), 263 | }); 264 | Box::into_raw(b) 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | #![deny(missing_docs)] 3 | //! # SPMC 4 | //! 5 | //! A single producer, multiple consumers. Commonly used to implement 6 | //! work-stealing. 7 | //! 8 | //! ## Example 9 | //! 10 | //! ``` 11 | //! # use std::thread; 12 | //! let (mut tx, rx) = spmc::channel(); 13 | //! 14 | //! let mut handles = Vec::new(); 15 | //! for n in 0..5 { 16 | //! let rx = rx.clone(); 17 | //! handles.push(thread::spawn(move || { 18 | //! let msg = rx.recv().unwrap(); 19 | //! println!("worker {} recvd: {}", n, msg); 20 | //! })); 21 | //! } 22 | //! 23 | //! for i in 0..5 { 24 | //! tx.send(i * 2).unwrap(); 25 | //! } 26 | //! 27 | //! for handle in handles { 28 | //! handle.join().unwrap(); 29 | //! } 30 | //! ``` 31 | 32 | mod channel; 33 | mod loom; 34 | 35 | pub use self::channel::{channel, Sender, Receiver}; 36 | pub use std::sync::mpsc::{SendError, RecvError, TryRecvError}; 37 | 38 | 39 | #[cfg(test)] 40 | mod tests { 41 | use super::*; 42 | 43 | #[test] 44 | fn test_sanity() { 45 | let (mut tx, rx) = channel(); 46 | tx.send(5).unwrap(); 47 | tx.send(12).unwrap(); 48 | tx.send(1).unwrap(); 49 | 50 | assert_eq!(rx.try_recv(), Ok(5)); 51 | assert_eq!(rx.try_recv(), Ok(12)); 52 | assert_eq!(rx.try_recv(), Ok(1)); 53 | assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); 54 | } 55 | 56 | #[test] 57 | fn test_multiple_consumers() { 58 | let (mut tx, rx) = channel(); 59 | let rx2 = rx.clone(); 60 | tx.send(5).unwrap(); 61 | tx.send(12).unwrap(); 62 | tx.send(1).unwrap(); 63 | 64 | assert_eq!(rx.try_recv(), Ok(5)); 65 | assert_eq!(rx2.try_recv(), Ok(12)); 66 | assert_eq!(rx2.try_recv(), Ok(1)); 67 | assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); 68 | assert_eq!(rx2.try_recv(), Err(TryRecvError::Empty)); 69 | } 70 | 71 | #[test] 72 | fn test_send_on_dropped_chan() { 73 | let (mut tx, rx) = channel(); 74 | drop(rx); 75 | assert_eq!(tx.send(5), Err(SendError(5))); 76 | } 77 | 78 | #[test] 79 | fn test_try_recv_on_dropped_chan() { 80 | let (mut tx, rx) = channel(); 81 | tx.send(2).unwrap(); 82 | drop(tx); 83 | 84 | assert_eq!(rx.try_recv(), Ok(2)); 85 | assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); 86 | assert_eq!(rx.recv(), Err(RecvError)); 87 | } 88 | 89 | #[test] 90 | fn test_recv_blocks() { 91 | use std::thread; 92 | use std::sync::Arc; 93 | use std::sync::atomic::{AtomicBool, Ordering}; 94 | 95 | let (mut tx, rx) = channel(); 96 | let toggle = Arc::new(AtomicBool::new(false)); 97 | let toggle_clone = toggle.clone(); 98 | thread::spawn(move || { 99 | toggle_clone.store(true, Ordering::Relaxed); 100 | tx.send(11).unwrap(); 101 | }); 102 | 103 | assert_eq!(rx.recv(), Ok(11)); 104 | assert!(toggle.load(Ordering::Relaxed)) 105 | } 106 | 107 | #[test] 108 | fn test_recv_unblocks_on_dropped_chan() { 109 | use std::thread; 110 | 111 | let (tx, rx) = channel::(); 112 | thread::spawn(move || { 113 | let _tx = tx; 114 | }); 115 | 116 | assert_eq!(rx.recv(), Err(RecvError)); 117 | } 118 | 119 | #[test] 120 | fn test_send_sleep() { 121 | use std::thread; 122 | use std::time::Duration; 123 | 124 | let (mut tx, rx) = channel(); 125 | 126 | let mut handles = Vec::new(); 127 | for _ in 0..5 { 128 | let rx = rx.clone(); 129 | handles.push(thread::spawn(move || { 130 | rx.recv().unwrap(); 131 | })); 132 | } 133 | 134 | for i in 0..5 { 135 | tx.send(i * 2).unwrap(); 136 | thread::sleep(Duration::from_millis(100)); 137 | } 138 | 139 | for handle in handles { 140 | handle.join().unwrap(); 141 | } 142 | } 143 | 144 | #[test] 145 | fn test_tx_dropped_rxs_drain() { 146 | for l in 0..10 { 147 | println!("loop {}", l); 148 | 149 | let (mut tx, rx) = channel(); 150 | 151 | let mut handles = Vec::new(); 152 | for _ in 0..5 { 153 | let rx = rx.clone(); 154 | handles.push(::std::thread::spawn(move || { 155 | loop { 156 | match rx.recv() { 157 | Ok(_) => continue, 158 | Err(_) => break, 159 | } 160 | } 161 | })); 162 | } 163 | 164 | for i in 0..10 { 165 | tx.send(format!("Sending value {} {}", l, i)).unwrap(); 166 | } 167 | drop(tx); 168 | 169 | for handle in handles { 170 | handle.join().unwrap(); 171 | } 172 | } 173 | } 174 | 175 | #[test] 176 | fn msg_dropped() { 177 | use std::sync::Arc; 178 | use std::sync::atomic::{AtomicBool, Ordering}; 179 | struct Dropped(Arc); 180 | 181 | impl Drop for Dropped { 182 | fn drop(&mut self) { 183 | self.0.store(true, Ordering::Relaxed); 184 | } 185 | } 186 | 187 | let sentinel = Arc::new(AtomicBool::new(false)); 188 | assert!(!sentinel.load(Ordering::Relaxed)); 189 | 190 | 191 | let (mut tx, rx) = channel(); 192 | 193 | tx.send(Dropped(sentinel.clone())).unwrap(); 194 | assert!(!sentinel.load(Ordering::Relaxed)); 195 | 196 | rx.recv().unwrap(); 197 | assert!(sentinel.load(Ordering::Relaxed)); 198 | } 199 | 200 | 201 | #[test] 202 | fn msgs_dropped() { 203 | use std::sync::Arc; 204 | use std::sync::atomic::{AtomicUsize, Ordering}; 205 | struct Dropped(Arc); 206 | 207 | impl Drop for Dropped { 208 | fn drop(&mut self) { 209 | self.0.fetch_add(1, Ordering::Relaxed); 210 | } 211 | } 212 | 213 | let sentinel = Arc::new(AtomicUsize::new(0)); 214 | assert_eq!(0, sentinel.load(Ordering::Relaxed)); 215 | 216 | 217 | let (mut tx, rx) = channel(); 218 | 219 | tx.send(Dropped(sentinel.clone())).unwrap(); 220 | tx.send(Dropped(sentinel.clone())).unwrap(); 221 | tx.send(Dropped(sentinel.clone())).unwrap(); 222 | tx.send(Dropped(sentinel.clone())).unwrap(); 223 | assert_eq!(0, sentinel.load(Ordering::Relaxed)); 224 | 225 | rx.recv().unwrap(); 226 | assert_eq!(1, sentinel.load(Ordering::Relaxed)); 227 | rx.recv().unwrap(); 228 | rx.recv().unwrap(); 229 | rx.recv().unwrap(); 230 | assert_eq!(4, sentinel.load(Ordering::Relaxed)); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/loom.rs: -------------------------------------------------------------------------------- 1 | pub mod sync { 2 | pub use std::sync::{Arc, Mutex, Condvar}; 3 | 4 | pub mod atomic { 5 | pub use std::sync::atomic::{AtomicPtr, AtomicBool, AtomicUsize, Ordering}; 6 | } 7 | 8 | use std::cell::UnsafeCell; 9 | 10 | pub struct CausalCell(UnsafeCell); 11 | 12 | impl CausalCell { 13 | pub fn new(data: T) -> CausalCell { 14 | CausalCell(UnsafeCell::new(data)) 15 | } 16 | 17 | /* 18 | pub fn with(&self, f: F) -> R 19 | where 20 | F: FnOnce(*const T) -> R, 21 | { 22 | f(self.0.get()) 23 | } 24 | */ 25 | 26 | /* 27 | pub fn with_unchecked(&self, f: F) -> R 28 | where 29 | F: FnOnce(*const T) -> R, 30 | { 31 | f(self.0.get()) 32 | } 33 | */ 34 | 35 | pub fn with_mut(&self, f: F) -> R 36 | where 37 | F: FnOnce(*mut T) -> R, 38 | { 39 | f(self.0.get()) 40 | } 41 | } 42 | } 43 | 44 | pub mod thread { 45 | // Requires Rust 1.24+ 46 | // pub use std::sync::atomic::spin_loop_hint as yield_now; 47 | pub fn yield_now() {} 48 | } 49 | -------------------------------------------------------------------------------- /tests/fuzz.rs: -------------------------------------------------------------------------------- 1 | extern crate loom; 2 | 3 | use loom::thread; 4 | 5 | #[path = "../src/channel.rs"] 6 | mod spmc; 7 | 8 | struct DropCounter(usize); 9 | 10 | impl Drop for DropCounter { 11 | fn drop(&mut self) { 12 | self.0 += 1; 13 | assert_eq!(self.0, 1, "DropCounter dropped too many times"); 14 | } 15 | } 16 | 17 | fn msg() -> DropCounter { 18 | DropCounter(0) 19 | } 20 | 21 | #[test] 22 | fn smoke() { 23 | 24 | loom::model(|| { 25 | let (mut tx, rx) = spmc::channel::(); 26 | 27 | let th = thread::spawn(move || { 28 | while let Ok(_s) = rx.recv() { 29 | // ok 30 | } 31 | }); 32 | 33 | tx.send("hello".into()).unwrap(); 34 | drop(tx); 35 | th.join().unwrap(); 36 | }); 37 | } 38 | 39 | #[test] 40 | fn no_send() { 41 | loom::model(|| { 42 | let (tx, rx) = spmc::channel::(); 43 | 44 | let th = thread::spawn(move || { 45 | while let Ok(_s) = rx.recv() { 46 | unreachable!("no sends"); 47 | } 48 | }); 49 | 50 | drop(tx); 51 | th.join().unwrap(); 52 | }); 53 | } 54 | 55 | #[test] 56 | fn multiple_threads_race() { 57 | loom::model(|| { 58 | let (mut tx, rx) = spmc::channel(); 59 | 60 | 61 | let mut threads = Vec::new(); 62 | 63 | threads.push(thread::spawn(move || { 64 | tx.send(msg()).unwrap(); 65 | tx.send(msg()).unwrap(); 66 | })); 67 | 68 | for _ in 0..2 { 69 | let rx = rx.clone(); 70 | threads.push(thread::spawn(move || { 71 | let mut cnt = 0; 72 | while let Ok(_s) = rx.recv() { 73 | cnt += 1; 74 | } 75 | drop(cnt); 76 | })); 77 | } 78 | 79 | for th in threads { 80 | th.join().unwrap(); 81 | } 82 | }); 83 | } 84 | 85 | 86 | #[test] 87 | fn message_per_thread() { 88 | loom::model(|| { 89 | let (mut tx, rx) = spmc::channel(); 90 | 91 | 92 | let mut threads = Vec::new(); 93 | 94 | threads.push(thread::spawn(move || { 95 | tx.send(msg()).unwrap(); 96 | tx.send(msg()).unwrap(); 97 | })); 98 | 99 | for t in 0..2 { 100 | let rx = rx.clone(); 101 | threads.push(thread::spawn(move || { 102 | match rx.recv() { 103 | Ok(_s) => (), 104 | Err(_e) => panic!("rx thread {} didn't get message", t), 105 | } 106 | })); 107 | } 108 | 109 | for th in threads { 110 | th.join().unwrap(); 111 | } 112 | }); 113 | } 114 | 115 | #[test] 116 | fn extra_message() { 117 | loom::model(|| { 118 | let (mut tx, rx) = spmc::channel(); 119 | 120 | 121 | let mut threads = Vec::new(); 122 | 123 | threads.push(thread::spawn(move || { 124 | tx.send(msg()).unwrap(); 125 | tx.send(msg()).unwrap(); 126 | tx.send(msg()).unwrap(); 127 | })); 128 | 129 | for t in 0..2 { 130 | let rx = rx.clone(); 131 | threads.push(thread::spawn(move || { 132 | match rx.recv() { 133 | Ok(_s) => (), 134 | Err(_e) => panic!("rx thread {} didn't get message", t), 135 | } 136 | })); 137 | } 138 | 139 | for th in threads { 140 | th.join().unwrap(); 141 | } 142 | }); 143 | } 144 | --------------------------------------------------------------------------------