├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src └── lib.rs └── tests └── smoke.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "futures-threadpool" 3 | version = "0.1.0" 4 | authors = ["Carl Lerche "] 5 | license = "MIT/Apache-2.0" 6 | readme = "README.md" 7 | keywords = ["futures", "threadpool"] 8 | repository = "https://github.com/carllerche/futures-threadpool" 9 | homepage = "https://github.com/carllerche/futures-threadpool" 10 | documentation = "https://docs.rs/futures-threadpool" 11 | description = """ 12 | An implementation of thread pools using futures 13 | """ 14 | 15 | [dependencies] 16 | futures = "0.1" 17 | num_cpus = "1.0" 18 | crossbeam = "0.2" 19 | 20 | [dependencies.futures-spawn] 21 | version = "0.1" 22 | default-features = false 23 | features = ["use_std"] 24 | -------------------------------------------------------------------------------- /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 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Carl Lerche 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Carl Lerche 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 | # futures-threadpool 2 | 3 | A library for creating futures representing work happening concurrently on a 4 | dedicated thread pool. 5 | 6 | ## Usage 7 | 8 | First, add this to your `Cargo.toml`: 9 | 10 | ```toml 11 | [dependencies] 12 | futures = "0.1" 13 | futures-threadpool = "0.1" 14 | ``` 15 | 16 | Next, add this to your crate: 17 | 18 | ```rust 19 | extern crate futures; 20 | extern crate futures_threadpool; 21 | 22 | use futures_threadpool::ThreadPool; 23 | ``` 24 | 25 | # License 26 | 27 | `futures-threadpool` is primarily distributed under the terms of both the MIT 28 | license and the Apache License (Version 2.0), with portions covered by various 29 | BSD-like licenses. 30 | 31 | See LICENSE-APACHE, and LICENSE-MIT for details. 32 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A simple crate for executing work on a thread pool, and getting back a 2 | //! future. 3 | //! 4 | //! This crate provides a simple thread pool abstraction for running work 5 | //! externally from the current thread that's running. An instance of `Future` 6 | //! is handed back to represent that the work may be done later, and further 7 | //! computations can be chained along with it as well. 8 | //! 9 | //! ```rust 10 | //! extern crate futures; 11 | //! extern crate futures_spawn; 12 | //! extern crate futures_threadpool; 13 | //! 14 | //! use futures::Future; 15 | //! use futures_spawn::SpawnHelper; 16 | //! use futures_threadpool::ThreadPool; 17 | //! 18 | //! # fn long_running_future(a: u32) -> futures::future::BoxFuture { 19 | //! # futures::future::result(Ok(a)).boxed() 20 | //! # } 21 | //! # fn main() { 22 | //! 23 | //! // Create a worker thread pool with four threads 24 | //! let pool = ThreadPool::new(4); 25 | //! 26 | //! // Execute some work on the thread pool, optionally closing over data. 27 | //! let a = pool.spawn(long_running_future(2)); 28 | //! let b = pool.spawn(long_running_future(100)); 29 | //! 30 | //! // Express some further computation once the work is completed on the thread 31 | //! // pool. 32 | //! let c = a.join(b).map(|(a, b)| a + b).wait().unwrap(); 33 | //! 34 | //! // Print out the result 35 | //! println!("{:?}", c); 36 | //! # } 37 | //! ``` 38 | 39 | #![deny(warnings, missing_docs)] 40 | 41 | extern crate futures_spawn; 42 | extern crate crossbeam; 43 | 44 | #[macro_use] 45 | extern crate futures; 46 | extern crate num_cpus; 47 | 48 | use std::panic::AssertUnwindSafe; 49 | use std::sync::Arc; 50 | use std::sync::atomic::{AtomicUsize, Ordering}; 51 | use std::thread; 52 | 53 | use crossbeam::sync::MsQueue; 54 | use futures::{Future, Poll, Async}; 55 | use futures::executor::{self, Run, Executor}; 56 | use futures_spawn::Spawn; 57 | 58 | /// A thread pool intended to run CPU intensive work. 59 | /// 60 | /// This thread pool will hand out futures representing the completed work 61 | /// that happens on the thread pool itself, and the futures can then be later 62 | /// composed with other work as part of an overall computation. 63 | /// 64 | /// The worker threads associated with a thread pool are kept alive so long as 65 | /// there is an open handle to the `ThreadPool` or there is work running on them. Once 66 | /// all work has been drained and all references have gone away the worker 67 | /// threads will be shut down. 68 | /// 69 | /// Currently `ThreadPool` implements `Clone` which just clones a new reference to 70 | /// the underlying thread pool. 71 | /// 72 | /// **Note:** if you use ThreadPool inside a library it's better accept a 73 | /// `Builder` object for thread configuration rather than configuring just 74 | /// pool size. This not only future proof for other settings but also allows 75 | /// user to attach monitoring tools to lifecycle hooks. 76 | pub struct ThreadPool { 77 | inner: Arc, 78 | } 79 | 80 | /// Thread pool configuration object 81 | /// 82 | /// Builder starts with a number of workers equal to the number 83 | /// of CPUs on the host. But you can change it until you call `create()`. 84 | pub struct Builder { 85 | pool_size: usize, 86 | name_prefix: Option, 87 | after_start: Option>, 88 | before_stop: Option>, 89 | } 90 | 91 | struct MySender { 92 | fut: F, 93 | } 94 | 95 | fn _assert() { 96 | fn _assert_send() {} 97 | fn _assert_sync() {} 98 | _assert_send::(); 99 | _assert_sync::(); 100 | } 101 | 102 | struct Inner { 103 | queue: MsQueue, 104 | cnt: AtomicUsize, 105 | size: usize, 106 | after_start: Option>, 107 | before_stop: Option>, 108 | } 109 | 110 | enum Message { 111 | Run(Run), 112 | Close, 113 | } 114 | 115 | impl ThreadPool { 116 | /// Creates a new thread pool with `size` worker threads associated with it. 117 | /// 118 | /// The returned handle can use `execute` to run work on this thread pool, 119 | /// and clones can be made of it to get multiple references to the same 120 | /// thread pool. 121 | /// 122 | /// This is a shortcut for: 123 | /// ```rust 124 | /// Builder::new().pool_size(size).create() 125 | /// ``` 126 | pub fn new(size: usize) -> ThreadPool { 127 | Builder::new().pool_size(size).create() 128 | } 129 | 130 | /// Creates a new thread pool with a number of workers equal to the number 131 | /// of CPUs on the host. 132 | /// 133 | /// This is a shortcut for: 134 | /// ```rust 135 | /// Builder::new().create() 136 | /// ``` 137 | pub fn new_num_cpus() -> ThreadPool { 138 | Builder::new().create() 139 | } 140 | } 141 | 142 | impl Spawn for ThreadPool 143 | where T: Future + Send + 'static, 144 | { 145 | fn spawn_detached(&self, f: T) { 146 | // AssertUnwindSafe is used here becuase `Send + 'static` is basically 147 | // an alias for an implementation of the `UnwindSafe` trait but we can't 148 | // express that in the standard library right now. 149 | let f = AssertUnwindSafe(f).catch_unwind(); 150 | let sender = MySender { fut: f }; 151 | executor::spawn(sender).execute(self.inner.clone()); 152 | } 153 | } 154 | 155 | fn work(inner: &Inner) { 156 | inner.after_start.as_ref().map(|fun| fun()); 157 | loop { 158 | match inner.queue.pop() { 159 | Message::Run(r) => r.run(), 160 | Message::Close => break, 161 | } 162 | } 163 | inner.before_stop.as_ref().map(|fun| fun()); 164 | } 165 | 166 | impl Clone for ThreadPool { 167 | fn clone(&self) -> ThreadPool { 168 | self.inner.cnt.fetch_add(1, Ordering::Relaxed); 169 | ThreadPool { inner: self.inner.clone() } 170 | } 171 | } 172 | 173 | impl Drop for ThreadPool { 174 | fn drop(&mut self) { 175 | if self.inner.cnt.fetch_sub(1, Ordering::Relaxed) == 1 { 176 | for _ in 0..self.inner.size { 177 | self.inner.queue.push(Message::Close); 178 | } 179 | } 180 | } 181 | } 182 | 183 | impl Executor for Inner { 184 | fn execute(&self, run: Run) { 185 | self.queue.push(Message::Run(run)) 186 | } 187 | } 188 | 189 | impl Future for MySender { 190 | type Item = (); 191 | type Error = (); 192 | 193 | fn poll(&mut self) -> Poll<(), ()> { 194 | match self.fut.poll() { 195 | Ok(Async::NotReady) => return Ok(Async::NotReady), 196 | _ => {} 197 | } 198 | 199 | Ok(Async::Ready(())) 200 | } 201 | } 202 | 203 | impl Builder { 204 | /// Create a builder a number of workers equal to the number 205 | /// of CPUs on the host. 206 | pub fn new() -> Builder { 207 | Builder { 208 | pool_size: num_cpus::get(), 209 | name_prefix: None, 210 | after_start: None, 211 | before_stop: None, 212 | } 213 | } 214 | 215 | /// Set size of a future ThreadPool 216 | /// 217 | /// The size of a thread pool is the number of worker threads spawned 218 | pub fn pool_size(&mut self, size: usize) -> &mut Self { 219 | self.pool_size = size; 220 | self 221 | } 222 | 223 | /// Set thread name prefix of a future ThreadPool 224 | /// 225 | /// Thread name prefix is used for generating thread names. For example, if prefix is 226 | /// `my-pool-`, then threads in the pool will get names like `my-pool-1` etc. 227 | pub fn name_prefix>(&mut self, name_prefix: S) -> &mut Self { 228 | self.name_prefix = Some(name_prefix.into()); 229 | self 230 | } 231 | 232 | /// Execute function `f` right after each thread is started but before 233 | /// running any jobs on it 234 | /// 235 | /// This is initially intended for bookkeeping and monitoring uses 236 | pub fn after_start(&mut self, f: F) -> &mut Self 237 | where F: Fn() + Send + Sync + 'static 238 | { 239 | self.after_start = Some(Arc::new(f)); 240 | self 241 | } 242 | 243 | /// Execute function `f` before each worker thread stops 244 | /// 245 | /// This is initially intended for bookkeeping and monitoring uses 246 | pub fn before_stop(&mut self, f: F) -> &mut Self 247 | where F: Fn() + Send + Sync + 'static 248 | { 249 | self.before_stop = Some(Arc::new(f)); 250 | self 251 | } 252 | 253 | /// Create ThreadPool with configured parameters 254 | pub fn create(&mut self) -> ThreadPool { 255 | let pool = ThreadPool { 256 | inner: Arc::new(Inner { 257 | queue: MsQueue::new(), 258 | cnt: AtomicUsize::new(1), 259 | size: self.pool_size, 260 | after_start: self.after_start.clone(), 261 | before_stop: self.before_stop.clone(), 262 | }), 263 | }; 264 | assert!(self.pool_size > 0); 265 | 266 | for counter in 0..self.pool_size { 267 | let inner = pool.inner.clone(); 268 | let mut thread_builder = thread::Builder::new(); 269 | if let Some(ref name_prefix) = self.name_prefix { 270 | thread_builder = thread_builder.name(format!("{}{}", name_prefix, counter)); 271 | } 272 | thread_builder.spawn(move || work(&inner)).unwrap(); 273 | } 274 | 275 | return pool 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /tests/smoke.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate futures_spawn; 3 | extern crate futures_threadpool; 4 | 5 | use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; 6 | use std::thread; 7 | use std::time::Duration; 8 | 9 | use futures::future::{Future, BoxFuture}; 10 | use futures_spawn::{SpawnHelper}; 11 | use futures_threadpool::{ThreadPool, Builder}; 12 | 13 | fn done(t: T) -> BoxFuture { 14 | futures::future::ok(t).boxed() 15 | } 16 | 17 | #[test] 18 | fn join() { 19 | let pool = ThreadPool::new(2); 20 | let a = pool.spawn(done(1)); 21 | let b = pool.spawn(done(2)); 22 | let res = a.join(b).map(|(a, b)| a + b).wait(); 23 | 24 | assert_eq!(res.unwrap(), 3); 25 | } 26 | 27 | #[test] 28 | fn select() { 29 | let pool = ThreadPool::new(2); 30 | let a = pool.spawn(done(1)); 31 | let b = pool.spawn(done(2)); 32 | let (item1, next) = a.select(b).wait().ok().unwrap(); 33 | let item2 = next.wait().unwrap(); 34 | 35 | assert!(item1 != item2); 36 | assert!((item1 == 1 && item2 == 2) || (item1 == 2 && item2 == 1)); 37 | } 38 | 39 | #[test] 40 | fn threads_go_away() { 41 | static CNT: AtomicUsize = ATOMIC_USIZE_INIT; 42 | 43 | struct A; 44 | 45 | impl Drop for A { 46 | fn drop(&mut self) { 47 | CNT.fetch_add(1, Ordering::SeqCst); 48 | } 49 | } 50 | 51 | thread_local!(static FOO: A = A); 52 | 53 | let pool = ThreadPool::new(2); 54 | let _handle = pool.spawn_fn(|| { 55 | FOO.with(|_| ()); 56 | Ok::<(), ()>(()) 57 | }); 58 | drop(pool); 59 | 60 | for _ in 0..100 { 61 | if CNT.load(Ordering::SeqCst) == 1 { 62 | return 63 | } 64 | thread::sleep(Duration::from_millis(10)); 65 | } 66 | panic!("thread didn't exit"); 67 | } 68 | 69 | #[test] 70 | fn lifecycle_test() { 71 | static NUM_STARTS: AtomicUsize = ATOMIC_USIZE_INIT; 72 | static NUM_STOPS: AtomicUsize = ATOMIC_USIZE_INIT; 73 | 74 | fn after_start() { 75 | NUM_STARTS.fetch_add(1, Ordering::SeqCst); 76 | } 77 | 78 | fn before_stop() { 79 | NUM_STOPS.fetch_add(1, Ordering::SeqCst); 80 | } 81 | 82 | let pool = Builder::new() 83 | .pool_size(4) 84 | .after_start(after_start) 85 | .before_stop(before_stop) 86 | .create(); 87 | let _handle = pool.spawn_fn(|| { 88 | Ok::<(), ()>(()) 89 | }); 90 | drop(pool); 91 | 92 | for _ in 0..100 { 93 | if NUM_STOPS.load(Ordering::SeqCst) == 4 { 94 | assert_eq!(NUM_STARTS.load(Ordering::SeqCst), 4); 95 | return; 96 | } 97 | thread::sleep(Duration::from_millis(10)); 98 | } 99 | panic!("thread didn't exit"); 100 | } 101 | 102 | #[test] 103 | fn thread_name() { 104 | let pool = Builder::new() 105 | .name_prefix("my-pool-") 106 | .create(); 107 | let future = pool.spawn_fn(|| { 108 | assert!(thread::current().name().unwrap().starts_with("my-pool-")); 109 | Ok::<(), ()>(()) 110 | }); 111 | let _ = future.wait(); 112 | } 113 | --------------------------------------------------------------------------------