generator-0.7.1/.cargo_vcs_info.json0000644000000001360000000000100130270ustar { "git": { "sha1": "737495ada9bc5b368857c10f4b574bc60e6b58c8" }, "path_in_vcs": "" }generator-0.7.1/.github/workflows/rust.yml000064400000000000000000000026220072674642500167660ustar 00000000000000name: CI on: push: paths-ignore: - '**.md' pull_request: paths-ignore: - '**.md' workflow_dispatch: env: CARGO_TERM_COLOR: always jobs: lints: name: Run cargo fmt and cargo clippy runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true components: rustfmt, clippy - name: Run cargo clippy uses: actions-rs/cargo@v1 with: command: clippy args: -- -D warnings test: name: Test ${{ matrix.rust }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: rust: - stable - nightly os: - ubuntu-latest - windows-latest - macOS-latest steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install Rust (${{ matrix.rust }}) uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true - name: Run cargo test uses: actions-rs/cargo@v1 with: command: test - name: Run cargo release test uses: actions-rs/cargo@v1 with: command: test args: --release generator-0.7.1/Cargo.lock0000644000000047610000000000100110120ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "cc" version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "generator" version = "0.7.1" dependencies = [ "cc", "libc", "log", "rustversion", "windows", ] [[package]] name = "libc" version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "rustversion" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24c8ad4f0c00e1eb5bc7614d236a7f1300e3dbd76b68cac8e06fb00b015ad8d8" [[package]] name = "windows" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec" dependencies = [ "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_msvc" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" [[package]] name = "windows_i686_gnu" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" [[package]] name = "windows_i686_msvc" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" [[package]] name = "windows_x86_64_gnu" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" [[package]] name = "windows_x86_64_msvc" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" generator-0.7.1/Cargo.toml0000644000000027700000000000100110330ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "generator" version = "0.7.1" authors = ["Xudong Huang "] build = "build.rs" exclude = [ ".gitignore", ".travis.yml", "appveyor.yml", "benches/**/*", ] description = "Stackfull Generator Library in Rust" homepage = "https://github.com/Xudong-Huang/generator-rs.git" documentation = "https://docs.rs/generator" readme = "README.md" keywords = [ "generator", "coroutine", "green", "thread", "fiber", ] categories = [ "data-structures", "algorithms", ] license = "MIT/Apache-2.0" repository = "https://github.com/Xudong-Huang/generator-rs.git" [profile.release] lto = true [dependencies.log] version = "0.4" [build-dependencies.cc] version = "1.0" [build-dependencies.rustversion] version = "1.0" [target."cfg(unix)".dependencies.libc] version = "0.2" [target."cfg(windows)".dependencies.windows] version = "0.32" features = [ "Win32_System_Memory", "Win32_Foundation", "Win32_System_SystemInformation", "Win32_System_Diagnostics_Debug", ] generator-0.7.1/Cargo.toml.orig000064400000000000000000000017360072674642500145450ustar 00000000000000[package] name = "generator" version = "0.7.1" edition = "2018" authors = ["Xudong Huang "] license = "MIT/Apache-2.0" repository = "https://github.com/Xudong-Huang/generator-rs.git" homepage = "https://github.com/Xudong-Huang/generator-rs.git" documentation = "https://docs.rs/generator" description = "Stackfull Generator Library in Rust" readme = "README.md" keywords = ["generator", "coroutine", "green", "thread", "fiber"] categories = ["data-structures", "algorithms"] build = "build.rs" exclude = [ ".gitignore", ".travis.yml", "appveyor.yml", "benches/**/*", ] [target.'cfg(windows)'.dependencies.windows] version = "0.32" features = [ "Win32_System_Memory", "Win32_Foundation", "Win32_System_SystemInformation", "Win32_System_Diagnostics_Debug" ] [target.'cfg(unix)'.dependencies] libc = "0.2" [dependencies] log = "0.4" [build-dependencies] cc = "1.0" rustversion = "1.0" # release build [profile.release] lto = true generator-0.7.1/LICENSE-APACHE000064400000000000000000000261330072674642500136000ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. generator-0.7.1/LICENSE-MIT000064400000000000000000000020370072674642500133050ustar 00000000000000Copyright (c) 2017 Xudong Huang Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.generator-0.7.1/README.md000064400000000000000000000033220072674642500131260ustar 00000000000000[![Build Status](https://github.com/Xudong-Huang/generator-rs/workflows/CI/badge.svg)](https://github.com/Xudong-Huang/generator-rs/actions?query=workflow%3ACI) [![Current Crates.io Version](https://img.shields.io/crates/v/generator.svg)](https://crates.io/crates/generator) [![Document](https://img.shields.io/badge/doc-generator-green.svg)](https://docs.rs/generator) # Generator-rs rust stackful generator library ```toml [dependencies] generator = "0.7" ``` ## Usage ```rust use generator::{done, Gn}; fn main() { let g = Gn::new_scoped(|mut s| { let (mut a, mut b) = (0, 1); while b < 200 { std::mem::swap(&mut a, &mut b); b = a + b; s.yield_(b); } done!(); }); for i in g { println!("{}", i); } } ``` ## Output ``` 1 2 3 5 8 13 21 34 55 89 144 233 ``` ## Goals - [x] basic send/yield with message support - [x] generator cancel support - [x] yield_from support - [x] panic inside generator support - [x] stack size tune support - [x] scoped static type support - [x] basic coroutine interface support - [x] stable rust support ## based on this basic library - we can easily port python library based on generator into rust - coroutine framework running on multi thread ## Notices * This crate supports below platforms, welcome to contribute with other arch and platforms - x86_64 Linux - x86_64 MacOs - x86_64 Windows - aarch64 Linux ## License This project is licensed under either of the following, at your option: * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT License ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)generator-0.7.1/build.rs000064400000000000000000000040610072674642500133150ustar 00000000000000extern crate cc; use std::env; use std::path::PathBuf; fn main() { // Set cfg flags depending on release channel if NIGHTLY { println!("cargo:rustc-cfg=nightly"); } // for the stable build asm lib let target: String = env::var("TARGET").unwrap(); let is_win_gnu = target.ends_with("windows-gnu"); let is_win_msvc = target.ends_with("windows-msvc"); let is_win = is_win_gnu || is_win_msvc; let arch = match target.split('-').next().unwrap() { // "arm" | "armv7" | "armv7s" => "arm", "arm64" | "aarch64" => "aarch64", // "x86" | "i386" | "i486" | "i586" | "i686" => "i386", // "mips" | "mipsel" => "mips32", // "powerpc" => "ppc32", // "powerpc64" => "ppc64", "x86_64" => "x86_64", _ => { panic!("Unsupported architecture: {}", target); } }; let abi = match arch { "arm" | "aarch64" => "aapcs", "mips32" => "o32", _ => { if is_win { "ms" } else { "sysv" } } }; let format = if is_win { "pe" } else if target.contains("apple") { "macho" } else if target.ends_with("aix") { "xcoff" } else { "elf" }; let (asm, ext) = if is_win_msvc { if arch == "arm" { ("armasm", "asm") } else { ("masm", "asm") } } else if is_win_gnu { ("gas", "asm") } else { ("gas", "S") }; let mut path: PathBuf = "src/detail/asm".into(); let mut config = cc::Build::new(); if is_win_gnu { config.flag("-x").flag("assembler-with-cpp"); } let file_name: [&str; 11] = ["asm", "_", arch, "_", abi, "_", format, "_", asm, ".", ext]; let file_name = file_name.concat(); path.push(file_name); config.file(path.to_str().unwrap()); // create the static asm libary config.compile("libasm.a"); } #[rustversion::nightly] const NIGHTLY: bool = true; #[rustversion::not(nightly)] const NIGHTLY: bool = false; generator-0.7.1/examples/cd.rs000064400000000000000000000035150072674642500144250ustar 00000000000000use generator::*; #[derive(Debug)] enum Action { Play(&'static str), Stop, } #[derive(Debug, Clone, Copy, PartialEq)] enum State { Playing, Stopped, } use crate::Action::*; use crate::State::*; fn main() { let mut cd_player = Gn::new_scoped(|mut s| { let mut state = Stopped; loop { // println!("{:?}", *state); // in release mod without this there is bugs!!!!! (rustc 1.59.0 (9d1b2106e 2022-02-23)) std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::AcqRel); match state { Stopped => match s.get_yield() { Some(Play(t)) => { println!("I'm playing {}", t); state = Playing; } Some(Stop) => println!("I'm already stopped"), _ => unreachable!("some thing wrong"), }, Playing => match s.get_yield() { Some(Stop) => { println!("I'm stopped"); state = Stopped; } Some(Play(_)) => println!("should first stop"), _ => unreachable!("some thing wrong"), }, } s.yield_with(state); } }); for _ in 0..1000 { let ret = cd_player.send(Play("hello world")); assert_eq!(ret, Playing); let ret = cd_player.send(Play("hello another day")); assert_eq!(ret, Playing); let ret = cd_player.send(Stop); assert_eq!(ret, Stopped); let ret = cd_player.send(Stop); assert_eq!(ret, Stopped); let ret = cd_player.send(Play("hello another day")); assert_eq!(ret, Playing); let ret = cd_player.send(Stop); assert_eq!(ret, Stopped); } } generator-0.7.1/examples/fib.rs000064400000000000000000000004730072674642500145770ustar 00000000000000use generator::{done, Gn}; fn main() { let g = Gn::new_scoped(|mut s| { let (mut a, mut b) = (0, 1); while b < 200 { std::mem::swap(&mut a, &mut b); b += a; s.yield_(b); } done!(); }); for i in g { println!("{}", i); } } generator-0.7.1/examples/get_yield.rs000064400000000000000000000007110072674642500157770ustar 00000000000000#![allow(deprecated)] use generator::{get_yield, yield_with, Gn}; fn sum(a: u32) -> u32 { let mut sum = a; let mut recv: u32; while sum < 200 { recv = get_yield().unwrap(); yield_with(sum); sum += recv; } sum } fn main() { // we specify the send type is u32 let mut s = Gn::::new(|| sum(1)); let mut i = 1u32; while !s.is_done() { i = s.send(i); println!("{}", i); } } generator-0.7.1/examples/number.rs000064400000000000000000000010270072674642500153230ustar 00000000000000use generator::*; fn factors(n: u32) -> Generator<'static, (), u32> { Gn::new_scoped(move |mut s| { if n == 0 { return 0; } s.yield_with(1); for i in 2..n { if n % i == 0 { s.yield_with(i); } } done!(); }) } fn main() { for i in factors(28) { println!("{}", i); } (0..10000) .filter(|n| factors(*n).sum::() == *n) .fold((), |_, n| { println!("n = {}", n); }) } generator-0.7.1/examples/pipe.rs000064400000000000000000000015550072674642500147760ustar 00000000000000use generator::*; fn main() { // fn square<'a, T: Iterator + 'a>(input: T) -> impl Iterator + 'a { fn square<'a, T: Iterator + Send + 'a>(input: T) -> Generator<'a, (), u32> { Gn::new_scoped(|mut s| { for i in input { s.yield_with(i * i); } done!(); }) } // fn sum<'a, T: Iterator + 'a>(input: T) -> impl Iterator + 'a { fn sum<'a, T: Iterator + Send + 'a>(input: T) -> Generator<'a, (), u32> { Gn::new_scoped(|mut s| { let mut acc = 0; for i in input { acc += i; s.yield_with(acc); } done!(); }) } for (i, sum) in sum(square(0..20)).enumerate() { println!("square_sum_{:<2} = {:^4}", i, sum); } } generator-0.7.1/examples/range.rs000064400000000000000000000004620072674642500151310ustar 00000000000000use generator::{done, Gn}; fn main() { let n = 100000; let range = Gn::new_scoped(move |mut s| { let mut num = 0; while num < n { s.yield_(num); num += 1; } done!(); }); let sum: usize = range.sum(); println!("sum ={}", sum); } generator-0.7.1/examples/send.rs000064400000000000000000000014040072674642500147630ustar 00000000000000#![allow(deprecated)] use generator::{yield_, Gn}; use std::mem; fn sum(a: u32) -> u32 { let mut sum = a; let mut recv: u32; while sum < 200 { // println!("sum={} ", sum); recv = yield_(sum).unwrap(); // println!("recv={}", recv); sum += recv; } sum } fn main() { // we specify the send type is u32 let mut s = Gn::::new(|| sum(0)); // first start the generator assert_eq!(s.raw_send(None).unwrap(), 0); let mut cur = 1; let mut last = 1; while !s.is_done() { // println!("send={}", last); mem::swap(&mut cur, &mut last); cur = s.send(cur); // s += cur // println!("cur={} last={}", cur, last); println!("{}", cur); } } generator-0.7.1/examples/yield_from.rs000064400000000000000000000007140072674642500161660ustar 00000000000000#![allow(deprecated)] use generator::*; fn xrange(start: u32, end: u32) -> u32 { for i in start..end { yield_with(i); } done!(); } fn main() { let g1 = Gn::new(|| xrange(0, 10)); let g2 = Gn::new(|| xrange(10, 20)); let g = Gn::new_scoped(|mut s| { s.yield_from(g1); s.yield_from(g2); done!(); }); g.fold(0, |sum, x| { println!("i={}, sum={}", x, sum + x); sum + x }); } generator-0.7.1/src/detail/aarch64_unix.rs000064400000000000000000000032460072674642500165460ustar 00000000000000use crate::detail::align_down; use crate::reg_context::InitFn; use crate::stack::Stack; #[link(name = "asm", kind = "static")] extern "C" { pub fn bootstrap_green_task(); pub fn prefetch(data: *const usize); pub fn swap_registers(out_regs: *mut Registers, in_regs: *const Registers); } #[repr(C, align(16))] #[derive(Debug)] pub struct Registers { // We save the 13 callee-saved registers: // x19--x28, fp (x29), lr (x30), sp // and the 8 callee-saved floating point registers: // v8--v15 gpr: [usize; 32], } impl Registers { pub fn new() -> Registers { Registers { gpr: [0; 32] } } #[inline] pub fn prefetch(&self) { unsafe { prefetch(self as *const _ as *const usize); prefetch(self.gpr[1] as *const usize); } } } pub fn initialize_call_frame( regs: &mut Registers, fptr: InitFn, arg: usize, arg2: *mut usize, stack: &Stack, ) { // Callee-saved registers start at x19 const X19: usize = 19 - 19; const X20: usize = 20 - 19; const X21: usize = 21 - 19; const FP: usize = 29 - 19; const LR: usize = 30 - 19; const SP: usize = 31 - 19; let sp = align_down(stack.end()); // These registers are frobbed by bootstrap_green_task into the right // location so we can invoke the "real init function", `fptr`. regs.gpr[X19] = arg; regs.gpr[X20] = arg2 as usize; regs.gpr[X21] = fptr as usize; // Aarch64 current stack frame pointer regs.gpr[FP] = sp as usize; regs.gpr[LR] = bootstrap_green_task as usize; // setup the init stack // this is prepared for the swap context regs.gpr[SP] = sp as usize; } generator-0.7.1/src/detail/asm/asm_aarch64_aapcs_elf_gas.S000064400000000000000000000023270072674642500215270ustar 00000000000000.text .globl prefetch .type prefetch,@function .align 16 prefetch: prfm pldl1keep, [x0] ret .size prefetch,.-prefetch .text .globl bootstrap_green_task .type bootstrap_green_task,@function .align 16 bootstrap_green_task: mov x0, x19 // arg0 mov x1, x20 // arg1 mov x30, #0 // clear LR ret x21 .size bootstrap_green_task,.-bootstrap_green_task .text .globl swap_registers .type swap_registers,@function .align 16 swap_registers: stp x19, x20, [x0, #0] stp x21, x22, [x0, #16] stp x23, x24, [x0, #32] stp x25, x26, [x0, #48] stp x27, x28, [x0, #64] stp x29, x30, [x0, #80] mov x2, sp str x2, [x0, #96] stp d8, d9, [x0, #112] stp d10, d11, [x0, #128] stp d12, d13, [x0, #144] stp d14, d15, [x0, #160] ldp x19, x20, [x1, #0] ldp x21, x22, [x1, #16] ldp x23, x24, [x1, #32] ldp x25, x26, [x1, #48] ldp x27, x28, [x1, #64] ldp x29, x30, [x1, #80] ldr x2, [x1, #96] mov sp, x2 ldp d8, d9, [x1, #112] ldp d10, d11, [x1, #128] ldp d12, d13, [x1, #144] ldp d14, d15, [x1, #160] ret .size swap_registers,.-swap_registers /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits generator-0.7.1/src/detail/asm/asm_aarch64_aapcs_macho_gas.S000064400000000000000000000016610072674642500220500ustar 00000000000000.text .globl _prefetch .align 8 _prefetch: prfm pldl1keep, [x0] ret .text .globl _bootstrap_green_task .align 8 _bootstrap_green_task: mov x0, x19 // arg0 mov x1, x20 // arg1 mov x30, #0 // clear LR ret x21 .text .globl _swap_registers .align 8 _swap_registers: stp x19, x20, [x0, #0] stp x21, x22, [x0, #16] stp x23, x24, [x0, #32] stp x25, x26, [x0, #48] stp x27, x28, [x0, #64] stp x29, x30, [x0, #80] mov x2, sp str x2, [x0, #96] stp d8, d9, [x0, #112] stp d10, d11, [x0, #128] stp d12, d13, [x0, #144] stp d14, d15, [x0, #160] ldp x19, x20, [x1, #0] ldp x21, x22, [x1, #16] ldp x23, x24, [x1, #32] ldp x25, x26, [x1, #48] ldp x27, x28, [x1, #64] ldp x29, x30, [x1, #80] ldr x2, [x1, #96] mov sp, x2 ldp d8, d9, [x1, #112] ldp d10, d11, [x1, #128] ldp d12, d13, [x1, #144] ldp d14, d15, [x1, #160] ret generator-0.7.1/src/detail/asm/asm_x86_64_ms_pe_gas.asm000064400000000000000000000062020072674642500207750ustar 00000000000000.file "asm_x86_64_ms_pe_gas.asm" .text .p2align 4,,15 .globl prefetch_asm .def prefetch_asm; .scl 2; .type 32; .endef .seh_proc prefetch_asm prefetch_asm: .seh_endprologue prefetcht1 (%rdi) ret .seh_endproc .section .drectve .ascii " -export:\"prefetch_asm\"" .text .p2align 4,,15 .globl bootstrap_green_task .def bootstrap_green_task; .scl 2; .type 32; .endef .seh_proc bootstrap_green_task bootstrap_green_task: .seh_endprologue mov %r12, %rcx /* setup the function arg */ mov %r13, %rdx /* setup the function arg */ and $-16, %rsp /* align the stack pointer */ mov %r14, (%rsp) /* this is the new return adrress */ ret .seh_endproc .section .drectve .ascii " -export:\"bootstrap_green_task\"" .text .p2align 4,,15 .globl swap_registers .def swap_registers; .scl 2; .type 32; .endef .seh_proc swap_registers swap_registers: .seh_endprologue mov %rbx, (0*8)(%rcx) mov %rsp, (1*8)(%rcx) mov %rbp, (2*8)(%rcx) mov %r12, (4*8)(%rcx) mov %r13, (5*8)(%rcx) mov %r14, (6*8)(%rcx) mov %r15, (7*8)(%rcx) mov %rdi, (9*8)(%rcx) mov %rsi, (10*8)(%rcx) /* align mem */ mov %rcx, %r10 and $0xf0, %r10b /* Save non-volatile XMM registers */ movapd %xmm6, (16*8)(%r10) movapd %xmm7, (18*8)(%r10) movapd %xmm8, (20*8)(%r10) movapd %xmm9, (22*8)(%r10) movapd %xmm10, (24*8)(%r10) movapd %xmm11, (26*8)(%r10) movapd %xmm12, (28*8)(%r10) movapd %xmm13, (30*8)(%r10) movapd %xmm14, (32*8)(%r10) movapd %xmm15, (34*8)(%r10) /* load NT_TIB */ movq %gs:(0x30), %r10 /* save current stack base */ movq 0x08(%r10), %rax mov %rax, (11*8)(%rcx) /* save current stack limit */ movq 0x10(%r10), %rax mov %rax, (12*8)(%rcx) /* save current deallocation stack */ movq 0x1478(%r10), %rax mov %rax, (13*8)(%rcx) /* save fiber local storage */ /* movq 0x18(%r10), %rax */ /* mov %rax, (14*8)(%rcx) */ ; mov %rcx, (3*8)(%rcx) mov (0*8)(%rdx), %rbx mov (1*8)(%rdx), %rsp mov (2*8)(%rdx), %rbp mov (4*8)(%rdx), %r12 mov (5*8)(%rdx), %r13 mov (6*8)(%rdx), %r14 mov (7*8)(%rdx), %r15 mov (9*8)(%rdx), %rdi mov (10*8)(%rdx), %rsi /* align mem */ mov %rdx, %r10 and $0xf0, %r10b /* Restore non-volatile XMM registers */ movapd (16*8)(%r10), %xmm6 movapd (18*8)(%r10), %xmm7 movapd (20*8)(%r10), %xmm8 movapd (22*8)(%r10), %xmm9 movapd (24*8)(%r10), %xmm10 movapd (26*8)(%r10), %xmm11 movapd (28*8)(%r10), %xmm12 movapd (30*8)(%r10), %xmm13 movapd (32*8)(%r10), %xmm14 movapd (34*8)(%r10), %xmm15 /* load NT_TIB */ movq %gs:(0x30), %r10 /* restore fiber local storage */ /* mov (14*8)(%rdx), %rax */ /* movq %rax, 0x18(%r10) */ /* restore deallocation stack */ mov (13*8)(%rdx), %rax movq %rax, 0x1478(%r10) /* restore stack limit */ mov (12*8)(%rdx), %rax movq %rax, 0x10(%r10) /* restore stack base */ mov (11*8)(%rdx), %rax movq %rax, 0x8(%r10) ; mov (3*8)(%rdx), %rcx ret .seh_endproc .section .drectve .ascii " -export:\"swap_registers\"" generator-0.7.1/src/detail/asm/asm_x86_64_ms_pe_masm.asm000064400000000000000000000046650072674642500211730ustar 00000000000000.code prefetch_asm PROC FRAME .endprolog prefetcht1 [rcx] ret prefetch_asm ENDP bootstrap_green_task PROC FRAME .endprolog mov rcx, r12 ; setup the function arg mov rdx, r13 ; setup the function arg and rsp, -16 ; align the stack pointer mov [rsp], r14 ; this is the new return adrress ret bootstrap_green_task ENDP swap_registers PROC FRAME .endprolog mov [rcx + 0*8], rbx mov [rcx + 1*8], rsp mov [rcx + 2*8], rbp mov [rcx + 4*8], r12 mov [rcx + 5*8], r13 mov [rcx + 6*8], r14 mov [rcx + 7*8], r15 mov [rcx + 9*8], rdi mov [rcx + 10*8], rsi mov r10, rcx and r10, not 8 ; Save non-volatile XMM registers: movapd [r10 + 16*8], xmm6 movapd [r10 + 18*8], xmm7 movapd [r10 + 20*8], xmm8 movapd [r10 + 22*8], xmm9 movapd [r10 + 24*8], xmm10 movapd [r10 + 26*8], xmm11 movapd [r10 + 28*8], xmm12 movapd [r10 + 30*8], xmm13 movapd [r10 + 32*8], xmm14 movapd [r10 + 34*8], xmm15 ; load NT_TIB mov r10, gs:[030h] ; save current stack base mov rax, [r10 + 08h] mov [rcx + 11*8], rax ; save current stack limit mov rax, [r10 + 010h] mov [rcx + 12*8], rax ; save current deallocation stack mov rax, [r10 + 01478h] mov [rcx + 13*8], rax ; save fiber local storage ; mov rax, [r10 + 0x18] ; mov [rcx + 14*8], rax ; mov [rcx + 3*8], rcx mov rbx, [rdx + 0*8] mov rsp, [rdx + 1*8] mov rbp, [rdx + 2*8] mov r12, [rdx + 4*8] mov r13, [rdx + 5*8] mov r14, [rdx + 6*8] mov r15, [rdx + 7*8] mov rdi, [rdx + 9*8] mov rsi, [rdx + 10*8] mov r10, rdx and r10, not 8 ; Restore non-volatile XMM registers: movapd xmm6, [r10 + 16*8] movapd xmm7, [r10 + 18*8] movapd xmm8, [r10 + 20*8] movapd xmm9, [r10 + 22*8] movapd xmm10, [r10 + 24*8] movapd xmm11, [r10 + 26*8] movapd xmm12, [r10 + 28*8] movapd xmm13, [r10 + 30*8] movapd xmm14, [r10 + 32*8] movapd xmm15, [r10 + 34*8] ; load NT_TIB mov r10, gs:[030h] ; restore fiber local storage ; mov [rdx + 14*8], rax ; movq rax, [r10 + 0x18] ; restore deallocation stack mov rax, [rdx + 13*8] mov [r10 + 01478h], rax ; restore stack limit mov rax, [rdx + 12*8] mov [r10 + 010h], rax ; restore stack base mov rax, [rdx + 11*8] mov [r10 + 08h], rax ; mov rcx, [rdx + 3*8] ret swap_registers ENDP END generator-0.7.1/src/detail/asm/asm_x86_64_sysv_elf_gas.S000064400000000000000000000021130072674642500211430ustar 00000000000000.text .globl prefetch .type prefetch,@function .align 16 prefetch: prefetcht1 (%rdi) ret .size prefetch,.-prefetch .text .globl bootstrap_green_task .type bootstrap_green_task,@function .align 16 bootstrap_green_task: mov %r12, %rdi /* setup the function arg */ mov %r13, %rsi /* setup the function arg */ and $-16, %rsp /* align the stack pointer */ mov %r14, (%rsp) /* this is the new return adrress */ ret .size bootstrap_green_task,.-bootstrap_green_task .text .globl swap_registers .type swap_registers,@function .align 16 swap_registers: mov %rbx, (0*8)(%rdi) mov %rsp, (1*8)(%rdi) mov %rbp, (2*8)(%rdi) mov %r12, (4*8)(%rdi) mov %r13, (5*8)(%rdi) mov %r14, (6*8)(%rdi) mov %r15, (7*8)(%rdi) mov (0*8)(%rsi), %rbx mov (1*8)(%rsi), %rsp mov (2*8)(%rsi), %rbp mov (4*8)(%rsi), %r12 mov (5*8)(%rsi), %r13 mov (6*8)(%rsi), %r14 mov (7*8)(%rsi), %r15 ret .size bootstrap_green_task,.-bootstrap_green_task /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits generator-0.7.1/src/detail/asm/asm_x86_64_sysv_macho_gas.S000064400000000000000000000014340072674642500214710ustar 00000000000000.text .globl _prefetch .align 8 _prefetch: prefetcht1 (%rdi) ret .text .globl _bootstrap_green_task .align 8 _bootstrap_green_task: mov %r12, %rdi /* setup the function arg */ mov %r13, %rsi /* setup the function arg */ and $-16, %rsp /* align the stack pointer */ mov %r14, (%rsp) /* this is the new return adrress */ ret .text .globl _swap_registers .align 8 _swap_registers: mov %rbx, (0*8)(%rdi) mov %rsp, (1*8)(%rdi) mov %rbp, (2*8)(%rdi) mov %r12, (4*8)(%rdi) mov %r13, (5*8)(%rdi) mov %r14, (6*8)(%rdi) mov %r15, (7*8)(%rdi) mov (0*8)(%rsi), %rbx mov (1*8)(%rsi), %rsp mov (2*8)(%rsi), %rbp mov (4*8)(%rsi), %r12 mov (5*8)(%rsi), %r13 mov (6*8)(%rsi), %r14 mov (7*8)(%rsi), %r15 ret generator-0.7.1/src/detail/mod.rs000064400000000000000000000037220072674642500150310ustar 00000000000000// Register contexts used in various architectures // // These structures all represent a context of one task throughout its // execution. Each struct is a representation of the architecture's register // set. When swapping between tasks, these register sets are used to save off // the current registers into one struct, and load them all from another. // // Note that this is only used for context switching, which means that some of // the registers may go unused. For example, for architectures with // callee/caller saved registers, the context will only reflect the callee-saved // registers. This is because the caller saved registers are already stored // elsewhere on the stack (if it was necessary anyway). // // Additionally, there may be fields on various architectures which are unused // entirely because they only reflect what is theoretically possible for a // "complete register set" to show, but user-space cannot alter these registers. // An example of this would be the segment selectors for x86. // // These structures/functions are roughly in-sync with the source files inside // of src/rt/arch/$arch. The only currently used function from those folders is // the `rust_swap_registers` function, but that's only because for now segmented // stacks are disabled. #[cfg(all(unix, target_arch = "x86_64"))] #[path = "x86_64_unix.rs"] pub mod asm; #[cfg(all(windows, target_arch = "x86_64"))] #[path = "x86_64_windows.rs"] pub mod asm; #[cfg(all(unix, target_arch = "aarch64"))] #[path = "aarch64_unix.rs"] pub mod asm; pub use self::asm::{initialize_call_frame, prefetch, swap_registers, Registers}; #[inline] fn align_down(sp: *mut usize) -> *mut usize { let sp = (sp as usize) & !(16 - 1); sp as *mut usize } // ptr::mut_offset is positive isize only #[inline] #[allow(unused)] fn mut_offset(ptr: *mut T, count: isize) -> *mut T { // use std::mem::size_of; // (ptr as isize + count * (size_of::() as isize)) as *mut T unsafe { ptr.offset(count) } } generator-0.7.1/src/detail/x86_64_unix.rs000064400000000000000000000074670072674642500162650ustar 00000000000000use crate::detail::{align_down, mut_offset}; use crate::reg_context::InitFn; use crate::stack::Stack; // #[cfg(not(nightly))] #[link(name = "asm", kind = "static")] extern "C" { pub fn bootstrap_green_task(); pub fn prefetch(data: *const usize); pub fn swap_registers(out_regs: *mut Registers, in_regs: *const Registers); } /* #[cfg(nightly)] mod asm_impl { use super::Registers; /// prefetch data #[inline] pub unsafe extern "C" fn prefetch(data: *const usize) { llvm_asm!( "prefetcht1 $0" : // no output : "m"(*data) : : "volatile" ); } #[naked] #[inline(never)] pub unsafe extern "C" fn bootstrap_green_task() { llvm_asm!( " mov %r12, %rdi // setup the function arg mov %r13, %rsi // setup the function arg and $$-16, %rsp // align the stack pointer mov %r14, (%rsp) // this is the new return address " : // no output : // no input : "memory" : "volatile" ); } #[naked] #[inline(never)] pub unsafe extern "C" fn swap_registers(out_regs: *mut Registers, in_regs: *const Registers) { // The first argument is in %rdi, and the second one is in %rsi llvm_asm!( "" : : "{rdi}"(out_regs), "{rsi}"(in_regs) : : ); // introduce this function to workaround rustc bug! (#6) #[naked] unsafe extern "C" fn _swap_reg() { // Save registers llvm_asm!( " mov %rbx, (0*8)(%rdi) mov %rsp, (1*8)(%rdi) mov %rbp, (2*8)(%rdi) mov %r12, (4*8)(%rdi) mov %r13, (5*8)(%rdi) mov %r14, (6*8)(%rdi) mov %r15, (7*8)(%rdi) mov (0*8)(%rsi), %rbx mov (1*8)(%rsi), %rsp mov (2*8)(%rsi), %rbp mov (4*8)(%rsi), %r12 mov (5*8)(%rsi), %r13 mov (6*8)(%rsi), %r14 mov (7*8)(%rsi), %r15 " : : //"{rdi}"(out_regs), "{rsi}"(in_regs) : "memory" : "volatile" ); } _swap_reg() } } #[cfg(nightly)] pub use self::asm_impl::*; */ #[repr(C)] #[derive(Debug)] pub struct Registers { gpr: [usize; 8], } impl Registers { pub fn new() -> Registers { Registers { gpr: [0; 8] } } #[inline] pub fn prefetch(&self) { unsafe { prefetch(self as *const _ as *const usize); prefetch(self.gpr[1] as *const usize); } } } pub fn initialize_call_frame( regs: &mut Registers, fptr: InitFn, arg: usize, arg2: *mut usize, stack: &Stack, ) { // Redefinitions from rt/arch/x86_64/regs.h const RUSTRT_RSP: usize = 1; const RUSTRT_RBP: usize = 2; const RUSTRT_R12: usize = 4; const RUSTRT_R13: usize = 5; const RUSTRT_R14: usize = 6; let sp = align_down(stack.end()); // These registers are frobbed by bootstrap_green_task into the right // location so we can invoke the "real init function", `fptr`. regs.gpr[RUSTRT_R12] = arg; regs.gpr[RUSTRT_R13] = arg2 as usize; regs.gpr[RUSTRT_R14] = fptr as usize; // Last base pointer on the stack should be 0 regs.gpr[RUSTRT_RBP] = 0; // setup the init stack // this is prepared for the swap context regs.gpr[RUSTRT_RSP] = mut_offset(sp, -2) as usize; unsafe { // leave enough space for RET *mut_offset(sp, -2) = bootstrap_green_task as usize; *mut_offset(sp, -1) = 0; } } generator-0.7.1/src/detail/x86_64_windows.rs000064400000000000000000000170520072674642500167630ustar 00000000000000use crate::detail::{align_down, mut_offset}; use crate::reg_context::InitFn; use crate::stack::Stack; // #[cfg(not(nightly))] #[link(name = "asm", kind = "static")] extern "C" { pub fn bootstrap_green_task(); pub fn prefetch_asm(data: *const usize); pub fn swap_registers(out_regs: *mut Registers, in_regs: *const Registers); } #[inline] #[allow(dead_code)] pub fn prefetch(data: *const usize) { unsafe { prefetch_asm(data) } } /* #[cfg(nightly)] mod asm_impl { use super::Registers; /// prefetch data #[inline] pub unsafe extern "C" fn prefetch_asm(data: *const usize) { llvm_asm!( "prefetcht1 $0" : // no output : "m"(*data) : : "volatile" ); } #[naked] #[inline(never)] pub unsafe extern "C" fn bootstrap_green_task() { llvm_asm!( " mov %r12, %rcx // setup the function arg mov %r13, %rdx // setup the function arg and $$-16, %rsp // align the stack pointer mov %r14, (%rsp) // this is the new return address " : // no output : // no input : "memory" : "volatile" ); } #[naked] #[inline(never)] pub unsafe extern "C" fn swap_registers(out_regs: *mut Registers, in_regs: *const Registers) { // The first argument is in %rcx, and the second one is in %rdx llvm_asm!( "" : : "{rcx}"(out_regs), "{rdx}"(in_regs) : : ); // introduce this function to workaround rustc bug! (#6) #[naked] unsafe extern "C" fn _swap_reg() { // Save registers llvm_asm!( " mov %rbx, (0*8)(%rcx) mov %rsp, (1*8)(%rcx) mov %rbp, (2*8)(%rcx) mov %r12, (4*8)(%rcx) mov %r13, (5*8)(%rcx) mov %r14, (6*8)(%rcx) mov %r15, (7*8)(%rcx) mov %rdi, (9*8)(%rcx) mov %rsi, (10*8)(%rcx) // mov %rcx, %r10 // and $$0xf0, %r10b // Save non-volatile XMM registers: movapd %xmm6, (16*8)(%rcx) movapd %xmm7, (18*8)(%rcx) movapd %xmm8, (20*8)(%rcx) movapd %xmm9, (22*8)(%rcx) movapd %xmm10, (24*8)(%rcx) movapd %xmm11, (26*8)(%rcx) movapd %xmm12, (28*8)(%rcx) movapd %xmm13, (30*8)(%rcx) movapd %xmm14, (32*8)(%rcx) movapd %xmm15, (34*8)(%rcx) /* load NT_TIB */ movq %gs:(0x30), %r10 /* save current stack base */ movq 0x08(%r10), %rax mov %rax, (11*8)(%rcx) /* save current stack limit */ movq 0x10(%r10), %rax mov %rax, (12*8)(%rcx) /* save current deallocation stack */ movq 0x1478(%r10), %rax mov %rax, (13*8)(%rcx) /* save fiber local storage */ // movq 0x18(%r10), %rax // mov %rax, (14*8)(%rcx) // mov %rcx, (3*8)(%rcx) mov (0*8)(%rdx), %rbx mov (1*8)(%rdx), %rsp mov (2*8)(%rdx), %rbp mov (4*8)(%rdx), %r12 mov (5*8)(%rdx), %r13 mov (6*8)(%rdx), %r14 mov (7*8)(%rdx), %r15 mov (9*8)(%rdx), %rdi mov (10*8)(%rdx), %rsi // Restore non-volatile XMM registers: movapd (16*8)(%rdx), %xmm6 movapd (18*8)(%rdx), %xmm7 movapd (20*8)(%rdx), %xmm8 movapd (22*8)(%rdx), %xmm9 movapd (24*8)(%rdx), %xmm10 movapd (26*8)(%rdx), %xmm11 movapd (28*8)(%rdx), %xmm12 movapd (30*8)(%rdx), %xmm13 movapd (32*8)(%rdx), %xmm14 movapd (34*8)(%rdx), %xmm15 /* load NT_TIB */ movq %gs:(0x30), %r10 /* restore fiber local storage */ // mov (14*8)(%rdx), %rax // movq %rax, 0x18(%r10) /* restore deallocation stack */ mov (13*8)(%rdx), %rax movq %rax, 0x1478(%r10) /* restore stack limit */ mov (12*8)(%rdx), %rax movq %rax, 0x10(%r10) /* restore stack base */ mov (11*8)(%rdx), %rax movq %rax, 0x8(%r10) // mov (3*8)(%rdx), %rcx " // why save the rcx and rdx in stack? this will overwrite something! // the naked function should only use the asm block, debug version breaks // since rustc 1.27.0-nightly, we have to use O2 level optimization (#6) : : //"{rcx}"(out_regs), "{rdx}"(in_regs) : "memory" : "volatile" ); } _swap_reg() } } #[cfg(nightly)] pub use self::asm_impl::*; */ // #[cfg_attr(nightly, repr(simd))] #[repr(C)] #[derive(Debug, Copy, Clone, Eq, PartialEq)] struct Xmm(u32, u32, u32, u32); impl Xmm { pub fn new(a: u32, b: u32, c: u32, d: u32) -> Self { Xmm(a, b, c, d) } } // windows need to restore xmm6~xmm15, for most cases only use two xmm registers #[repr(C)] #[derive(Debug)] pub struct Registers { gpr: [usize; 16], // keep enough for place holder _xmm: [Xmm; 10], } impl Registers { pub fn new() -> Registers { Registers { gpr: [0; 16], _xmm: [Xmm::new(0, 0, 0, 0); 10], } } #[inline] pub fn prefetch(&self) { unsafe { prefetch_asm(self as *const _ as *const usize); prefetch_asm(self.gpr[1] as *const usize); } } } pub fn initialize_call_frame( regs: &mut Registers, fptr: InitFn, arg: usize, arg2: *mut usize, stack: &Stack, ) { // Redefinitions from rt/arch/x86_64/regs.h const RUSTRT_RSP: usize = 1; const RUSTRT_RBP: usize = 2; const RUSTRT_R12: usize = 4; const RUSTRT_R13: usize = 5; const RUSTRT_R14: usize = 6; const RUSTRT_STACK_BASE: usize = 11; const RUSTRT_STACK_LIMIT: usize = 12; const RUSTRT_STACK_DEALLOC: usize = 13; let sp = align_down(stack.end()); // These registers are frobbed by bootstrap_green_task into the right // location so we can invoke the "real init function", `fptr`. regs.gpr[RUSTRT_R12] = arg; regs.gpr[RUSTRT_R13] = arg2 as usize; regs.gpr[RUSTRT_R14] = fptr as usize; // Last base pointer on the stack should be 0 regs.gpr[RUSTRT_RBP] = 0; regs.gpr[RUSTRT_STACK_BASE] = stack.end() as usize; regs.gpr[RUSTRT_STACK_LIMIT] = stack.begin() as usize; regs.gpr[RUSTRT_STACK_DEALLOC] = 0; //mut_offset(sp, -8192) as usize; // setup the init stack // this is prepared for the swap context regs.gpr[RUSTRT_RSP] = mut_offset(sp, -2) as usize; unsafe { // leave enough space for RET *mut_offset(sp, -2) = bootstrap_green_task as usize; *mut_offset(sp, -1) = 0; } } generator-0.7.1/src/gen_impl.rs000064400000000000000000000401500072674642500145760ustar 00000000000000//! # generator //! //! Rust generator implementation //! use std::any::Any; use std::fmt; use std::marker::PhantomData; use std::panic; use std::thread; use crate::reg_context::RegContext; use crate::rt::{Context, ContextStack, Error}; use crate::scope::Scope; use crate::stack::{Func, Stack, StackBox}; use crate::yield_::yield_now; // default stack size, in usize // windows has a minimal size as 0x4a8!!!! pub const DEFAULT_STACK_SIZE: usize = 0x1000; /// the generator obj type, the functor passed to it must be Send pub struct GeneratorObj<'a, A, T, const LOCAL: bool> { gen: StackBox>, } /// the generator type, the functor passed to it must be Send pub type Generator<'a, A, T> = GeneratorObj<'a, A, T, false>; // only when A, T and Functor are all sendable, the generator could be send unsafe impl Send for Generator<'static, A, T> {} impl<'a, A, T> Generator<'a, A, T> { /// init a heap based generator with scoped closure pub fn scoped_init) -> T + Send + 'a>(&mut self, f: F) where T: Send + 'a, A: Send + 'a, { self.gen.scoped_init(f); } /// init a heap based generator // it's can be used to re-init a 'done' generator before it's get dropped pub fn init_code T + Send + 'a>(&mut self, f: F) where T: Send + 'a, { self.gen.init_code(f); } } /// the local generator type, can't Send pub type LocalGenerator<'a, A, T> = GeneratorObj<'a, A, T, true>; impl<'a, A, T> LocalGenerator<'a, A, T> { /// init a heap based generator with scoped closure pub fn scoped_init) -> T + 'a>(&mut self, f: F) where T: 'a, A: 'a, { self.gen.scoped_init(f); } } impl<'a, A, T, const LOCAL: bool> GeneratorObj<'a, A, T, LOCAL> { /// Constructs a Generator from a raw pointer. /// /// # Safety /// /// This function is unsafe because improper use may lead to /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. #[inline] pub unsafe fn from_raw(raw: *mut usize) -> Self { GeneratorObj { gen: StackBox::from_raw(raw as *mut GeneratorImpl<'a, A, T>), } } /// Consumes the `Generator`, returning a wrapped raw pointer. #[inline] pub fn into_raw(self) -> *mut usize { let ret = self.gen.as_ptr() as *mut usize; std::mem::forget(self); ret } /// prefetch the generator into cache #[inline] pub fn prefetch(&self) { self.gen.prefetch(); } /// prepare the para that passed into generator before send #[inline] pub fn set_para(&mut self, para: A) { self.gen.set_para(para); } /// set the generator local data #[inline] pub fn set_local_data(&mut self, data: *mut u8) { self.gen.set_local_data(data); } /// get the generator local data #[inline] pub fn get_local_data(&self) -> *mut u8 { self.gen.get_local_data() } /// get the generator panic data #[inline] pub fn get_panic_data(&mut self) -> Option> { self.gen.get_panic_data() } /// resume the generator without touch the para /// you should call `set_para` before this method #[inline] pub fn resume(&mut self) -> Option { self.gen.resume() } /// `raw_send` #[inline] pub fn raw_send(&mut self, para: Option) -> Option { self.gen.raw_send(para) } /// send interface pub fn send(&mut self, para: A) -> T { self.gen.send(para) } /// cancel the generator /// this will trigger a Cancel panic to unwind the stack and finish the generator pub fn cancel(&mut self) { self.gen.cancel() } /// is finished #[inline] pub fn is_done(&self) -> bool { self.gen.is_done() } /// get stack total size and used size in word pub fn stack_usage(&self) -> (usize, usize) { self.gen.stack_usage() } } impl<'a, T, const LOCAL: bool> Iterator for GeneratorObj<'a, (), T, LOCAL> { type Item = T; fn next(&mut self) -> Option { self.resume() } } impl<'a, A, T, const LOCAL: bool> fmt::Debug for GeneratorObj<'a, A, T, LOCAL> { #[cfg(nightly)] #[allow(unused_unsafe)] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use std::intrinsics::type_name; write!( f, "Generator<{}, Output={}, Local={}> {{ ... }}", unsafe { type_name::() }, unsafe { type_name::() }, LOCAL ) } #[cfg(not(nightly))] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Generator {{ ... }}") } } /// Generator helper pub struct Gn { dummy: PhantomData, } impl Gn { /// create a scoped generator with default stack size pub fn new_scoped<'a, T, F>(f: F) -> Generator<'a, A, T> where F: FnOnce(Scope) -> T + Send + 'a, T: Send + 'a, A: Send + 'a, { Self::new_scoped_opt(DEFAULT_STACK_SIZE, f) } /// create a scoped local generator with default stack size pub fn new_scoped_local<'a, T, F>(f: F) -> LocalGenerator<'a, A, T> where F: FnOnce(Scope) -> T + 'a, T: 'a, A: 'a, { Self::new_scoped_opt_local(DEFAULT_STACK_SIZE, f) } /// create a scoped generator with specified stack size pub fn new_scoped_opt<'a, T, F>(size: usize, f: F) -> Generator<'a, A, T> where F: FnOnce(Scope) -> T + Send + 'a, T: Send + 'a, A: Send + 'a, { let mut gen = GeneratorImpl::::new(Stack::new(size)); gen.scoped_init(f); Generator { gen } } /// create a scoped local generator with specified stack size pub fn new_scoped_opt_local<'a, T, F>(size: usize, f: F) -> LocalGenerator<'a, A, T> where F: FnOnce(Scope) -> T + 'a, T: 'a, A: 'a, { let mut gen = GeneratorImpl::::new(Stack::new(size)); gen.scoped_init(f); LocalGenerator { gen } } } impl Gn { /// create a new generator with default stack size #[cfg_attr(feature = "cargo-clippy", allow(clippy::new_ret_no_self))] #[deprecated(since = "0.6.18", note = "please use `scope` version instead")] pub fn new<'a, T: Any, F>(f: F) -> Generator<'a, A, T> where F: FnOnce() -> T + Send + 'a, { Self::new_opt(DEFAULT_STACK_SIZE, f) } /// create a new generator with specified stack size // the `may` library use this API so we can't deprecated it yet. pub fn new_opt<'a, T: Any, F>(size: usize, f: F) -> Generator<'a, A, T> where F: FnOnce() -> T + Send + 'a, { let mut gen = GeneratorImpl::::new(Stack::new(size)); gen.init_context(); gen.init_code(f); Generator { gen } } } /// `GeneratorImpl` #[repr(C)] struct GeneratorImpl<'a, A, T> { // run time context context: Context, // stack stack: Stack, // save the input para: Option, // save the output ret: Option, // boxed functor f: Option, // phantom lifetime phantom: PhantomData<&'a T>, } impl<'a, A: Any, T: Any> GeneratorImpl<'a, A, T> { /// create a new generator with default stack size fn init_context(&mut self) { unsafe { std::ptr::write( self.context.para.as_mut_ptr(), &mut self.para as &mut dyn Any, ); std::ptr::write(self.context.ret.as_mut_ptr(), &mut self.ret as &mut dyn Any); } } } impl<'a, A, T> GeneratorImpl<'a, A, T> { /// create a new generator with specified stack size fn new(mut stack: Stack) -> StackBox { // the stack box would finally dealloc the stack! unsafe { let mut stack_box = stack.alloc_uninit_box::>(); (*stack_box.as_mut_ptr()).init(GeneratorImpl { para: None, stack, ret: None, f: None, context: Context::new(), phantom: PhantomData, }); stack_box.assume_init() } } /// prefetch the generator into cache #[inline] pub fn prefetch(&self) { self.context.regs.prefetch(); } /// init a heap based generator with scoped closure fn scoped_init) -> T + 'a>(&mut self, f: F) where T: 'a, A: 'a, { use std::mem::transmute; let scope = unsafe { transmute(Scope::new(&mut self.para, &mut self.ret)) }; self.init_code(move || f(scope)); } /// init a heap based generator // it's can be used to re-init a 'done' generator before it's get dropped fn init_code T + 'a>(&mut self, f: F) where T: 'a, { // make sure the last one is finished if self.f.is_none() && self.context._ref == 0 { self.cancel(); } else { let _ = self.f.take(); } // init ctx parent to itself, this would be the new top self.context.parent = &mut self.context; // init the ref to 0 means that it's ready to start self.context._ref = 0; let ret = &mut self.ret as *mut _; // alloc the function on stack let func = StackBox::new_fn_once(&mut self.stack, move || { let r = f(); unsafe { *ret = Some(r) }; }); self.f = Some(func); self.context.regs.init_with( gen_init, 0, &mut self.f as *mut _ as *mut usize, &self.stack, ); } /// resume the generator #[inline] fn resume_gen(&mut self) { let env = ContextStack::current(); // get the current regs let cur = &mut env.top().regs; // switch to new context, always use the top context's reg // for normal generator self.context.parent == self.context // for coroutine self.context.parent == top generator context debug_assert!(!self.context.parent.is_null()); let top = unsafe { &mut *self.context.parent }; // save current generator context on stack env.push_context(&mut self.context); // swap to the generator RegContext::swap(cur, &top.regs); // comes back, check the panic status // this would propagate the panic until root context // if it's a coroutine just stop propagate if !self.context.local_data.is_null() { return; } if let Some(err) = self.context.err.take() { // pass the error to the parent until root panic::resume_unwind(err); } } #[inline] fn is_started(&self) -> bool { // when the f is consumed we think it's running self.f.is_none() } /// prepare the para that passed into generator before send #[inline] fn set_para(&mut self, para: A) { self.para = Some(para); } /// set the generator local data #[inline] fn set_local_data(&mut self, data: *mut u8) { self.context.local_data = data; } /// get the generator local data #[inline] fn get_local_data(&self) -> *mut u8 { self.context.local_data } /// get the generator panic data #[inline] fn get_panic_data(&mut self) -> Option> { self.context.err.take() } /// resume the generator without touch the para /// you should call `set_para` before this method #[inline] fn resume(&mut self) -> Option { if self.is_done() { return None; } // every time we call the function, increase the ref count // yield will decrease it and return will not self.context._ref += 1; self.resume_gen(); self.ret.take() } /// `raw_send` #[inline] fn raw_send(&mut self, para: Option) -> Option { if self.is_done() { return None; } // this is the passed in value of the send primitive // the yield part would read out this value in the next round self.para = para; // every time we call the function, increase the ref count // yield will decrease it and return will not self.context._ref += 1; self.resume_gen(); self.ret.take() } /// send interface fn send(&mut self, para: A) -> T { let ret = self.raw_send(Some(para)); ret.expect("send got None return") } /// cancel the generator without any check #[inline] fn raw_cancel(&mut self) { // tell the func to panic // so that we can stop the inner func self.context._ref = 2; // save the old panic hook, we don't want to print anything for the Cancel let old = ::std::panic::take_hook(); ::std::panic::set_hook(Box::new(|_| {})); self.resume_gen(); ::std::panic::set_hook(old); } /// cancel the generator /// this will trigger a Cancel panic to unwind the stack fn cancel(&mut self) { if self.is_done() { return; } // consume the fun if it's not started if !self.is_started() { self.f.take(); self.context._ref = 1; } else { self.raw_cancel(); } } /// is finished #[inline] fn is_done(&self) -> bool { self.is_started() && (self.context._ref & 0x3) != 0 } /// get stack total size and used size in word fn stack_usage(&self) -> (usize, usize) { (self.stack.size(), self.stack.get_used_size()) } } impl<'a, A, T> Drop for GeneratorImpl<'a, A, T> { fn drop(&mut self) { // when the thread is already panic, do nothing if thread::panicking() { return; } if !self.is_started() { // not started yet, just drop the gen return; } if !self.is_done() { trace!("generator is not done while drop"); self.raw_cancel() } assert!(self.is_done()); let (total_stack, used_stack) = self.stack_usage(); if used_stack < total_stack { // here we should record the stack in the class // next time will just use // set_stack_size::(used_stack); } else { error!("stack overflow detected!"); std::panic::panic_any(Error::StackErr); } } } /// don't print panic info for Done/Cancel fn catch_unwind_filter R + panic::UnwindSafe, R>(f: F) -> std::thread::Result { use std::sync::Once; static INIT: std::sync::Once = Once::new(); INIT.call_once(|| { let prev_hook = panic::take_hook(); panic::set_hook(Box::new(move |info| { if let Some(e) = info.payload().downcast_ref::() { match e { // this is not an error at all, ignore it Error::Cancel | Error::Done => return, _ => {} } } prev_hook(info); })); }); panic::catch_unwind(f) } /// the init function passed to reg_context fn gen_init(_: usize, f: *mut usize) -> ! { let clo = move || { // consume self.f let f: &mut Option = unsafe { &mut *(f as *mut _) }; let func = f.take().unwrap(); func.call_once(); }; fn check_err(cause: Box) { if let Some(e) = cause.downcast_ref::() { match e { // this is not an error at all, ignore it Error::Cancel | Error::Done => return, _ => {} } } error!("set panic inside generator"); ContextStack::current().top().err = Some(cause); } // we can't panic inside the generator context // need to propagate the panic to the main thread if let Err(cause) = catch_unwind_filter(clo) { check_err(cause); } yield_now(); unreachable!("Should never come back"); } generator-0.7.1/src/lib.rs000064400000000000000000000011440072674642500135520ustar 00000000000000//! # generator //! //! Rust generator library //! #![cfg_attr(nightly, feature(core_intrinsics))] #![cfg_attr(nightly, feature(thread_local))] #![cfg_attr(test, deny(warnings))] #![deny(missing_docs)] #![allow(deprecated)] #[macro_use] extern crate log; mod detail; mod gen_impl; mod reg_context; mod rt; mod scope; mod stack; mod yield_; pub use crate::gen_impl::{Generator, Gn, LocalGenerator}; pub use crate::rt::{get_local_data, is_generator, Error}; pub use crate::scope::Scope; pub use crate::yield_::{ co_get_yield, co_set_para, co_yield_with, done, get_yield, yield_, yield_from, yield_with, }; generator-0.7.1/src/reg_context.rs000064400000000000000000000054420072674642500153320ustar 00000000000000use crate::detail::{initialize_call_frame, swap_registers, Registers}; use crate::stack::Stack; #[derive(Debug)] pub struct RegContext { /// Hold the registers while the task or scheduler is suspended regs: Registers, } // first argument is task handle, second is thunk ptr pub type InitFn = fn(usize, *mut usize) -> !; impl RegContext { pub fn empty() -> RegContext { RegContext { regs: Registers::new(), } } #[inline] pub fn prefetch(&self) { self.regs.prefetch(); } /// Create a new context #[allow(dead_code)] pub fn new(init: InitFn, arg: usize, start: *mut usize, stack: &Stack) -> RegContext { let mut ctx = RegContext::empty(); ctx.init_with(init, arg, start, stack); ctx } /// init the generator register #[inline] pub fn init_with(&mut self, init: InitFn, arg: usize, start: *mut usize, stack: &Stack) { // Save and then immediately load the current context, // we will modify it to call the given function when restored back initialize_call_frame(&mut self.regs, init, arg, start, stack); } /// Switch contexts /// /// Suspend the current execution context and resume another by /// saving the registers values of the executing thread to a Context /// then loading the registers from a previously saved Context. #[inline] pub fn swap(out_context: &mut RegContext, in_context: &RegContext) { // debug!("register raw swap"); unsafe { swap_registers(&mut out_context.regs, &in_context.regs) } } /// Load the context and switch. This function will never return. #[inline] #[allow(dead_code)] pub fn load(to_context: &RegContext) { let mut cur = Registers::new(); let regs: &Registers = &to_context.regs; unsafe { swap_registers(&mut cur, regs) } } } #[cfg(test)] mod test { use std::mem::transmute; use crate::reg_context::RegContext; use crate::stack::Stack; const MIN_STACK: usize = 1024; fn init_fn(arg: usize, f: *mut usize) -> ! { let func: fn() = unsafe { transmute(f) }; func(); let ctx: &RegContext = unsafe { transmute(arg) }; RegContext::load(ctx); unreachable!("Should never comeback"); } #[test] fn test_swap_context() { static mut VAL: bool = false; let mut cur = RegContext::empty(); fn callback() { unsafe { VAL = true; } } let stk = Stack::new(MIN_STACK); let ctx = RegContext::new( init_fn, unsafe { transmute(&cur) }, callback as *mut usize, &stk, ); RegContext::swap(&mut cur, &ctx); unsafe { assert!(VAL); } } } generator-0.7.1/src/rt.rs000064400000000000000000000200450072674642500134320ustar 00000000000000//! # generator run time support //! //! generator run time context management //! use std::any::Any; use std::mem::MaybeUninit; use std::ptr; use crate::reg_context::RegContext; thread_local!( /// each thread has it's own generator context stack static ROOT_CONTEXT: Box = { let mut root = Box::new(Context::new()); let p = &mut *root as *mut _; root.parent = p; // init top to current root } ); // fast access pointer, this is will be init only once // when ROOT_CONTEXT get initialized. but in debug mode it // will be zero in generator context since the stack changed // to a different place, be careful about that. #[cfg(nightly)] #[thread_local] #[no_mangle] static mut ROOT_CONTEXT_P: *mut Context = ptr::null_mut(); /// yield panic error types #[allow(dead_code)] #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Error { /// Done panic Done, /// Cancel panic Cancel, /// Type mismatch panic TypeErr, /// Stack overflow panic StackErr, /// Wrong Context panic ContextErr, } /// generator context #[repr(C)] pub struct Context { /// generator regs context pub regs: RegContext, /// child context child: *mut Context, /// parent context pub parent: *mut Context, /// passed in para for send pub para: MaybeUninit<*mut dyn Any>, /// this is just a buffer for the return value pub ret: MaybeUninit<*mut dyn Any>, /// track generator ref, yield will -1, send will +1 pub _ref: usize, /// context local storage pub local_data: *mut u8, /// propagate panic pub err: Option>, } impl Context { /// return a default generator context pub fn new() -> Context { Context { regs: RegContext::empty(), para: MaybeUninit::zeroed(), ret: MaybeUninit::zeroed(), _ref: 1, // none zero means it's not running err: None, child: ptr::null_mut(), parent: ptr::null_mut(), local_data: ptr::null_mut(), } } /// judge it's generator context #[inline] pub fn is_generator(&self) -> bool { self.parent != self as *const _ as *mut _ } /// get current generator send para #[inline] pub fn get_para(&mut self) -> Option where A: Any, { let para = unsafe { let para_ptr = *self.para.as_mut_ptr(); assert!(!para_ptr.is_null()); &mut *para_ptr }; match para.downcast_mut::>() { Some(v) => v.take(), None => type_error::("get yield type mismatch error detected"), } } /// get coroutine send para #[inline] pub fn co_get_para(&mut self) -> Option { let para = unsafe { let para_ptr = *self.para.as_mut_ptr(); debug_assert!(!para_ptr.is_null()); &mut *(para_ptr as *mut Option) }; para.take() } /// set current generator send para // #[inline] // pub fn set_para(&self, data: A) // where // A: Any, // { // let para = unsafe { &mut *self.para }; // match para.downcast_mut::>() { // Some(v) => *v = Some(data), // None => type_error::("set yield type mismatch error detected"), // } // } /// set coroutine send para /// without check the data type for coroutine performance reason #[inline] pub fn co_set_para(&mut self, data: A) { let para = unsafe { let para_ptr = *self.para.as_mut_ptr(); debug_assert!(!para_ptr.is_null()); &mut *(para_ptr as *mut Option) }; *para = Some(data); } /// set current generator return value #[inline] pub fn set_ret(&mut self, v: T) where T: Any, { let ret = unsafe { let ret_ptr = *self.ret.as_mut_ptr(); assert!(!ret_ptr.is_null()); &mut *ret_ptr }; match ret.downcast_mut::>() { Some(r) => *r = Some(v), None => type_error::("yield type mismatch error detected"), } } /// set coroutine return value /// without check the data type for coroutine performance reason #[inline] pub fn co_set_ret(&mut self, v: T) { let ret = unsafe { let ret_ptr = *self.ret.as_mut_ptr(); debug_assert!(!ret_ptr.is_null()); &mut *(ret_ptr as *mut Option) }; *ret = Some(v); } } /// Coroutine managing environment pub struct ContextStack { root: *mut Context, } #[cfg(nightly)] #[inline(never)] unsafe fn init_root_p() { ROOT_CONTEXT_P = ROOT_CONTEXT.with(|r| &**r as *const _ as *mut Context); } impl ContextStack { #[cfg(nightly)] #[inline(never)] pub fn current() -> ContextStack { unsafe { if ROOT_CONTEXT_P.is_null() { init_root_p(); } ContextStack { root: ROOT_CONTEXT_P, } } } #[cfg(not(nightly))] #[inline(never)] pub fn current() -> ContextStack { let root = ROOT_CONTEXT.with(|r| &**r as *const _ as *mut Context); ContextStack { root } } /// get the top context #[inline] pub fn top(&self) -> &'static mut Context { let root = unsafe { &mut *self.root }; unsafe { &mut *root.parent } } /// get the coroutine context #[inline] pub fn co_ctx(&self) -> Option<&'static mut Context> { let root = unsafe { &mut *self.root }; // search from top let mut ctx = unsafe { &mut *root.parent }; while ctx as *const _ != root as *const _ { if !ctx.local_data.is_null() { return Some(ctx); } ctx = unsafe { &mut *ctx.parent }; } // not find any coroutine None } /// push the context to the thread context list #[inline] pub fn push_context(&self, ctx: *mut Context) { let root = unsafe { &mut *self.root }; let ctx = unsafe { &mut *ctx }; let top = unsafe { &mut *root.parent }; let new_top = ctx.parent; // link top and new ctx top.child = ctx; ctx.parent = top; // save the new top root.parent = new_top; } /// pop the context from the thread context list and return it's parent context #[inline] pub fn pop_context(&self, ctx: *mut Context) -> &'static mut Context { let root = unsafe { &mut *self.root }; let ctx = unsafe { &mut *ctx }; let parent = unsafe { &mut *ctx.parent }; // save the old top in ctx's parent ctx.parent = root.parent; // unlink ctx and it's parent parent.child = ptr::null_mut(); // save the new top root.parent = parent; parent } } #[inline] fn type_error(msg: &str) -> ! { #[cfg(nightly)] #[allow(unused_unsafe)] { use std::intrinsics::type_name; let t = unsafe { type_name::() }; error!("{}, expected type: {}", msg, t); } #[cfg(not(nightly))] { error!("{}", msg); } std::panic::panic_any(Error::TypeErr) } /// check the current context if it's generator #[inline] pub fn is_generator() -> bool { let env = ContextStack::current(); let root = unsafe { &mut *env.root }; !root.child.is_null() } /// get the current context local data /// only coroutine support local data #[inline] pub fn get_local_data() -> *mut u8 { let env = ContextStack::current(); let root = unsafe { &mut *env.root }; // search from top let mut ctx = unsafe { &mut *root.parent }; while ctx as *const _ != root as *const _ { if !ctx.local_data.is_null() { return ctx.local_data; } ctx = unsafe { &mut *ctx.parent }; } ptr::null_mut() } #[cfg(test)] mod test { use super::is_generator; #[test] fn test_is_context() { // this is the root context assert!(!is_generator()); } } generator-0.7.1/src/scope.rs000064400000000000000000000046730072674642500141270ustar 00000000000000//! # yield //! //! generator yield implementation //! use std::sync::atomic; use crate::gen_impl::Generator; use crate::rt::{Context, ContextStack, Error}; use crate::yield_::raw_yield_now; /// passed in scope type /// it not use the context to pass data, but keep it's own data ref /// this struct provide both compile type info and runtime data pub struct Scope<'a, A, T> { para: &'a mut Option, ret: &'a mut Option, } impl<'a, A, T> Scope<'a, A, T> { /// create a new scope object pub(crate) fn new(para: &'a mut Option, ret: &'a mut Option) -> Self { Scope { para, ret } } /// set current generator return value #[inline] fn set_ret(&mut self, v: T) { *self.ret = Some(v); } /// raw yield without catch passed in para #[inline] fn raw_yield(&mut self, env: &ContextStack, context: &mut Context, v: T) { // check the context if !context.is_generator() { panic!("yield from none generator context"); } self.set_ret(v); context._ref -= 1; raw_yield_now(env, context); // here we just panic to exit the func if context._ref != 1 { std::panic::panic_any(Error::Cancel); } } /// yield something without catch passed in para #[inline] pub fn yield_with(&mut self, v: T) { let env = ContextStack::current(); let context = env.top(); self.raw_yield(&env, context, v); } /// get current generator send para #[inline] pub fn get_yield(&mut self) -> Option { self.para.take() } /// yield and get the send para // it's totally safe that we can refer to the function block // since we will come back later #[inline] pub fn yield_(&mut self, v: T) -> Option { self.yield_with(v); atomic::compiler_fence(atomic::Ordering::Acquire); self.get_yield() } /// `yield_from` /// the from generator must has the same type as itself pub fn yield_from(&mut self, mut g: Generator) -> Option { let env = ContextStack::current(); let context = env.top(); let mut p = self.get_yield(); while !g.is_done() { match g.raw_send(p) { None => return None, Some(r) => self.raw_yield(&env, context, r), } p = self.get_yield(); } drop(g); // explicitly consume g p } } generator-0.7.1/src/stack/mod.rs000064400000000000000000000265430072674642500147020ustar 00000000000000//! # generator stack //! //! use std::error::Error; use std::fmt::{self, Display}; use std::io; use std::mem::MaybeUninit; use std::os::raw::c_void; use std::ptr; #[cfg(all(unix, target_arch = "x86_64"))] #[path = "unix.rs"] pub mod sys; #[cfg(all(unix, target_arch = "aarch64"))] #[path = "unix.rs"] pub mod sys; #[cfg(all(windows, target_arch = "x86_64"))] #[path = "windows.rs"] pub mod sys; // must align with StackBoxHeader const ALIGN: usize = std::mem::size_of::(); const HEADER_SIZE: usize = std::mem::size_of::() / std::mem::size_of::(); struct StackBoxHeader { // track the stack stack: Stack, // track how big the data is (in usize) data_size: usize, // non zero dealloc the stack need_drop: usize, } /// A pointer type for stack allocation. pub struct StackBox { // the stack memory ptr: ptr::NonNull, } impl StackBox { /// create uninit stack box fn new_uninit(stack: &mut Stack, need_drop: usize) -> MaybeUninit { let offset = unsafe { &mut *stack.get_offset() }; // alloc the data let layout = std::alloc::Layout::new::(); let align = std::cmp::max(layout.align(), ALIGN); let size = ((layout.size() + align - 1) & !(align - 1)) / std::mem::size_of::(); let u_align = align / std::mem::size_of::(); let pad_size = u_align - (*offset + size) % u_align; let data_size = size + pad_size; *offset += data_size; let ptr = unsafe { ptr::NonNull::new_unchecked(stack.end() as *mut T) }; // init the header *offset += HEADER_SIZE; unsafe { let mut header = ptr::NonNull::new_unchecked(stack.end() as *mut StackBoxHeader); let header = header.as_mut(); header.data_size = data_size; header.need_drop = need_drop; header.stack = stack.shadow_clone(); std::mem::MaybeUninit::new(StackBox { ptr }) } } fn get_header(&self) -> &StackBoxHeader { unsafe { let header = (self.ptr.as_ptr() as *mut usize).offset(0 - HEADER_SIZE as isize); &*(header as *const StackBoxHeader) } } /// move data into the box pub(crate) unsafe fn init(&mut self, data: T) { ptr::write(self.ptr.as_ptr(), data); } // get the stack ptr pub(crate) fn as_ptr(&self) -> *mut T { self.ptr.as_ptr() } /// Constructs a StackBox from a raw pointer. /// /// # Safety /// /// This function is unsafe because improper use may lead to /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. #[inline] pub(crate) unsafe fn from_raw(raw: *mut T) -> Self { StackBox { ptr: ptr::NonNull::new_unchecked(raw), } } // Consumes the `StackBox`, returning a wrapped raw pointer. // #[inline] // pub(crate) fn into_raw(b: StackBox) -> *mut T { // let ret = b.ptr.as_ptr(); // std::mem::forget(b); // ret // } } pub struct Func { data: *mut (), size: usize, offset: *mut usize, func: fn(*mut ()), drop: fn(*mut ()), } impl Func { pub fn call_once(mut self) { let data = self.data; self.data = ptr::null_mut(); (self.func)(data); } } impl Drop for Func { fn drop(&mut self) { if !self.data.is_null() { (self.drop)(self.data); } unsafe { *self.offset -= self.size }; } } impl StackBox { fn call_once(data: *mut ()) { unsafe { let data = data as *mut F; let f = data.read(); f(); } } fn drop_inner(data: *mut ()) { unsafe { let data = data as *mut F; ptr::drop_in_place(data); } } /// create a functor on the stack pub(crate) fn new_fn_once(stack: &mut Stack, data: F) -> Func { unsafe { let mut d = Self::new_uninit(stack, 0); (*d.as_mut_ptr()).init(data); let d = d.assume_init(); let header = d.get_header(); let f = Func { data: d.ptr.as_ptr() as *mut (), size: header.data_size + HEADER_SIZE, offset: stack.get_offset(), func: Self::call_once, drop: Self::drop_inner, }; std::mem::forget(d); f } } } impl std::ops::Deref for StackBox { type Target = T; fn deref(&self) -> &T { unsafe { self.ptr.as_ref() } } } impl std::ops::DerefMut for StackBox { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.ptr.as_mut() } } } impl Drop for StackBox { fn drop(&mut self) { let header = self.get_header(); unsafe { *header.stack.get_offset() -= header.data_size + HEADER_SIZE; ptr::drop_in_place(self.ptr.as_ptr()); if header.need_drop != 0 { header.stack.drop_stack(); } } } } /// Error type returned by stack allocation methods. #[derive(Debug)] pub enum StackError { /// Contains the maximum amount of memory allowed to be allocated as stack space. ExceedsMaximumSize(usize), /// Returned if some kind of I/O error happens during allocation. IoError(io::Error), } impl Display for StackError { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { StackError::ExceedsMaximumSize(size) => write!( fmt, "Requested more than max size of {} bytes for a stack", size ), StackError::IoError(ref e) => e.fmt(fmt), } } } impl Error for StackError { fn source(&self) -> Option<&(dyn Error + 'static)> { match *self { StackError::ExceedsMaximumSize(_) => None, StackError::IoError(ref e) => Some(e), } } } /// Represents any kind of stack memory. /// /// `FixedSizeStack` as well as `ProtectedFixedSizeStack` /// can be used to allocate actual stack space. #[derive(Debug)] pub struct SysStack { top: *mut c_void, bottom: *mut c_void, } impl SysStack { /// Creates a (non-owning) representation of some stack memory. /// /// It is unsafe because it is your responsibility to make sure that `top` and `bottom` are valid /// addresses. #[inline] pub unsafe fn new(top: *mut c_void, bottom: *mut c_void) -> SysStack { debug_assert!(top >= bottom); SysStack { top, bottom } } /// Returns the top of the stack from which on it grows downwards towards bottom(). #[inline] pub fn top(&self) -> *mut c_void { self.top } /// Returns the bottom of the stack and thus it's end. #[inline] pub fn bottom(&self) -> *mut c_void { self.bottom } /// Returns the size of the stack between top() and bottom(). #[inline] pub fn len(&self) -> usize { self.top as usize - self.bottom as usize } /// Returns the minimal stack size allowed by the current platform. #[inline] pub fn min_size() -> usize { sys::min_stack_size() } /// Allocates a new stack of `size`. fn allocate(mut size: usize, protected: bool) -> Result { let page_size = sys::page_size(); let min_stack_size = sys::min_stack_size(); let max_stack_size = sys::max_stack_size(); let add_shift = if protected { 1 } else { 0 }; let add = page_size << add_shift; if size < min_stack_size { size = min_stack_size; } size = (size - 1) & !(page_size.overflowing_sub(1).0); if let Some(size) = size.checked_add(add) { if size <= max_stack_size { let mut ret = unsafe { sys::allocate_stack(size) }; if protected { if let Ok(stack) = ret { ret = unsafe { sys::protect_stack(&stack) }; } } return ret.map_err(StackError::IoError); } } Err(StackError::ExceedsMaximumSize(max_stack_size - add)) } } unsafe impl Send for SysStack {} /// generator stack /// this struct will not dealloc the memory /// instead StackBox<> would track it's usage and dealloc it pub struct Stack { buf: SysStack, } impl Stack { /// Allocate a new stack of `size`. If size = 0, this is a `dummy_stack` pub fn new(size: usize) -> Stack { let track = (size & 1) != 0; let mut bytes = size * std::mem::size_of::(); // the minimal size let min_size = SysStack::min_size(); if bytes < min_size { bytes = min_size; } let buf = SysStack::allocate(bytes, true).expect("failed to alloc sys stack"); let stk = Stack { buf }; // if size is not even we do the full foot print test let count = if track { stk.size() } else { // we only check the last few words 8 }; unsafe { let buf = stk.buf.bottom as *mut usize; ptr::write_bytes(buf, 0xEE, count); } // init the stack box usage let offset = stk.get_offset(); unsafe { *offset = 1 }; stk } /// get used stack size pub fn get_used_size(&self) -> usize { let mut offset: usize = 0; unsafe { let mut magic: usize = 0xEE; ptr::write_bytes(&mut magic, 0xEE, 1); let mut ptr = self.buf.bottom as *mut usize; while *ptr == magic { offset += 1; ptr = ptr.offset(1); } } let cap = self.size(); cap - offset } /// get the stack cap #[inline] pub fn size(&self) -> usize { self.buf.len() / std::mem::size_of::() } /// Point to the high end of the allocated stack pub fn end(&self) -> *mut usize { let offset = self.get_offset(); unsafe { (self.buf.top as *mut usize).offset(0 - *offset as isize) } } /// Point to the low end of the allocated stack #[allow(dead_code)] pub fn begin(&self) -> *mut usize { self.buf.bottom as *mut _ } /// alloc buffer on this stack pub fn alloc_uninit_box(&mut self) -> MaybeUninit> { // the first obj should set need drop to non zero StackBox::::new_uninit(self, 1) } // get offset fn get_offset(&self) -> *mut usize { unsafe { (self.buf.top as *mut usize).offset(-1) } } // dealloc the stack fn drop_stack(&self) { if self.buf.len() == 0 { return; } let page_size = sys::page_size(); let guard = (self.buf.bottom as usize - page_size) as *mut c_void; let size_with_guard = self.buf.len() + page_size; unsafe { sys::deallocate_stack(guard, size_with_guard); } } fn shadow_clone(&self) -> Self { Stack { buf: SysStack { top: self.buf.top, bottom: self.buf.bottom, }, } } } impl fmt::Debug for Stack { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let offset = self.get_offset(); write!(f, "Stack<{:?}, Offset={}>", self.buf, unsafe { *offset }) } } generator-0.7.1/src/stack/unix.rs000064400000000000000000000057530072674642500151060ustar 00000000000000use std::io; use std::mem; use std::os::raw::c_void; use std::sync::atomic::{AtomicUsize, Ordering}; use std::usize; use super::SysStack; #[cfg(any( target_os = "openbsd", target_os = "macos", target_os = "ios", target_os = "android", target_os = "illumos", target_os = "solaris" ))] const MAP_STACK: libc::c_int = 0; #[cfg(not(any( target_os = "openbsd", target_os = "macos", target_os = "ios", target_os = "android", target_os = "illumos", target_os = "solaris" )))] const MAP_STACK: libc::c_int = libc::MAP_STACK; pub unsafe fn allocate_stack(size: usize) -> io::Result { const NULL: *mut libc::c_void = 0 as *mut libc::c_void; const PROT: libc::c_int = libc::PROT_READ | libc::PROT_WRITE; const TYPE: libc::c_int = libc::MAP_PRIVATE | libc::MAP_ANON | MAP_STACK; let ptr = libc::mmap(NULL, size, PROT, TYPE, -1, 0); if ptr == libc::MAP_FAILED { Err(io::Error::last_os_error()) } else { Ok(SysStack::new( (ptr as usize + size) as *mut c_void, ptr as *mut c_void, )) } } pub unsafe fn protect_stack(stack: &SysStack) -> io::Result { let page_size = page_size(); debug_assert!(stack.len() % page_size == 0 && stack.len() != 0); let ret = { let bottom = stack.bottom() as *mut libc::c_void; libc::mprotect(bottom, page_size, libc::PROT_NONE) }; if ret != 0 { Err(io::Error::last_os_error()) } else { let bottom = (stack.bottom() as usize + page_size) as *mut c_void; Ok(SysStack::new(stack.top(), bottom)) } } pub unsafe fn deallocate_stack(ptr: *mut c_void, size: usize) { libc::munmap(ptr as *mut libc::c_void, size); } pub fn page_size() -> usize { static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); let mut ret = PAGE_SIZE.load(Ordering::Relaxed); if ret == 0 { unsafe { ret = libc::sysconf(libc::_SC_PAGESIZE) as usize; } PAGE_SIZE.store(ret, Ordering::Relaxed); } ret } pub fn min_stack_size() -> usize { // Previously libc::SIGSTKSZ has been used for this, but it proofed to be very unreliable, // because the resulting values varied greatly between platforms. page_size() } pub fn max_stack_size() -> usize { static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); let mut ret = PAGE_SIZE.load(Ordering::Relaxed); if ret == 0 { let mut limit = mem::MaybeUninit::uninit(); let limitret = unsafe { libc::getrlimit(libc::RLIMIT_STACK, limit.as_mut_ptr()) }; let limit = unsafe { limit.assume_init() }; if limitret == 0 { ret = if limit.rlim_max == libc::RLIM_INFINITY || limit.rlim_max > (usize::MAX as libc::rlim_t) { usize::MAX } else { limit.rlim_max as usize }; PAGE_SIZE.store(ret, Ordering::Relaxed); } else { ret = 1024 * 1024 * 1024; } } ret } generator-0.7.1/src/stack/windows.rs000064400000000000000000000035220072674642500156050ustar 00000000000000use std::io; use std::mem; use std::os::raw::c_void; use std::ptr; use std::sync::atomic::{AtomicUsize, Ordering}; use std::usize; use windows::Win32::System::Memory::*; use windows::Win32::System::SystemInformation::*; use super::SysStack; pub unsafe fn allocate_stack(size: usize) -> io::Result { let ptr = VirtualAlloc(ptr::null(), size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if ptr.is_null() { Err(io::Error::last_os_error()) } else { Ok(SysStack::new( (ptr as usize + size) as *mut c_void, ptr as *mut c_void, )) } } pub unsafe fn protect_stack(stack: &SysStack) -> io::Result { let page_size = page_size(); let mut old_prot = PAGE_PROTECTION_FLAGS(0); debug_assert!(stack.len() % page_size == 0 && stack.len() != 0); let ret = VirtualProtect( stack.bottom(), page_size, PAGE_READONLY | PAGE_GUARD, &mut old_prot, ); if !ret.as_bool() { Err(io::Error::last_os_error()) } else { let bottom = (stack.bottom() as usize + page_size) as *mut c_void; Ok(SysStack::new(stack.top(), bottom)) } } pub unsafe fn deallocate_stack(ptr: *mut c_void, _: usize) { VirtualFree(ptr, 0, MEM_RELEASE); } pub fn page_size() -> usize { static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); let mut ret = PAGE_SIZE.load(Ordering::Relaxed); if ret == 0 { ret = unsafe { let mut info = mem::zeroed(); GetSystemInfo(&mut info); info.dwPageSize as usize }; PAGE_SIZE.store(ret, Ordering::Relaxed); } ret } // Windows does not seem to provide a stack limit API pub fn min_stack_size() -> usize { page_size() } // Windows does not seem to provide a stack limit API pub fn max_stack_size() -> usize { usize::MAX } generator-0.7.1/src/yield_.rs000064400000000000000000000105130072674642500142510ustar 00000000000000//! # yield //! //! generator yield implementation //! use std::any::Any; use std::sync::atomic; use crate::gen_impl::Generator; use crate::reg_context::RegContext; use crate::rt::{is_generator, Context, ContextStack, Error}; /// it's a special return instruction that yield nothing /// but only terminate the generator safely #[macro_export] macro_rules! done { () => {{ return $crate::done(); }}; } /// don't use it directly, use done!() macro instead /// would panic if use in none generator context #[doc(hidden)] #[inline] pub fn done() -> T { assert!(is_generator(), "done is only possible in a generator"); std::panic::panic_any(Error::Done) } /// switch back to parent context #[inline] pub fn yield_now() { let env = ContextStack::current(); let cur = env.top(); raw_yield_now(&env, cur); } #[inline] pub fn raw_yield_now(env: &ContextStack, cur: &mut Context) { let parent = env.pop_context(cur as *mut _); RegContext::swap(&mut cur.regs, &parent.regs); } /// raw yield without catch passed in para #[inline] fn raw_yield(env: &ContextStack, context: &mut Context, v: T) { // check the context if !context.is_generator() { panic!("yield from none generator context"); } context.set_ret(v); context._ref -= 1; raw_yield_now(env, context); // here we just panic to exit the func if context._ref != 1 { std::panic::panic_any(Error::Cancel); } } /// yield something without catch passed in para #[inline] #[deprecated(since = "0.6.18", note = "please use `scope` version instead")] pub fn yield_with(v: T) { let env = ContextStack::current(); let context = env.top(); raw_yield(&env, context, v); } /// get the passed in para #[inline] #[deprecated(since = "0.6.18", note = "please use `scope` version instead")] pub fn get_yield() -> Option { let context = ContextStack::current().top(); raw_get_yield(context) } /// get the passed in para from context #[inline] fn raw_get_yield(context: &mut Context) -> Option { // check the context if !context.is_generator() { { error!("get yield from none generator context"); std::panic::panic_any(Error::ContextErr); } } context.get_para() } /// yield and get the send para // here yield need to return a static lifetime value, which is Any required // this is fine, but it's totally safe that we can refer to the function block // since we will come back later #[inline] #[deprecated(since = "0.6.18", note = "please use `scope` version instead")] pub fn yield_(v: T) -> Option { let env = ContextStack::current(); let context = env.top(); raw_yield(&env, context, v); atomic::compiler_fence(atomic::Ordering::Acquire); raw_get_yield(context) } /// `yield_from` #[deprecated(since = "0.6.18", note = "please use `scope` version instead")] pub fn yield_from(mut g: Generator) -> Option { let env = ContextStack::current(); let context = env.top(); let mut p = context.get_para(); while !g.is_done() { match g.raw_send(p) { None => return None, Some(r) => raw_yield(&env, context, r), } p = context.get_para(); } drop(g); // explicitly consume g p } /// coroutine yield pub fn co_yield_with(v: T) { let env = ContextStack::current(); let context = env.co_ctx().unwrap(); // check the context, already checked in co_ctx() // if !context.is_generator() { // info!("yield from none coroutine context"); // // do nothing, just return // return; // } // here we just panic to exit the func if context._ref != 1 { std::panic::panic_any(Error::Cancel); } context.co_set_ret(v); context._ref -= 1; let parent = env.pop_context(context); let top = unsafe { &mut *context.parent }; // here we should use the top regs RegContext::swap(&mut top.regs, &parent.regs); } /// coroutine get passed in yield para pub fn co_get_yield() -> Option { ContextStack::current() .co_ctx() .and_then(|ctx| ctx.co_get_para()) } /// set current coroutine para in user space pub fn co_set_para(para: A) { if let Some(ctx) = ContextStack::current().co_ctx() { ctx.co_set_para(para) } } generator-0.7.1/tests/lib.rs000064400000000000000000000261220072674642500141300ustar 00000000000000#![allow(deprecated)] #![allow(unused_assignments)] extern crate generator; use generator::*; #[test] fn test_return() { let mut g = Gn::new_scoped(|_s| 42u32); assert_eq!(g.next(), Some(42)); assert!(g.is_done()); } #[test] fn generator_is_done() { let mut g = Gn::<()>::new(|| { yield_with(()); }); g.next(); assert!(!g.is_done()); g.next(); assert!(g.is_done()); } #[test] fn generator_is_done1() { let mut g = Gn::new_scoped(|mut s| { s.yield_(2); done!(); }); assert_eq!(g.next(), Some(2)); assert!(!g.is_done()); assert_eq!(g.next(), None); assert!(g.is_done()); } #[test] fn generator_is_done_with_drop() { let mut g = Gn::new_scoped(|mut s| { s.yield_(String::from("string")); done!(); }); assert_eq!(g.next(), Some(String::from("string"))); assert!(!g.is_done()); assert_eq!(g.next(), None); assert!(g.is_done()); } #[test] fn test_yield_a() { let mut g = Gn::::new(|| { let r: i32 = yield_(10).unwrap(); r * 2 }); // first start the generator let i = g.raw_send(None).unwrap(); assert_eq!(i, 10); let i = g.send(3); assert_eq!(i, 6); assert!(g.is_done()); } #[test] fn test_yield_with() { let mut g = Gn::new(|| { yield_with(10); 20 }); // the para type could be deduced here let i = g.send(()); assert!(i == 10); let j = g.next(); assert!(j.unwrap() == 20); } #[test] #[should_panic] fn test_yield_with_type_error() { let mut g = Gn::<()>::new(|| { // yield_with::(10); yield_with(10u32); 20i32 }); g.next(); } #[test] #[should_panic] fn test_get_yield_type_error() { let mut g = Gn::::new(|| { get_yield::(); }); g.send(10); } #[test] #[should_panic] fn test_deep_yield_with_type_error() { let mut g = Gn::<()>::new(|| { let mut g = Gn::<()>::new(|| { yield_with(0); }); g.next(); }); g.next(); } #[test] fn test_scoped() { use std::cell::RefCell; use std::rc::Rc; let x = Rc::new(RefCell::new(10)); let x1 = x.clone(); let mut g = Gn::<()>::new_scoped_local(move |mut s| { *x1.borrow_mut() = 20; s.yield_with(()); *x1.borrow_mut() = 5; }); g.next(); assert!(*x.borrow() == 20); g.next(); assert!(*x.borrow() == 5); assert!(g.is_done()); } #[test] fn test_scoped_1() { let mut x = 10; { let mut g = Gn::<()>::new(|| { x = 5; }); g.next(); } assert!(x == 5); } #[test] fn test_scoped_yield() { let mut g = Gn::new_scoped(|mut s| { let mut i = 0; loop { let v = s.yield_(i); i += 1; match v { Some(x) => { // dbg!(x, i); assert_eq!(x, i); } None => { // for elegant exit break; } } } 20usize }); // start g g.raw_send(None); for i in 1..100 { let data: usize = g.send(i); assert_eq!(data, i); } // quit g g.raw_send(None); } #[test] fn test_inner_ref() { let mut g = Gn::<()>::new_scoped(|mut s| { use std::mem; // setup something let mut x: u32 = 10; // return internal ref not compiled because the // lifetime of internal ref is smaller than the generator // but the generator interface require the return type's // lifetime bigger than the generator // the x memory remains on heap even returned! // the life time of x is associated with the generator // however modify this internal value is really unsafe // but this is useful pattern for setup and teardown // which can be put in the same place // s.yield_(&mut x); s.yield_(unsafe { mem::transmute(&mut x) }); // this was modified by the invoker assert!(x == 5); // teardown happened when the generator get dropped // this is just a safe dummy ret static mut RET: u32 = 0; unsafe { &mut RET } }); // use the resource setup from generator let a = g.next().unwrap(); assert!(*a == 10); *a = 5; // a keeps valid until the generator dropped } #[test] fn test_drop() { let mut x = 10; { let mut g = Gn::<()>::new(|| { x = 1; yield_with(()); x = 5; }); g.send(()); } assert!(x == 1); } #[test] fn test_ill_drop() { let mut x = 10u32; { Gn::::new(|| { x = 5; // here we got None from drop x = get_yield().unwrap_or(0); }); // not started the gen, change nothing } assert!(x == 10); } #[test] fn test_loop_drop() { let mut x = 10u32; { let mut g = Gn::<()>::new(|| { x = 5; loop { yield_with(()); } }); g.send(()); // here the generator drop will cancel the loop } assert!(x == 5); } #[test] fn test_panic_inside() { use std::panic::{catch_unwind, AssertUnwindSafe}; let mut x = 10; { let mut wrapper = AssertUnwindSafe(&mut x); if let Err(panic) = catch_unwind(move || { let mut g = Gn::<()>::new(|| { **wrapper = 5; panic!("panic inside!"); }); g.resume(); }) { match panic.downcast_ref::<&str>() { // why can't get the message here?? is it lost? Some(msg) => println!("get panic: {:?}", msg), None => println!("can't get panic message"), } } // wrapper dropped here } assert!(x == 5); } #[test] #[allow(unreachable_code)] fn test_cancel() { let mut g = Gn::<()>::new(|| { let mut i = 0; loop { yield_with(i); i += 1; } i }); loop { let i = g.next().unwrap(); if i > 10 { g.cancel(); break; } } assert!(g.is_done()); } #[test] #[should_panic] fn test_yield_from_functor_context() { // this is not run from generator yield_::<(), _>(0); } #[test] #[should_panic] fn test_yield_with_from_functor_context() { // this is not run from generator yield_with(0); } #[test] fn test_yield_from_generator_context() { let mut g = Gn::<()>::new(|| { let mut g1 = Gn::<()>::new(|| { yield_with(5); 10 }); let i = g1.send(()); yield_with(i); 0 }); let n = g.send(()); assert!(n == 5); let n = g.send(()); assert!(n == 0); } #[test] fn test_yield_from() { let mut g = Gn::<()>::new(|| { let g1 = Gn::<()>::new(|| { yield_with(5); 10 }); yield_from(g1); 0 }); let n = g.send(()); assert!(n == 5); let n = g.send(()); assert!(n == 10); let n = g.send(()); assert!(n == 0); assert!(g.is_done()); } #[test] fn test_yield_from_send() { let mut g = Gn::::new(|| { let g1 = Gn::::new(|| { let mut i: u32 = yield_(1u32).unwrap(); i = yield_(i * 2).unwrap(); i * 2 }); let i = yield_from(g1).unwrap(); assert_eq!(i, 10); // here we need a unused return to indicate this function's return type 0u32 }); // first start the generator let n = g.raw_send(None).unwrap(); assert!(n == 1); let n = g.send(3); assert!(n == 6); let n = g.send(4); assert!(n == 8); let n = g.send(10); assert!(n == 0); assert!(g.is_done()); } #[test] #[should_panic] fn test_yield_from_send_type_miss_match() { let mut g = Gn::::new(|| { let g1 = Gn::::new(|| { let mut i: u32 = yield_(1u32).unwrap(); i = yield_(i * 2).unwrap(); i * 2 }); yield_from(g1); // here the return type should be 0u32 0 }); let n = g.send(3); assert!(n == 1); let n = g.send(4); assert!(n == 6); let n = g.send(10); assert!(n == 8); // the last send has no meaning for the return let n = g.send(0); assert!(n == 0); assert!(g.is_done()); } // windows has it's own check, this test would make the app abort // #[test] // #[should_panic] // fn test_stack_overflow() { // // here the stack size is not big enough // // and will panic when get detected in drop // let clo = || { // let big_data = [0usize; 0x400]; // println!("this would overflow the stack, {}", big_data[100]); // }; // Gn::<()>::new_opt(clo, 10); // } #[test] fn test_scope_gen() { // now we can even deduce the input para type let mut g = Gn::new_scoped(|mut s| { let i = s.yield_(0).unwrap(); // below would have a compile error, nice! // s.yield_(Box::new(0)); i * 2 }); assert_eq!(g.raw_send(None), Some(0)); assert_eq!(g.raw_send(Some(3)), Some(6)); assert_eq!(g.raw_send(None), None); } #[test] fn test_scope_yield_from_send() { let mut g = Gn::new_scoped(|mut s| { let g1 = Gn::new_scoped(|mut s| { let mut i: u32 = s.yield_(1u32).unwrap(); i = s.yield_(i * 2).unwrap(); i * 2 }); let i = s.yield_from(g1).unwrap(); // here the return type should be 0u32 i * 2 }); let n = g.send(3); assert_eq!(n, 1); let n = g.send(4); assert_eq!(n, 8); let n = g.send(10); assert_eq!(n, 20); // the last send has no meaning for the return let n = g.send(7); assert!(n == 14); assert!(g.is_done()); } #[test] fn test_re_init() { let clo = || { |mut s: Scope<(), _>| { s.yield_(0); s.yield_(3); 5 } }; let mut g = Gn::new_opt(0x800, || 0); g.scoped_init(clo()); assert_eq!(g.next(), Some(0)); assert_eq!(g.next(), Some(3)); assert_eq!(g.next(), Some(5)); assert!(g.is_done()); // re-init generator g.scoped_init(clo()); assert_eq!(g.next(), Some(0)); assert_eq!(g.next(), Some(3)); assert_eq!(g.next(), Some(5)); assert!(g.is_done()); } #[test] #[should_panic] fn done_in_normal() { done!(); } #[test] #[should_panic] fn invalid_yield_in_scope() { let g = Gn::new_scoped(|_| { // invalid use raw yield API with scope yield_::(()); }); for () in g {} } #[test] fn test_yield_float() { let mut g = Gn::::new(|| { let r: f64 = yield_(10.0).unwrap(); let x = r * 2.0; // 6 let y = x * 9.0; // 54 let z = y / 3.0; // 18 let r: f64 = yield_(6.0).unwrap(); x * r * y * z }); // first start the generator let i = g.raw_send(None).unwrap(); let x = i * 10.0; assert_eq!(i, 10.0); let i = g.send(3.0); assert_eq!(i, 6.0); let i = g.send(x / 25.0); assert_eq!(i, 23328.0); assert!(g.is_done()); }