├── .gitignore ├── examples ├── first_last.rs ├── simple.rs └── refcount.rs ├── .travis.yml ├── src ├── coroutine │ ├── mod.rs │ ├── raw.rs │ ├── unique.rs │ ├── clonable.rs │ └── asymmetric.rs ├── options.rs ├── lib.rs └── asymmetric.rs ├── Cargo.toml ├── LICENSE-MIT ├── README.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *.racertmp 4 | -------------------------------------------------------------------------------- /examples/first_last.rs: -------------------------------------------------------------------------------- 1 | extern crate coroutine; 2 | 3 | use coroutine::asymmetric::Coroutine; 4 | 5 | fn main() { 6 | let mut coro = Coroutine::spawn(|_, initial| { 7 | println!("Initial value: {}", initial); 8 | 2 9 | }); 10 | 11 | println!("Final value: {}", coro.resume(1).unwrap()); 12 | } 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: stable 3 | 4 | os: 5 | - linux 6 | - osx 7 | 8 | cache: cargo 9 | 10 | script: 11 | - rustc -V 12 | - cargo -V 13 | - cargo test --no-fail-fast 14 | - cargo test --release --no-fail-fast 15 | - cargo run --example simple 16 | - cargo run --release --example simple 17 | - cargo doc --no-deps 18 | -------------------------------------------------------------------------------- /src/coroutine/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | pub use self::inner_impl::{Coroutine, Handle}; 3 | 4 | #[cfg(feature = "enable-clonable-handle")] 5 | pub use self::clonable as inner_impl; 6 | #[cfg(not(feature = "enable-clonable-handle"))] 7 | pub use self::unique as inner_impl; 8 | 9 | #[cfg(feature = "enable-clonable-handle")] 10 | pub mod clonable; 11 | #[cfg(not(feature = "enable-clonable-handle"))] 12 | pub mod unique; 13 | 14 | pub mod asymmetric; 15 | pub mod raw; 16 | -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | extern crate coroutine; 2 | extern crate env_logger; 3 | 4 | use std::usize; 5 | use coroutine::asymmetric::Coroutine; 6 | 7 | fn main() { 8 | env_logger::init().unwrap(); 9 | 10 | let coro = Coroutine::spawn(|me, _| { 11 | for num in 0..10 { 12 | me.yield_with(num); 13 | } 14 | usize::MAX 15 | }); 16 | 17 | for num in coro { 18 | println!("{}", num.unwrap()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/options.rs: -------------------------------------------------------------------------------- 1 | //! Coroutine options 2 | 3 | const DEFAULT_STACK_SIZE: usize = 2 * 1024 * 1024; // 2M 4 | 5 | /// Coroutine spawn options 6 | #[derive(Debug)] 7 | pub struct Options { 8 | /// The size of the stack 9 | pub stack_size: usize, 10 | 11 | /// The name of the Coroutine 12 | pub name: Option, 13 | } 14 | 15 | impl Default for Options { 16 | fn default() -> Options { 17 | Options { 18 | stack_size: DEFAULT_STACK_SIZE, 19 | name: None, 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coroutine" 3 | version = "0.8.0" 4 | authors = [ 5 | "Y. T. Chung ", 6 | "Young.Wu ", 7 | "Rustcc Developers", 8 | "Denis Kolodin " 9 | ] 10 | license = "MIT/Apache-2.0" 11 | repository = "https://github.com/rustcc/coroutine-rs" 12 | description = "Coroutine Library in Rust" 13 | readme = "README.md" 14 | homepage = "https://github.com/rustcc/coroutine-rs" 15 | keywords = ["coroutine", "green", "thread", "fiber"] 16 | documentation = "https://docs.rs/coroutine" 17 | 18 | [build-dependencies] 19 | gcc = "0.3" 20 | log = "0.3" 21 | 22 | [lib] 23 | name = "coroutine" 24 | path = "src/lib.rs" 25 | 26 | [dependencies] 27 | libc = "0.2" 28 | context = "1.0" 29 | log = "0.3" 30 | 31 | [dev-dependencies] 32 | env_logger = "0.4.2" 33 | -------------------------------------------------------------------------------- /examples/refcount.rs: -------------------------------------------------------------------------------- 1 | extern crate coroutine; 2 | extern crate env_logger; 3 | 4 | use std::usize; 5 | use std::rc::Rc; 6 | use std::cell::RefCell; 7 | use coroutine::asymmetric::Coroutine; 8 | 9 | fn main() { 10 | env_logger::init().unwrap(); 11 | 12 | let rc = Rc::new(RefCell::new(0)); 13 | 14 | let rc1 = rc.clone(); 15 | let mut coro1 = Coroutine::spawn(move |me, _| { 16 | *rc1.borrow_mut() = 1; 17 | let val = *rc1.borrow(); 18 | me.yield_with(val); // (*rc1.borrow()) - fails with already borrowed 19 | usize::MAX 20 | }); 21 | 22 | let rc2 = rc.clone(); 23 | let mut coro2 = Coroutine::spawn(move |me, _| { 24 | *rc2.borrow_mut() = 2; 25 | let val = *rc2.borrow(); 26 | me.yield_with(val); 27 | usize::MAX 28 | }); 29 | 30 | println!("First: {:?}", coro1.resume(0)); 31 | println!("Second: {:?}", coro2.resume(0)); 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Rustcc Developers 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 | # coroutine-rs 2 | 3 | [![Build Status](https://img.shields.io/travis/rustcc/coroutine-rs.svg)](https://travis-ci.org/rustcc/coroutine-rs) [![crates.io](https://img.shields.io/crates/v/coroutine.svg)](https://crates.io/crates/coroutine) [![crates.io](https://img.shields.io/crates/l/coroutine.svg)](https://crates.io/crates/coroutine) 4 | 5 | [Coroutine](https://en.wikipedia.org/wiki/Coroutine) library in Rust 6 | 7 | ```toml 8 | [dependencies] 9 | coroutine = "0.8" 10 | ``` 11 | 12 | ## Usage 13 | 14 | Basic usage of Coroutine 15 | 16 | ```rust 17 | extern crate coroutine; 18 | 19 | use std::usize; 20 | use coroutine::asymmetric::Coroutine; 21 | 22 | fn main() { 23 | let coro: Coroutine = Coroutine::spawn(|me,_| { 24 | for num in 0..10 { 25 | me.yield_with(num); 26 | } 27 | usize::MAX 28 | }); 29 | 30 | for num in coro { 31 | println!("{}", num.unwrap()); 32 | } 33 | } 34 | ``` 35 | 36 | This program will print the following to the console 37 | 38 | ``` 39 | 0 40 | 1 41 | 2 42 | 3 43 | 4 44 | 5 45 | 6 46 | 7 47 | 8 48 | 9 49 | 18446744073709551615 50 | ``` 51 | 52 | For more detail, please run `cargo doc --open`. 53 | 54 | ## Goals 55 | 56 | - [x] Basic single threaded coroutine support 57 | 58 | - [x] Asymmetric Coroutines 59 | 60 | - [ ] Symmetric Coroutines 61 | 62 | - [ ] Thread-safe: can only resume a coroutine in one thread simultaneously 63 | 64 | ## Notes 65 | 66 | * Basically it supports arm, i686, mips, mipsel and x86_64 platforms, but we have only tested in 67 | 68 | - OS X 10.10.*, x86_64, nightly 69 | 70 | - ArchLinux, x86_64, nightly 71 | 72 | ## Thanks 73 | 74 | - The Rust developers (context switch ASM from libgreen) 75 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Coroutine implementation in Rust. 2 | //! 3 | //! ## Example 4 | //! 5 | //! ```rust 6 | //! use coroutine::asymmetric::*; 7 | //! 8 | //! let mut coro = Coroutine::spawn(|coro, val| { 9 | //! println!("Inside {}", val); 10 | //! coro.yield_with(val + 1) 11 | //! }); 12 | //! 13 | //! println!("Resume1 {}", coro.resume(0).unwrap()); 14 | //! println!("Resume2 {}", coro.resume(2).unwrap()); 15 | //! ``` 16 | //! 17 | //! This will prints 18 | //! 19 | //! ```plain 20 | //! Inside 0 21 | //! Resume1 1 22 | //! Resume2 2 23 | //! ``` 24 | #[macro_use] 25 | extern crate log; 26 | extern crate libc; 27 | extern crate context; 28 | 29 | use std::any::Any; 30 | use std::error; 31 | use std::fmt::{self, Display}; 32 | use std::panic; 33 | use std::thread; 34 | 35 | pub use options::Options; 36 | 37 | pub mod asymmetric; 38 | mod options; 39 | 40 | /// Return type of resuming. Ok if resume successfully with the current state, 41 | /// Err if resume failed with `Error`. 42 | pub type Result = ::std::result::Result; 43 | 44 | /// Resume Error 45 | pub enum Error { 46 | /// Coroutine is panicked 47 | Panicked, 48 | 49 | /// Coroutine is panicking, carry with the parameter of `panic!()` 50 | Panicking(Box), 51 | } 52 | 53 | impl fmt::Debug for Error { 54 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 55 | match self { 56 | &Error::Panicked => write!(f, "Panicked"), 57 | &Error::Panicking(ref err) => { 58 | let msg = match err.downcast_ref::<&'static str>() { 59 | Some(s) => *s, 60 | None => { 61 | match err.downcast_ref::() { 62 | Some(s) => &s[..], 63 | None => "Box", 64 | } 65 | } 66 | }; 67 | write!(f, "Panicking({})", msg) 68 | } 69 | } 70 | } 71 | } 72 | 73 | impl Display for Error { 74 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 75 | write!(f, "{}", error::Error::description(self)) 76 | } 77 | } 78 | 79 | impl error::Error for Error { 80 | fn description(&self) -> &str { 81 | match self { 82 | &Error::Panicked => "Panicked", 83 | &Error::Panicking(..) => "Panicking(..)", 84 | } 85 | } 86 | } 87 | 88 | unsafe fn try R>(f: F) -> thread::Result { 89 | let mut f = Some(f); 90 | let f = &mut f as *mut Option as usize; 91 | 92 | panic::catch_unwind(move || (*(f as *mut Option)).take().unwrap()()) 93 | } 94 | -------------------------------------------------------------------------------- /src/coroutine/raw.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Rustcc Developers 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | use std::mem; 23 | 24 | use context::{Context, Stack}; 25 | 26 | /// Coroutine is nothing more than a context and a stack 27 | #[derive(Debug)] 28 | pub struct Coroutine { 29 | context: Context, 30 | stack: Option, 31 | } 32 | 33 | impl Coroutine { 34 | /// Spawn an empty coroutine 35 | #[inline(always)] 36 | pub unsafe fn empty() -> Coroutine { 37 | Coroutine { 38 | context: Context::empty(), 39 | stack: None, 40 | } 41 | } 42 | 43 | /// New a coroutine with initialized context and stack 44 | #[inline(always)] 45 | pub fn new(ctx: Context, stack: Stack) -> Coroutine { 46 | Coroutine { 47 | context: ctx, 48 | stack: Some(stack), 49 | } 50 | } 51 | 52 | /// Yield to another coroutine 53 | #[inline(always)] 54 | pub fn yield_to(&mut self, target: &Coroutine) { 55 | Context::swap(&mut self.context, &target.context); 56 | } 57 | 58 | /// Take out the stack 59 | #[inline(always)] 60 | pub fn take_stack(mut self) -> Option { 61 | self.stack.take() 62 | } 63 | 64 | /// Get context 65 | #[inline(always)] 66 | pub fn context(&self) -> &Context { 67 | &self.context 68 | } 69 | 70 | /// Get mutable context 71 | #[inline(always)] 72 | pub fn context_mut(&mut self) -> &mut Context { 73 | &mut self.context 74 | } 75 | 76 | /// Set stack 77 | #[inline(always)] 78 | pub fn set_stack(&mut self, stack: Stack) -> Option { 79 | let mut opt_stack = Some(stack); 80 | mem::swap(&mut self.stack, &mut opt_stack); 81 | opt_stack 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /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 [2017] [Rustcc Developers] 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 | -------------------------------------------------------------------------------- /src/coroutine/unique.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Rustcc developers 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Basic single threaded Coroutine 23 | //! 24 | //! ```rust 25 | //! use coroutine::{spawn, sched}; 26 | //! 27 | //! let coro = spawn(|| { 28 | //! println!("Before yield"); 29 | //! 30 | //! // Yield back to its parent who resume this coroutine 31 | //! sched(); 32 | //! 33 | //! println!("I am back!"); 34 | //! }); 35 | //! 36 | //! // Starts the Coroutine 37 | //! coro.resume().ok().expect("Failed to resume"); 38 | //! 39 | //! println!("Back to main"); 40 | //! 41 | //! // Resume it 42 | //! coro.resume().ok().expect("Failed to resume"); 43 | //! 44 | //! println!("Coroutine finished"); 45 | //! ``` 46 | //! 47 | 48 | /* Here is the coroutine(with scheduler) workflow: 49 | * 50 | * -------------------------------- 51 | * -------------------------- | | 52 | * | | v | 53 | * | ---------------- | III.Coroutine::yield_now() 54 | * | ---> | Scheduler | <----- | 55 | * | parent | ---------------- | parent | 56 | * | | ^ parent | | 57 | * | -------------- -------------- -------------- | 58 | * | |Coroutine(1)| |Coroutine(2)| |Coroutine(3)| ---------- 59 | * | -------------- -------------- -------------- 60 | * | ^ | ^ 61 | * | | | | II.do_some_works 62 | * ----------- ------- 63 | * I.Handle.resume() 64 | * 65 | * 66 | * First, all coroutines have a link to a parent coroutine, which was set when the coroutine resumed. 67 | * In the scheduler/coroutine model, every worker coroutine has a parent pointer pointing to 68 | * the scheduler coroutine(which is a raw thread). 69 | * Scheduler resumes a proper coroutine and set the parent pointer, like procedure I does. 70 | * When a coroutine is awaken, it does some work like procedure II does. 71 | * When a coroutine yield(io, finished, paniced or sched), it resumes its parent's context, 72 | * like procedure III does. 73 | * Now the scheduler is awake again and it simply decides whether to put the coroutine to queue again or not, 74 | * according to the coroutine's return status. 75 | * And last, the scheduler continues the scheduling loop and selects a proper coroutine to wake up. 76 | */ 77 | 78 | use std::default::Default; 79 | use thunk::Thunk; 80 | use std::mem::transmute; 81 | use std::rt::unwind::try; 82 | use std::ops::Deref; 83 | use std::ptr::Unique; 84 | use std::fmt::{self, Debug}; 85 | use std::boxed; 86 | 87 | use context::Context; 88 | use stack::Stack; 89 | use {State, Result, Error}; 90 | use options::Options; 91 | use environment::Environment; 92 | 93 | /// Handle of a Coroutine 94 | pub struct Handle(Unique); 95 | 96 | impl Debug for Handle { 97 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 98 | unsafe { 99 | self.get_inner().name().fmt(f) 100 | } 101 | } 102 | } 103 | 104 | unsafe impl Send for Handle {} 105 | 106 | impl Drop for Handle { 107 | fn drop(&mut self) { 108 | unsafe { 109 | let p = Box::from_raw(*self.0); 110 | drop(p); 111 | } 112 | } 113 | } 114 | 115 | impl Handle { 116 | fn new(c: Coroutine) -> Handle { 117 | unsafe { 118 | Handle(Unique::new(boxed::into_raw(Box::new(c)))) 119 | } 120 | } 121 | 122 | unsafe fn get_inner_mut(&self) -> &mut Coroutine { 123 | &mut **self.0 124 | } 125 | 126 | unsafe fn get_inner(&self) -> &Coroutine { 127 | & *self.0.get() 128 | } 129 | 130 | /// Resume the Coroutine 131 | pub fn resume(&self) -> Result { 132 | match self.state() { 133 | State::Finished => return Err(Error::Finished), 134 | State::Panicked => return Err(Error::Panicked), 135 | State::Normal => return Err(Error::Waiting), 136 | State::Running => return Ok(State::Running), 137 | _ => {} 138 | } 139 | 140 | let env = Environment::current(); 141 | 142 | let from_coro_hdl = Coroutine::current(); 143 | { 144 | let (from_coro, to_coro) = unsafe { 145 | (from_coro_hdl.get_inner_mut(), self.get_inner_mut()) 146 | }; 147 | 148 | // Save state 149 | to_coro.set_state(State::Running); 150 | from_coro.set_state(State::Normal); 151 | 152 | env.push(self); 153 | Context::swap(&mut from_coro.saved_context, &to_coro.saved_context); 154 | 155 | from_coro.set_state(State::Running); 156 | } 157 | 158 | match env.take_last_resume_result() { 159 | Some(err) => Err(Error::Panicking(err)), 160 | None => Ok(self.state()), 161 | } 162 | } 163 | 164 | /// Join this Coroutine. 165 | /// 166 | /// If the Coroutine panicked, this method will return an `Err` with panic message. 167 | /// 168 | /// ```ignore 169 | /// // Wait until the Coroutine exits 170 | /// spawn(|| { 171 | /// println!("Before yield"); 172 | /// sched(); 173 | /// println!("Exiting"); 174 | /// }).join().unwrap(); 175 | /// ``` 176 | #[inline] 177 | pub fn join(&self) -> Result { 178 | loop { 179 | match self.resume() { 180 | Ok(State::Finished) => break, 181 | Ok(..) => {}, 182 | Err(Error::Finished) => break, 183 | Err(err) => return Err(err), 184 | } 185 | } 186 | Ok(State::Finished) 187 | } 188 | 189 | /// Get the state of the Coroutine 190 | #[inline] 191 | pub fn state(&self) -> State { 192 | unsafe { 193 | self.get_inner().state() 194 | } 195 | } 196 | 197 | /// Set the state of the Coroutine 198 | #[inline] 199 | fn set_state(&self, state: State) { 200 | unsafe { self.get_inner_mut().set_state(state) } 201 | } 202 | } 203 | 204 | impl Deref for Handle { 205 | type Target = Coroutine; 206 | 207 | #[inline] 208 | fn deref(&self) -> &Coroutine { 209 | unsafe { self.get_inner() } 210 | } 211 | } 212 | 213 | /// A coroutine is nothing more than a (register context, stack) pair. 214 | #[allow(raw_pointer_derive)] 215 | #[derive(Debug)] 216 | pub struct Coroutine { 217 | /// The segment of stack on which the task is currently running or 218 | /// if the task is blocked, on which the task will resume 219 | /// execution. 220 | current_stack_segment: Option, 221 | 222 | /// Always valid if the task is alive and not running. 223 | saved_context: Context, 224 | 225 | /// State 226 | state: State, 227 | 228 | /// Name 229 | name: Option, 230 | } 231 | 232 | unsafe impl Send for Coroutine {} 233 | 234 | /// Destroy coroutine and try to reuse std::stack segment. 235 | impl Drop for Coroutine { 236 | fn drop(&mut self) { 237 | match self.current_stack_segment.take() { 238 | Some(stack) => { 239 | Environment::current().give_stack(stack); 240 | }, 241 | None => {} 242 | } 243 | } 244 | } 245 | 246 | /// Initialization function for make context 247 | extern "C" fn coroutine_initialize(_: usize, f: *mut ()) -> ! { 248 | let func: Box = unsafe { transmute(f) }; 249 | 250 | let ret = unsafe { try(move|| func.invoke(())) }; 251 | 252 | let env = Environment::current(); 253 | 254 | let cur: &mut Coroutine = unsafe { 255 | env.running().get_inner_mut() 256 | }; 257 | 258 | let state = match ret { 259 | Ok(..) => { 260 | env.set_resume_result(None); 261 | 262 | State::Finished 263 | } 264 | Err(err) => { 265 | if cfg!(feature = "enable-panic-message") { 266 | use std::io::stderr; 267 | use std::io::Write; 268 | let msg = match err.downcast_ref::<&'static str>() { 269 | Some(s) => *s, 270 | None => match err.downcast_ref::() { 271 | Some(s) => &s[..], 272 | None => "Box", 273 | } 274 | }; 275 | 276 | let name = cur.name().unwrap_or(""); 277 | 278 | let _ = writeln!(&mut stderr(), "Coroutine '{}' panicked at '{}'", name, msg); 279 | } 280 | 281 | env.set_resume_result(Some(err)); 282 | 283 | State::Panicked 284 | } 285 | }; 286 | 287 | loop { 288 | unsafe { 289 | Coroutine::try_switch(env, state); 290 | } 291 | } 292 | } 293 | 294 | impl Coroutine { 295 | 296 | #[doc(hidden)] 297 | pub unsafe fn empty(name: Option, state: State) -> Handle { 298 | Handle::new(Coroutine { 299 | current_stack_segment: None, 300 | saved_context: Context::empty(), 301 | state: state, 302 | name: name, 303 | }) 304 | } 305 | 306 | #[doc(hidden)] 307 | pub fn new(name: Option, stack: Stack, ctx: Context, state: State) -> Handle { 308 | Handle::new(Coroutine { 309 | current_stack_segment: Some(stack), 310 | saved_context: ctx, 311 | state: state, 312 | name: name, 313 | }) 314 | } 315 | 316 | /// Spawn a Coroutine with options 317 | pub fn spawn_opts(f: F, opts: Options) -> Handle 318 | where F: FnOnce() + Send + 'static 319 | { 320 | 321 | let env = Environment::current(); 322 | let mut stack = env.take_stack(opts.stack_size); 323 | 324 | let ctx = Context::new(coroutine_initialize, 0, f, &mut stack); 325 | 326 | Coroutine::new(opts.name, stack, ctx, State::Suspended) 327 | } 328 | 329 | /// Spawn a Coroutine with default options 330 | pub fn spawn(f: F) -> Handle 331 | where F: FnOnce() + Send + 'static 332 | { 333 | Coroutine::spawn_opts(f, Default::default()) 334 | } 335 | 336 | /// Yield the current running Coroutine to its parent 337 | #[inline] 338 | pub fn yield_now(state: State) { 339 | // Cannot yield with Running state 340 | assert!(state != State::Running); 341 | 342 | let env = Environment::current(); 343 | unsafe { 344 | Coroutine::try_switch(env, state) 345 | } 346 | } 347 | 348 | #[inline(always)] 349 | unsafe fn try_switch(env: &mut Environment, state: State) { 350 | match (env.pop(), env.running()) { 351 | (Some(from_coro), to_coro) => { 352 | from_coro.set_state(state); 353 | Context::swap(&mut from_coro.get_inner_mut().saved_context, 354 | &to_coro.saved_context); 355 | }, 356 | // Environment root 357 | (None, _) => {} 358 | } 359 | } 360 | 361 | /// Yield the current running Coroutine with `Suspended` state 362 | #[inline] 363 | pub fn sched() { 364 | Coroutine::yield_now(State::Suspended) 365 | } 366 | 367 | /// Yield the current running Coroutine with `Blocked` state 368 | #[inline] 369 | pub fn block() { 370 | Coroutine::yield_now(State::Blocked) 371 | } 372 | 373 | /// Get a Handle to the current running Coroutine. 374 | /// 375 | /// It is unsafe because it is an undefined behavior if you resume a Coroutine 376 | /// in more than one native thread. 377 | #[inline] 378 | pub fn current() -> &'static Handle { 379 | Environment::current().running() 380 | } 381 | 382 | #[inline(always)] 383 | fn state(&self) -> State { 384 | self.state 385 | } 386 | 387 | #[inline(always)] 388 | fn set_state(&mut self, state: State) { 389 | self.state = state 390 | } 391 | 392 | /// Get the name of the Coroutine 393 | #[inline(always)] 394 | pub fn name(&self) -> Option<&str> { 395 | self.name.as_ref().map(|s| &**s) 396 | } 397 | 398 | /// Determines whether the current Coroutine is unwinding because of panic. 399 | #[inline(always)] 400 | pub fn panicking(&self) -> bool { 401 | self.state() == State::Panicked 402 | } 403 | 404 | /// Determines whether the Coroutine is finished 405 | #[inline(always)] 406 | pub fn finished(&self) -> bool { 407 | self.state() == State::Finished 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /src/coroutine/clonable.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Rustcc developers 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Basic single threaded Coroutine 23 | //! 24 | //! ```rust 25 | //! use coroutine::{spawn, sched}; 26 | //! 27 | //! let coro = spawn(|| { 28 | //! println!("Before yield"); 29 | //! 30 | //! // Yield back to its parent who resume this coroutine 31 | //! sched(); 32 | //! 33 | //! println!("I am back!"); 34 | //! }); 35 | //! 36 | //! // Starts the Coroutine 37 | //! coro.resume().ok().expect("Failed to resume"); 38 | //! 39 | //! println!("Back to main"); 40 | //! 41 | //! // Resume it 42 | //! coro.resume().ok().expect("Failed to resume"); 43 | //! 44 | //! println!("Coroutine finished"); 45 | //! ``` 46 | //! 47 | 48 | /* Here is the coroutine(with scheduler) workflow: 49 | * 50 | * -------------------------------- 51 | * -------------------------- | | 52 | * | | v | 53 | * | ---------------- | III.Coroutine::yield_now() 54 | * | ---> | Scheduler | <----- | 55 | * | parent | ---------------- | parent | 56 | * | | ^ parent | | 57 | * | -------------- -------------- -------------- | 58 | * | |Coroutine(1)| |Coroutine(2)| |Coroutine(3)| ---------- 59 | * | -------------- -------------- -------------- 60 | * | ^ | ^ 61 | * | | | | II.do_some_works 62 | * ----------- ------- 63 | * I.Handle.resume() 64 | * 65 | * 66 | * First, all coroutines have a link to a parent coroutine, which was set when the coroutine resumed. 67 | * In the scheduler/coroutine model, every worker coroutine has a parent pointer pointing to 68 | * the scheduler coroutine(which is a raw thread). 69 | * Scheduler resumes a proper coroutine and set the parent pointer, like procedure I does. 70 | * When a coroutine is awaken, it does some work like procedure II does. 71 | * When a coroutine yield(io, finished, paniced or sched), it resumes its parent's context, 72 | * like procedure III does. 73 | * Now the scheduler is awake again and it simply decides whether to put the coroutine to queue again or not, 74 | * according to the coroutine's return status. 75 | * And last, the scheduler continues the scheduling loop and selects a proper coroutine to wake up. 76 | */ 77 | 78 | use std::default::Default; 79 | use std::mem::transmute; 80 | use std::rt::unwind::try; 81 | use std::cell::UnsafeCell; 82 | use std::ops::Deref; 83 | use std::sync::Arc; 84 | use std::fmt::{self, Debug}; 85 | 86 | use context::thunk::Thunk; 87 | 88 | use spin::Mutex; 89 | 90 | use context::Context; 91 | use context::stack::Stack; 92 | use environment::Environment; 93 | use {Options, Result, Error, State}; 94 | 95 | /// Handle of a Coroutine 96 | #[derive(Clone)] 97 | pub struct Handle(Arc>); 98 | 99 | impl Debug for Handle { 100 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 101 | unsafe { 102 | self.get_inner().name().fmt(f) 103 | } 104 | } 105 | } 106 | 107 | unsafe impl Send for Handle {} 108 | unsafe impl Sync for Handle {} 109 | 110 | impl Handle { 111 | fn new(c: Coroutine) -> Handle { 112 | Handle(Arc::new(UnsafeCell::new(c))) 113 | } 114 | 115 | unsafe fn get_inner_mut(&self) -> &mut Coroutine { 116 | &mut *self.0.get() 117 | } 118 | 119 | unsafe fn get_inner(&self) -> &Coroutine { 120 | &*self.0.get() 121 | } 122 | 123 | /// Resume the Coroutine 124 | pub fn resume(&self) -> Result { 125 | { 126 | let mut self_state = self.state_lock().lock(); 127 | 128 | match *self_state { 129 | State::Finished => return Err(Error::Finished), 130 | State::Panicked => return Err(Error::Panicked), 131 | State::Normal => return Err(Error::Waiting), 132 | State::Running => return Ok(State::Running), 133 | _ => {} 134 | } 135 | 136 | *self_state = State::Running; 137 | } 138 | 139 | let env = Environment::current(); 140 | 141 | let from_coro_hdl = env.running(); 142 | { 143 | let (from_coro, to_coro) = unsafe { 144 | (from_coro_hdl.get_inner_mut(), self.get_inner_mut()) 145 | }; 146 | 147 | // Save state 148 | from_coro_hdl.set_state(State::Normal); 149 | 150 | env.push(self); 151 | Context::swap(&mut from_coro.saved_context, &to_coro.saved_context); 152 | 153 | from_coro_hdl.set_state(State::Running); 154 | } 155 | 156 | let result = env.take_last_resume_result(); 157 | let state = env.last_switch_state(); 158 | self.set_state(state); 159 | 160 | match result { 161 | Some(err) => Err(Error::Panicking(err)), 162 | None => Ok(state), 163 | } 164 | } 165 | 166 | /// Join this Coroutine. 167 | /// 168 | /// If the Coroutine panicked, this method will return an `Err` with panic message. 169 | /// 170 | /// ``` 171 | /// use coroutine::Coroutine; 172 | /// use coroutine::sched; 173 | /// // Wait until the Coroutine exits 174 | /// Coroutine::spawn(|| { 175 | /// println!("Before yield"); 176 | /// sched(); 177 | /// println!("Exiting"); 178 | /// }).join().unwrap(); 179 | /// ``` 180 | #[inline] 181 | pub fn join(&self) -> Result { 182 | loop { 183 | match self.resume() { 184 | Ok(State::Finished) => break, 185 | Ok(..) => {}, 186 | Err(Error::Finished) => break, 187 | Err(err) => return Err(err), 188 | } 189 | } 190 | Ok(State::Finished) 191 | } 192 | 193 | /// Get the state of the Coroutine 194 | #[inline] 195 | pub fn state(&self) -> State { 196 | *self.state_lock().lock() 197 | } 198 | 199 | /// Set the state of the Coroutine 200 | #[inline] 201 | fn set_state(&self, state: State) { 202 | *self.state_lock().lock() = state; 203 | } 204 | 205 | #[inline] 206 | fn state_lock(&self) -> &Mutex { 207 | unsafe { 208 | self.get_inner().state() 209 | } 210 | } 211 | } 212 | 213 | impl Deref for Handle { 214 | type Target = Coroutine; 215 | 216 | #[inline] 217 | fn deref(&self) -> &Coroutine { 218 | unsafe { self.get_inner() } 219 | } 220 | } 221 | 222 | /// A coroutine is nothing more than a (register context, stack) pair. 223 | // #[derive(Debug)] 224 | pub struct Coroutine { 225 | /// The segment of stack on which the task is currently running or 226 | /// if the task is blocked, on which the task will resume 227 | /// execution. 228 | current_stack_segment: Option, 229 | 230 | /// Always valid if the task is alive and not running. 231 | saved_context: Context, 232 | 233 | /// State 234 | state: Mutex, 235 | 236 | /// Name 237 | name: Option, 238 | } 239 | 240 | unsafe impl Send for Coroutine {} 241 | 242 | /// Destroy coroutine and try to reuse std::stack segment. 243 | impl Drop for Coroutine { 244 | fn drop(&mut self) { 245 | match self.current_stack_segment.take() { 246 | Some(stack) => { 247 | Environment::current().give_stack(stack); 248 | }, 249 | None => {} 250 | } 251 | } 252 | } 253 | 254 | /// Initialization function for make context 255 | extern "C" fn coroutine_initialize(_: usize, f: *mut ()) -> ! { 256 | let func: Box = unsafe { transmute(f) }; 257 | 258 | let ret = unsafe { try(move|| func.invoke(())) }; 259 | 260 | let env = Environment::current(); 261 | 262 | let cur: &mut Coroutine = unsafe { 263 | env.running().get_inner_mut() 264 | }; 265 | 266 | let state = match ret { 267 | Ok(..) => { 268 | env.set_resume_result(None); 269 | 270 | State::Finished 271 | } 272 | Err(err) => { 273 | if cfg!(feature = "enable-panic-message") { 274 | use std::io::stderr; 275 | use std::io::Write; 276 | let msg = match err.downcast_ref::<&'static str>() { 277 | Some(s) => *s, 278 | None => match err.downcast_ref::() { 279 | Some(s) => &s[..], 280 | None => "Box", 281 | } 282 | }; 283 | 284 | let name = cur.name().unwrap_or(""); 285 | 286 | let _ = writeln!(&mut stderr(), "Coroutine '{}' panicked at '{}'", name, msg); 287 | } 288 | 289 | env.set_resume_result(Some(err)); 290 | 291 | State::Panicked 292 | } 293 | }; 294 | 295 | loop { 296 | unsafe { 297 | Coroutine::try_switch(env, state); 298 | } 299 | } 300 | } 301 | 302 | impl Coroutine { 303 | 304 | #[doc(hidden)] 305 | pub unsafe fn empty(name: Option, state: State) -> Handle { 306 | Handle::new(Coroutine { 307 | current_stack_segment: None, 308 | saved_context: Context::empty(), 309 | state: Mutex::new(state), 310 | name: name, 311 | }) 312 | } 313 | 314 | #[doc(hidden)] 315 | pub fn new(name: Option, stack: Stack, ctx: Context, state: State) -> Handle { 316 | Handle::new(Coroutine { 317 | current_stack_segment: Some(stack), 318 | saved_context: ctx, 319 | state: Mutex::new(state), 320 | name: name, 321 | }) 322 | } 323 | 324 | /// Spawn a Coroutine with options 325 | pub fn spawn_opts(f: F, opts: Options) -> Handle 326 | where F: FnOnce() + Send + 'static 327 | { 328 | 329 | let env = Environment::current(); 330 | let mut stack = env.take_stack(opts.stack_size); 331 | 332 | let ctx = Context::new(coroutine_initialize, 0, f, &mut stack); 333 | 334 | Coroutine::new(opts.name, stack, ctx, State::Suspended) 335 | } 336 | 337 | /// Spawn a Coroutine with default options 338 | pub fn spawn(f: F) -> Handle 339 | where F: FnOnce() + Send + 'static 340 | { 341 | Coroutine::spawn_opts(f, Default::default()) 342 | } 343 | 344 | /// Yield the current running Coroutine to its parent 345 | #[inline] 346 | pub fn yield_now(state: State) { 347 | // Cannot yield with Running state 348 | assert!(state != State::Running); 349 | 350 | let env = Environment::current(); 351 | unsafe { 352 | Coroutine::try_switch(env, state) 353 | } 354 | } 355 | 356 | #[inline(always)] 357 | unsafe fn try_switch(env: &mut Environment, state: State) { 358 | match (env.pop(), env.running()) { 359 | (Some(from_coro), to_coro) => { 360 | env.set_switch_state(state); 361 | Context::swap(&mut from_coro.get_inner_mut().saved_context, 362 | &to_coro.saved_context); 363 | }, 364 | // Environment root 365 | (None, _) => {} 366 | } 367 | } 368 | 369 | /// Yield the current running Coroutine with `Suspended` state 370 | #[inline] 371 | pub fn sched() { 372 | Coroutine::yield_now(State::Suspended) 373 | } 374 | 375 | /// Yield the current running Coroutine with `Blocked` state 376 | #[inline] 377 | pub fn block() { 378 | Coroutine::yield_now(State::Blocked) 379 | } 380 | 381 | /// Get a Handle to the current running Coroutine. 382 | /// 383 | /// It is unsafe because it is an undefined behavior if you resume a Coroutine 384 | /// in more than one native thread. 385 | #[inline] 386 | pub fn current() -> &'static Handle { 387 | Environment::current().running() 388 | } 389 | 390 | #[inline(always)] 391 | fn state(&self) -> &Mutex { 392 | &self.state 393 | } 394 | 395 | /// Get the name of the Coroutine 396 | #[inline(always)] 397 | pub fn name(&self) -> Option<&str> { 398 | self.name.as_ref().map(|s| &**s) 399 | } 400 | 401 | /// Determines whether the current Coroutine is unwinding because of panic. 402 | #[inline(always)] 403 | pub fn panicking(&self) -> bool { 404 | *self.state().lock() == State::Panicked 405 | } 406 | 407 | /// Determines whether the Coroutine is finished 408 | #[inline(always)] 409 | pub fn finished(&self) -> bool { 410 | *self.state().lock() == State::Finished 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /src/coroutine/asymmetric.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Rustcc Developers 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the "Software"), 7 | // to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | // and/or sell copies of the Software, and to permit persons to whom the 10 | // Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | // DEALINGS IN THE SOFTWARE. 22 | 23 | use std::iter::Iterator; 24 | use std::mem::transmute; 25 | use std::cell::UnsafeCell; 26 | use std::default::Default; 27 | use std::ops::DerefMut; 28 | use std::fmt; 29 | use std::rt::unwind::try; 30 | use std::rt::unwind::begin_unwind; 31 | 32 | use context::stack::StackPool; 33 | use context::thunk::Thunk; 34 | 35 | use options::Options; 36 | 37 | use coroutine::raw; 38 | 39 | use Result; 40 | 41 | thread_local!(static STACK_POOL: UnsafeCell = UnsafeCell::new(StackPool::new())); 42 | 43 | struct ForceUnwind; 44 | 45 | /// Initialization function for make context 46 | extern "C" fn coroutine_initialize(_: usize, f: *mut ()) -> ! { 47 | let func: Box = unsafe { transmute(f) }; 48 | func.invoke(()); 49 | loop {} 50 | } 51 | 52 | #[derive(Debug, Copy, Clone)] 53 | enum State { 54 | Created, 55 | Running, 56 | Finished, 57 | ForceUnwind, 58 | } 59 | 60 | #[allow(raw_pointer_derive)] 61 | #[derive(Debug)] 62 | struct CoroutineImpl 63 | where T: Send, 64 | F: FnMut(CoroutineRef) 65 | { 66 | parent: raw::Coroutine, 67 | raw_impl: Option, 68 | 69 | name: Option, 70 | function: F, 71 | state: State, 72 | 73 | result: Option>>, 74 | } 75 | 76 | unsafe impl Send for CoroutineImpl 77 | where T: Send, 78 | F: FnMut(CoroutineRef) 79 | {} 80 | 81 | impl fmt::Display for CoroutineImpl 82 | where T: Send, 83 | F: FnMut(CoroutineRef) 84 | { 85 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 86 | write!(f, "Coroutine({})", self.name.as_ref() 87 | .map(|s| &s[..]) 88 | .unwrap_or("")) 89 | } 90 | } 91 | 92 | impl CoroutineImpl 93 | where T: Send, 94 | F: FnMut(CoroutineRef) 95 | { 96 | unsafe fn yield_back(&mut self) -> Option { 97 | self.raw_impl.as_mut().unwrap().yield_to(&self.parent); 98 | 99 | if let State::ForceUnwind = self.state { 100 | // Begin unwind to force memory reclain, no body cares what the file and line is 101 | begin_unwind(ForceUnwind, &(file!(), line!())); 102 | } 103 | 104 | match self.result.take() { 105 | None => None, 106 | Some(Ok(x)) => (*x).take(), 107 | _ => unreachable!("Coroutine is panicking"), 108 | } 109 | } 110 | 111 | unsafe fn resume(&mut self) -> Result> { 112 | self.parent.yield_to(&self.raw_impl.as_ref().unwrap()); 113 | match self.result.take() { 114 | None => Ok(None), 115 | Some(Ok(x)) => Ok((*x).take()), 116 | Some(Err(err)) => Err(err), 117 | } 118 | } 119 | 120 | pub fn name(&self) -> Option<&str> { 121 | self.name.as_ref().map(|s| &s[..]) 122 | } 123 | 124 | fn take_data(&mut self) -> Option { 125 | match self.result.take() { 126 | None => None, 127 | Some(Ok(x)) => unsafe { (*x).take() }, 128 | _ => unreachable!("Coroutine is panicking") 129 | } 130 | } 131 | 132 | unsafe fn yield_with(&mut self, data: T) -> Option { 133 | self.result = Some(Ok(&mut Some(data))); 134 | self.yield_back() 135 | } 136 | 137 | unsafe fn resume_with(&mut self, data: T) -> Result> { 138 | self.result = Some(Ok(&mut Some(data))); 139 | self.resume() 140 | } 141 | 142 | unsafe fn force_unwind(&mut self) { 143 | if let State::Running = self.state { 144 | self.state = State::ForceUnwind; 145 | let _ = try(|| { let _ = self.resume(); }); 146 | } 147 | } 148 | } 149 | 150 | impl Drop for CoroutineImpl 151 | where T: Send, 152 | F: FnMut(CoroutineRef) 153 | { 154 | fn drop(&mut self) { 155 | unsafe { 156 | self.force_unwind(); 157 | } 158 | STACK_POOL.with(|pool| unsafe { 159 | if let Some(stack) = self.raw_impl.take().unwrap().take_stack() { 160 | (&mut *pool.get()).give_stack(stack); 161 | } 162 | }); 163 | } 164 | } 165 | 166 | pub struct Coroutine 167 | where T: Send + 'static, 168 | F: FnMut(CoroutineRef) 169 | { 170 | coro: UnsafeCell>>, 171 | } 172 | 173 | impl fmt::Debug for Coroutine 174 | where T: Send, 175 | F: FnMut(CoroutineRef) 176 | { 177 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 178 | write!(f, "{:?}", self.coro.get()) 179 | } 180 | } 181 | 182 | impl Coroutine 183 | where T: Send, 184 | F: FnMut(CoroutineRef) 185 | { 186 | #[inline] 187 | pub fn spawn_opts(f: F, opts: Options) -> Coroutine { 188 | let mut stack = STACK_POOL.with(|pool| unsafe { 189 | (&mut *pool.get()).take_stack(opts.stack_size) 190 | }); 191 | 192 | let mut coro = Box::new(CoroutineImpl { 193 | parent: unsafe { raw::Coroutine::empty() }, 194 | raw_impl: unsafe { Some(raw::Coroutine::empty()) }, 195 | name: opts.name, 196 | function: f, 197 | state: State::Created, 198 | result: None, 199 | }); 200 | 201 | let coro_ref: &mut CoroutineImpl = unsafe { 202 | let ptr: *mut CoroutineImpl = coro.deref_mut(); 203 | &mut *ptr 204 | }; 205 | 206 | let puller_ref = CoroutineRef { 207 | coro: coro_ref 208 | }; 209 | 210 | // Coroutine function wrapper 211 | // Responsible for calling the function and dealing with panicking 212 | let wrapper = move|| -> ! { 213 | let ret = unsafe { 214 | let puller_ref = puller_ref.clone(); 215 | try(|| { 216 | let coro_ref: &mut CoroutineImpl = &mut *puller_ref.coro; 217 | coro_ref.state = State::Running; 218 | (coro_ref.function)(puller_ref) 219 | }) 220 | }; 221 | 222 | unsafe { 223 | let coro_ref: &mut CoroutineImpl = &mut *puller_ref.coro; 224 | coro_ref.state = State::Finished; 225 | } 226 | 227 | let is_panicked = match ret { 228 | Ok(..) => false, 229 | Err(err) => { 230 | if let None = err.downcast_ref::() { 231 | { 232 | use std::io::stderr; 233 | use std::io::Write; 234 | let msg = match err.downcast_ref::<&'static str>() { 235 | Some(s) => *s, 236 | None => match err.downcast_ref::() { 237 | Some(s) => &s[..], 238 | None => "Box", 239 | } 240 | }; 241 | 242 | let name = coro_ref.name().unwrap_or(""); 243 | let _ = writeln!(&mut stderr(), "Coroutine '{}' panicked at '{}'", name, msg); 244 | } 245 | 246 | coro_ref.result = Some(Err(::Error::Panicking(err))); 247 | true 248 | } else { 249 | false 250 | } 251 | } 252 | }; 253 | 254 | loop { 255 | if is_panicked { 256 | coro_ref.result = Some(Err(::Error::Panicked)); 257 | } 258 | 259 | unsafe { 260 | coro_ref.yield_back(); 261 | } 262 | } 263 | }; 264 | 265 | { 266 | let coro_ref = coro.raw_impl.as_mut().unwrap(); 267 | coro_ref.context_mut().init_with(coroutine_initialize, 0, wrapper, &mut stack); 268 | coro_ref.set_stack(stack); 269 | } 270 | 271 | Coroutine { 272 | coro: UnsafeCell::new(coro) 273 | } 274 | } 275 | 276 | #[inline] 277 | pub fn spawn(f: F) -> Coroutine { 278 | Coroutine::spawn_opts(f, Default::default()) 279 | } 280 | 281 | #[inline] 282 | pub fn name(&self) -> Option<&str> { 283 | unsafe { 284 | (&*self.coro.get()).name() 285 | } 286 | } 287 | 288 | #[inline] 289 | pub fn resume(&self) -> Result> { 290 | unsafe { 291 | (&mut *self.coro.get()).resume() 292 | } 293 | } 294 | 295 | #[inline] 296 | pub fn resume_with(&self, data: T) -> Result> { 297 | unsafe { 298 | (&mut *self.coro.get()).resume_with(data) 299 | } 300 | } 301 | } 302 | 303 | pub struct CoroutineRef 304 | where T: Send, 305 | F: FnMut(CoroutineRef) 306 | { 307 | coro: *mut CoroutineImpl, 308 | } 309 | 310 | impl Copy for CoroutineRef 311 | where T: Send, 312 | F: FnMut(CoroutineRef) 313 | {} 314 | 315 | impl Clone for CoroutineRef 316 | where T: Send, 317 | F: FnMut(CoroutineRef) 318 | { 319 | fn clone(&self) -> CoroutineRef { 320 | CoroutineRef { 321 | coro: self.coro, 322 | } 323 | } 324 | } 325 | 326 | unsafe impl Send for CoroutineRef 327 | where T: Send, 328 | F: FnMut(CoroutineRef) 329 | {} 330 | 331 | impl CoroutineRef 332 | where T: Send, 333 | F: FnMut(CoroutineRef) 334 | { 335 | #[inline] 336 | pub fn yield_back(&self) -> Option { 337 | unsafe { 338 | let coro: &mut CoroutineImpl = transmute(self.coro); 339 | coro.yield_back() 340 | } 341 | } 342 | 343 | #[inline] 344 | pub fn yield_with(&self, data: T) -> Option { 345 | unsafe { 346 | let coro: &mut CoroutineImpl = transmute(self.coro); 347 | coro.yield_with(data) 348 | } 349 | } 350 | 351 | #[inline] 352 | pub fn name(&self) -> Option<&str> { 353 | unsafe { 354 | (&*self.coro).name() 355 | } 356 | } 357 | 358 | #[inline] 359 | pub fn take_data(&self) -> Option { 360 | unsafe { 361 | let coro: &mut CoroutineImpl = transmute(self.coro); 362 | coro.take_data() 363 | } 364 | } 365 | } 366 | 367 | impl Iterator for Coroutine 368 | where T: Send, 369 | F: FnMut(CoroutineRef) 370 | { 371 | type Item = Result; 372 | 373 | fn next(&mut self) -> Option> { 374 | match self.resume() { 375 | Ok(r) => r.map(|x| Ok(x)), 376 | Err(err) => Some(Err(err)), 377 | } 378 | } 379 | } 380 | 381 | #[cfg(test)] 382 | mod test { 383 | use super::*; 384 | 385 | #[test] 386 | fn test_coroutine_basic() { 387 | let coro = Coroutine::spawn(|me| { 388 | for num in 0..10 { 389 | me.yield_with(num); 390 | } 391 | }); 392 | 393 | for num in 0..10 { 394 | assert_eq!(Some(num), coro.resume().unwrap()); 395 | } 396 | } 397 | 398 | #[test] 399 | fn test_coroutine_generator() { 400 | let generator = Coroutine::spawn(|me| { 401 | for num in 0..10 { 402 | me.yield_with(num); 403 | } 404 | }); 405 | 406 | for (actual, num) in generator.enumerate() { 407 | assert_eq!(actual, num.unwrap()); 408 | } 409 | } 410 | 411 | #[test] 412 | fn test_panic_inside() { 413 | let will_panic: Coroutine<_, ()> = Coroutine::spawn(|_| { 414 | panic!("Panic inside"); 415 | }); 416 | 417 | assert!(will_panic.resume().is_err()); 418 | } 419 | 420 | #[test] 421 | fn test_coroutine_push() { 422 | let coro = Coroutine::spawn(|me| { 423 | assert_eq!(Some(0), me.take_data()); 424 | 425 | for num in 1..10 { 426 | assert_eq!(Some(num), me.yield_back()); 427 | } 428 | }); 429 | 430 | for num in 0..10 { 431 | coro.resume_with(num).unwrap(); 432 | } 433 | } 434 | 435 | #[test] 436 | fn test_coroutine_pull_struct() { 437 | #[derive(Eq, PartialEq, Debug)] 438 | struct Foo(i32); 439 | 440 | let coro = Coroutine::spawn(|me| { 441 | for num in 0..10 { 442 | me.yield_with(Foo(num)); 443 | } 444 | }); 445 | 446 | for num in 0..10 { 447 | assert_eq!(Some(Foo(num)), coro.resume().unwrap()); 448 | } 449 | } 450 | 451 | #[test] 452 | fn test_coroutine_mut() { 453 | let mut outer = 0; 454 | 455 | { 456 | let coro = Coroutine::spawn(|me| { 457 | loop { 458 | outer += 1; 459 | me.yield_with(outer); 460 | } 461 | }); 462 | 463 | for _ in 0..10 { 464 | coro.resume().unwrap(); 465 | } 466 | } 467 | 468 | assert_eq!(outer, 10); 469 | } 470 | 471 | #[test] 472 | fn test_coroutine_unwind() { 473 | let mut counter = 0; 474 | 475 | { 476 | let coro: Coroutine<_, ()> = Coroutine::spawn(|me| { 477 | struct Guard<'a>(&'a mut i32); 478 | 479 | impl<'a> Drop for Guard<'a> { 480 | fn drop(&mut self) { 481 | *self.0 += 1; 482 | } 483 | } 484 | 485 | let _guard = Guard(&mut counter); 486 | *_guard.0 += 1; 487 | me.yield_back(); 488 | }); 489 | 490 | coro.resume().unwrap(); 491 | } 492 | 493 | assert_eq!(counter, 2); 494 | } 495 | } 496 | 497 | #[cfg(test)] 498 | mod benchmark { 499 | use super::*; 500 | 501 | use test::Bencher; 502 | 503 | #[bench] 504 | fn bench_coroutine_spawn(b: &mut Bencher) { 505 | b.iter(|| { 506 | let _: Coroutine<_, ()> = Coroutine::spawn(move|_| {}); 507 | }); 508 | } 509 | 510 | #[bench] 511 | fn bench_context_switch(b: &mut Bencher) { 512 | let coro: Coroutine<_, ()> = Coroutine::spawn(|me| loop { me.yield_back(); }); 513 | 514 | b.iter(|| coro.resume()); 515 | } 516 | } 517 | -------------------------------------------------------------------------------- /src/asymmetric.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Y. T. Chung 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the "Software"), 7 | // to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | // and/or sell copies of the Software, and to permit persons to whom the 10 | // Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | // DEALINGS IN THE SOFTWARE. 22 | 23 | //! Asymmetric coroutines 24 | 25 | use std::fmt; 26 | use std::usize; 27 | use std::panic; 28 | use std::mem; 29 | use std::iter::Iterator; 30 | use std::any::Any; 31 | 32 | use context::{Context, Transfer}; 33 | use context::stack::ProtectedFixedSizeStack; 34 | 35 | use options::Options; 36 | 37 | #[derive(Debug)] 38 | struct ForceUnwind; 39 | 40 | 41 | trait FnBox { 42 | fn call_box(self: Box, meta_ref: &mut Coroutine, data: usize) -> usize; 43 | } 44 | 45 | 46 | impl usize> FnBox for F { 47 | fn call_box(self: Box, meta_ref: &mut Coroutine, data: usize) -> usize { 48 | (*self)(meta_ref, data) 49 | } 50 | } 51 | 52 | type Thunk<'a> = Box; 53 | 54 | struct InitData { 55 | stack: ProtectedFixedSizeStack, 56 | callback: Thunk<'static>, 57 | } 58 | 59 | extern "C" fn coroutine_entry(t: Transfer) -> ! { 60 | // Take over the data from Coroutine::spawn_opts 61 | let InitData { stack, callback } = unsafe { 62 | let data_opt_ref = &mut *(t.data as *mut Option); 63 | data_opt_ref.take().expect("failed to acquire InitData") 64 | }; 65 | 66 | // This block will ensure the `meta` will be destroied before dropping the stack 67 | let (ctx, result) = { 68 | let mut meta = Coroutine { 69 | context: None, 70 | name: None, 71 | state: State::Suspended, 72 | panicked_error: None, 73 | }; 74 | 75 | // Yield back after take out the callback function 76 | // Now the Coroutine is initialized 77 | let meta_ptr = &mut meta as *mut _ as usize; 78 | let result = unsafe { 79 | ::try(move || { 80 | let Transfer { context, data } = t.context.resume(meta_ptr); 81 | let meta_ref = &mut *(meta_ptr as *mut Coroutine); 82 | meta_ref.context = Some(context); 83 | 84 | // Take out the callback and run it 85 | // let result = callback.call_box((meta_ref, data)); 86 | let result = callback.call_box(meta_ref, data); 87 | 88 | trace!("Coroutine `{}`: returned from callback with result {}", 89 | meta_ref.debug_name(), 90 | result); 91 | result 92 | }) 93 | }; 94 | 95 | let mut loc_data = match result { 96 | Ok(d) => { 97 | meta.state = State::Finished; 98 | d 99 | } 100 | Err(err) => { 101 | if err.is::() { 102 | meta.state = State::Finished 103 | } else { 104 | meta.state = State::Panicked; 105 | meta.panicked_error = Some(err); 106 | } 107 | usize::MAX 108 | } 109 | }; 110 | 111 | trace!("Coroutine `{}`: exited with {:?}", 112 | meta.debug_name(), 113 | meta.state); 114 | 115 | loop { 116 | let Transfer { context, data } = meta.context.take().unwrap().resume(loc_data); 117 | meta.context = Some(context); 118 | loc_data = data; 119 | 120 | if meta.state == State::Finished { 121 | break; 122 | } 123 | } 124 | 125 | trace!("Coroutine `{}`: finished => dropping stack", 126 | meta.debug_name()); 127 | 128 | // If panicked inside, the meta.context stores the actual return Context 129 | (meta.take_context(), loc_data) 130 | }; 131 | 132 | // Drop the stack after it is finished 133 | let mut stack_opt = Some((stack, result)); 134 | ctx.resume_ontop(&mut stack_opt as *mut _ as usize, coroutine_exit); 135 | 136 | unreachable!(); 137 | } 138 | 139 | extern "C" fn coroutine_exit(mut t: Transfer) -> Transfer { 140 | let data = unsafe { 141 | // Drop the stack 142 | let stack_ref = &mut *(t.data as *mut Option<(ProtectedFixedSizeStack, usize)>); 143 | let (_, result) = stack_ref.take().unwrap(); 144 | result 145 | }; 146 | 147 | t.data = data; 148 | t.context = unsafe { mem::transmute(0usize) }; 149 | t 150 | } 151 | 152 | extern "C" fn coroutine_unwind(t: Transfer) -> Transfer { 153 | // Save the Context in the Coroutine object 154 | // because the `t` won't be able to be passed to the caller 155 | let coro = unsafe { &mut *(t.data as *mut Coroutine) }; 156 | 157 | coro.context = Some(t.context); 158 | 159 | trace!("Coroutine `{}`: unwinding", coro.debug_name()); 160 | panic::resume_unwind(Box::new(ForceUnwind)); 161 | } 162 | 163 | /// Coroutine state 164 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 165 | pub enum State { 166 | /// Suspended state (yield from coroutine inside, ready for resume). 167 | Suspended, 168 | /// Running state (executing in callback). 169 | Running, 170 | /// Parked state. Similar to `Suspended` state, but `Suspended` is representing that coroutine 171 | /// will be waken up (resume) by scheduler automatically. Coroutines in `Parked` state should 172 | /// be waken up manually. 173 | Parked, 174 | /// Coroutine is finished and internal data has been destroyed. 175 | Finished, 176 | /// Coroutine is panicked inside. 177 | Panicked, 178 | } 179 | 180 | /// Coroutine context representation 181 | #[derive(Debug)] 182 | pub struct Coroutine { 183 | context: Option, 184 | name: Option, 185 | state: State, 186 | panicked_error: Option>, 187 | } 188 | 189 | impl Coroutine { 190 | /// Spawn a coroutine with `Options` 191 | #[inline] 192 | pub fn spawn_opts(f: F, opts: Options) -> Handle 193 | where F: FnOnce(&mut Coroutine, usize) -> usize + 'static 194 | { 195 | Self::spawn_opts_impl(Box::new(f) as Thunk<'static>, opts) 196 | } 197 | 198 | /// Spawn a coroutine with default options 199 | #[inline] 200 | pub fn spawn(f: F) -> Handle 201 | where F: FnOnce(&mut Coroutine, usize) -> usize + 'static 202 | { 203 | Self::spawn_opts_impl(Box::new(f), Options::default()) 204 | } 205 | 206 | fn spawn_opts_impl(f: Thunk<'static>, opts: Options) -> Handle { 207 | let data = InitData { 208 | stack: ProtectedFixedSizeStack::new(opts.stack_size).expect("failed to acquire stack"), 209 | callback: f, 210 | }; 211 | 212 | let context = Context::new(&data.stack, coroutine_entry); 213 | 214 | // Give him the initialization data 215 | let mut data_opt = Some(data); 216 | let t = context.resume(&mut data_opt as *mut _ as usize); 217 | debug_assert!(data_opt.is_none()); 218 | 219 | let coro_ref = unsafe { &mut *(t.data as *mut Coroutine) }; 220 | coro_ref.context = Some(t.context); 221 | 222 | if let Some(name) = opts.name { 223 | coro_ref.set_name(name); 224 | } 225 | 226 | // Done! 227 | Handle(coro_ref) 228 | } 229 | 230 | fn take_context(&mut self) -> Context { 231 | self.context.take().unwrap() 232 | } 233 | 234 | /// Gets state of Coroutine 235 | #[inline] 236 | pub fn state(&self) -> State { 237 | self.state 238 | } 239 | 240 | /// Gets name of Coroutine 241 | #[inline] 242 | pub fn name(&self) -> Option<&String> { 243 | self.name.as_ref() 244 | } 245 | 246 | /// Set name of Coroutine 247 | #[inline] 248 | pub fn set_name(&mut self, name: String) { 249 | self.name = Some(name); 250 | } 251 | 252 | /// Name for debugging 253 | #[inline] 254 | pub fn debug_name(&self) -> String { 255 | match self.name { 256 | Some(ref name) => name.clone(), 257 | None => format!("{:p}", self), 258 | } 259 | } 260 | 261 | #[inline(never)] 262 | fn inner_yield_with_state(&mut self, state: State, data: usize) -> usize { 263 | let context = self.take_context(); 264 | 265 | trace!("Coroutine `{}`: yielding to {:?}", 266 | self.debug_name(), 267 | &context); 268 | 269 | self.state = state; 270 | 271 | let Transfer { context, data } = context.resume(data); 272 | 273 | if unsafe { mem::transmute_copy::<_, usize>(&context) } != 0usize { 274 | self.context = Some(context); 275 | } 276 | data 277 | } 278 | 279 | #[inline] 280 | fn yield_with_state(&mut self, state: State, data: usize) -> ::Result { 281 | let data = self.inner_yield_with_state(state, data); 282 | 283 | if self.state() == State::Panicked { 284 | match self.panicked_error.take() { 285 | Some(err) => Err(::Error::Panicking(err)), 286 | None => Err(::Error::Panicked), 287 | } 288 | } else { 289 | Ok(data) 290 | } 291 | } 292 | 293 | /// Yield the current coroutine with `Suspended` state 294 | #[inline] 295 | pub fn yield_with(&mut self, data: usize) -> usize { 296 | self.inner_yield_with_state(State::Suspended, data) 297 | } 298 | 299 | /// Yield the current coroutine with `Parked` state 300 | #[inline] 301 | pub fn park_with(&mut self, data: usize) -> usize { 302 | self.inner_yield_with_state(State::Parked, data) 303 | } 304 | 305 | fn force_unwind(&mut self) { 306 | trace!("Coroutine `{}`: force unwinding", self.debug_name()); 307 | 308 | let ctx = self.take_context(); 309 | let Transfer { context, .. } = 310 | ctx.resume_ontop(self as *mut Coroutine as usize, coroutine_unwind); 311 | self.context = Some(context); 312 | 313 | trace!("Coroutine `{}`: force unwound", self.debug_name()); 314 | } 315 | } 316 | 317 | /// Handle for a Coroutine 318 | #[derive(Eq, PartialEq)] 319 | pub struct Handle(*mut Coroutine); 320 | 321 | impl Handle { 322 | #[doc(hidden)] 323 | #[inline] 324 | pub fn into_raw(self) -> *mut Coroutine { 325 | let coro = self.0; 326 | mem::forget(self); 327 | coro 328 | } 329 | 330 | #[doc(hidden)] 331 | #[inline] 332 | pub unsafe fn from_raw(coro: *mut Coroutine) -> Handle { 333 | assert!(!coro.is_null()); 334 | Handle(coro) 335 | } 336 | 337 | /// Check if the Coroutine is already finished 338 | #[inline] 339 | pub fn is_finished(&self) -> bool { 340 | match self.state() { 341 | State::Finished | State::Panicked => true, 342 | _ => false, 343 | } 344 | } 345 | 346 | #[inline] 347 | fn yield_with_state(&mut self, state: State, data: usize) -> ::Result { 348 | let coro = unsafe { &mut *self.0 }; 349 | coro.yield_with_state(state, data) 350 | } 351 | 352 | /// Resume the Coroutine 353 | #[inline] 354 | pub fn resume(&mut self, data: usize) -> ::Result { 355 | assert!(!self.is_finished()); 356 | self.yield_with_state(State::Running, data) 357 | } 358 | 359 | /// Gets state of Coroutine 360 | #[inline] 361 | pub fn state(&self) -> State { 362 | let coro = unsafe { &*self.0 }; 363 | coro.state() 364 | } 365 | 366 | /// Gets name of Coroutine 367 | #[inline] 368 | pub fn name(&self) -> Option<&String> { 369 | let coro = unsafe { &*self.0 }; 370 | coro.name() 371 | } 372 | 373 | /// Set name of Coroutine 374 | #[inline] 375 | pub fn set_name(&mut self, name: String) { 376 | let coro = unsafe { &mut *self.0 }; 377 | coro.set_name(name) 378 | } 379 | 380 | /// Name for debugging 381 | #[inline] 382 | pub fn debug_name(&self) -> String { 383 | let coro = unsafe { &*self.0 }; 384 | coro.debug_name() 385 | } 386 | } 387 | 388 | impl Drop for Handle { 389 | fn drop(&mut self) { 390 | trace!("Coroutine `{}`: dropping with {:?}", 391 | self.debug_name(), 392 | self.state()); 393 | 394 | let coro = unsafe { &mut *self.0 }; 395 | 396 | if !self.is_finished() { 397 | coro.force_unwind() 398 | } 399 | 400 | coro.inner_yield_with_state(State::Finished, 0); 401 | } 402 | } 403 | 404 | impl fmt::Debug for Handle { 405 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 406 | if self.is_finished() { 407 | write!(f, "Coroutine(None, Finished)") 408 | } else { 409 | write!(f, 410 | "Coroutine(Some({}), {:?})", 411 | self.debug_name(), 412 | self.state()) 413 | } 414 | } 415 | } 416 | 417 | impl Iterator for Handle { 418 | type Item = ::Result; 419 | fn next(&mut self) -> Option { 420 | if self.is_finished() { 421 | None 422 | } else { 423 | let x = self.resume(0); 424 | Some(x) 425 | } 426 | } 427 | } 428 | 429 | #[cfg(test)] 430 | mod test { 431 | use super::*; 432 | 433 | #[test] 434 | fn generator() { 435 | let coro = Coroutine::spawn(|coro, _| { 436 | for i in 0..10 { 437 | coro.yield_with(i); 438 | } 439 | 10 440 | }); 441 | 442 | let ret = coro.map(|x| x.unwrap()).collect::>(); 443 | assert_eq!(&ret[..], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 444 | } 445 | 446 | #[test] 447 | fn yield_data() { 448 | let mut coro = Coroutine::spawn(|coro, data| coro.yield_with(data)); 449 | 450 | assert_eq!(coro.resume(0).unwrap(), 0); 451 | assert_eq!(coro.resume(1).unwrap(), 1); 452 | assert!(coro.is_finished()); 453 | } 454 | 455 | #[test] 456 | fn force_unwinding() { 457 | use std::sync::Arc; 458 | use std::sync::atomic::{AtomicUsize, Ordering}; 459 | 460 | struct Guard { 461 | inner: Arc, 462 | } 463 | 464 | impl Drop for Guard { 465 | fn drop(&mut self) { 466 | self.inner.fetch_add(1, Ordering::SeqCst); 467 | } 468 | } 469 | 470 | let orig = Arc::new(AtomicUsize::new(0)); 471 | 472 | { 473 | let pass = orig.clone(); 474 | let mut coro = Coroutine::spawn(move |coro, _| { 475 | let _guard = Guard { inner: pass.clone() }; 476 | coro.yield_with(0); 477 | let _guard2 = Guard { inner: pass }; 478 | 0 479 | }); 480 | 481 | let _ = coro.resume(0); 482 | // Let it drop 483 | } 484 | 485 | assert_eq!(orig.load(Ordering::SeqCst), 1); 486 | } 487 | 488 | #[test] 489 | fn unwinding() { 490 | use std::sync::Arc; 491 | use std::sync::atomic::{AtomicUsize, Ordering}; 492 | 493 | struct Guard { 494 | inner: Arc, 495 | } 496 | 497 | impl Drop for Guard { 498 | fn drop(&mut self) { 499 | self.inner.fetch_add(1, Ordering::SeqCst); 500 | } 501 | } 502 | 503 | let orig = Arc::new(AtomicUsize::new(0)); 504 | 505 | { 506 | let pass = orig.clone(); 507 | let mut coro = Coroutine::spawn(move |_, _| { 508 | let _guard = Guard { inner: pass.clone() }; 509 | panic!("111"); 510 | }); 511 | 512 | let _ = coro.resume(0); 513 | // Let it drop 514 | } 515 | 516 | assert_eq!(orig.load(Ordering::SeqCst), 1); 517 | } 518 | 519 | #[test] 520 | #[should_panic] 521 | fn resume_after_finished() { 522 | let mut coro = Coroutine::spawn(|_, _| 0); 523 | let _ = coro.resume(0); 524 | let _ = coro.resume(0); 525 | } 526 | 527 | #[test] 528 | fn state() { 529 | let mut coro = Coroutine::spawn(|coro, _| { 530 | coro.yield_with(0); 531 | coro.park_with(0); 532 | 0 533 | }); 534 | 535 | assert_eq!(coro.state(), State::Suspended); 536 | let _ = coro.resume(0); 537 | assert_eq!(coro.state(), State::Suspended); 538 | let _ = coro.resume(0); 539 | assert_eq!(coro.state(), State::Parked); 540 | let _ = coro.resume(0); 541 | assert_eq!(coro.state(), State::Finished); 542 | } 543 | 544 | #[test] 545 | fn panicking() { 546 | let mut coro = Coroutine::spawn(|_, _| { 547 | panic!(1010); 548 | }); 549 | 550 | let result = coro.resume(0); 551 | println!("{:?} {:?}", result, coro.state()); 552 | assert!(result.is_err()); 553 | 554 | let err = result.unwrap_err(); 555 | 556 | match err { 557 | ::Error::Panicking(err) => { 558 | assert!(err.is::()); 559 | } 560 | _ => unreachable!(), 561 | } 562 | } 563 | } 564 | --------------------------------------------------------------------------------