generator-0.6.20/build.rs000064400000000000000000000040771361623630400134340ustar0000000000000000extern crate cc; extern crate rustc_version; use std::env; use std::path::PathBuf; use rustc_version::{version_meta, Channel}; fn main() { // Set cfg flags depending on release channel if let Channel::Nightly = version_meta().unwrap().channel { return 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" => "arm64", // "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" | "arm64" => "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"); } generator-0.6.20/Cargo.toml.orig000064400000000000000000000016001361624757400146560ustar0000000000000000[package] name = "generator" version = "0.6.20" 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] winapi = { version = "0.3", features = ["memoryapi", "sysinfoapi"] } [target.'cfg(unix)'.dependencies] libc = "0.2" [dependencies] log = "0.4" [build-dependencies] cc = "1.0" rustc_version = "0.2" # release build [profile.release] lto = true generator-0.6.20/Cargo.toml0000644000000026221361625175100112140ustar00# 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 believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "generator" version = "0.6.20" 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.rustc_version] version = "0.2" [target."cfg(unix)".dependencies.libc] version = "0.2" [target."cfg(windows)".dependencies.winapi] version = "0.3" features = ["memoryapi", "sysinfoapi"] generator-0.6.20/examples/cd.rs000064400000000000000000000025301353313737200145340ustar0000000000000000use generator::*; #[derive(Debug)] enum Action { Play(&'static str), Stop, } #[derive(Debug, Clone, Copy)] 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 { 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 { cd_player.send(Play("hello world")); cd_player.send(Play("hello another day")); cd_player.send(Stop); cd_player.send(Stop); cd_player.send(Play("hello another day")); cd_player.send(Stop); } } generator-0.6.20/examples/fib.rs000064400000000000000000000004761353313741400147120ustar0000000000000000use 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); } } generator-0.6.20/examples/get_yield.rs000064400000000000000000000007111361623453300161110ustar0000000000000000#![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.6.20/examples/number.rs000064400000000000000000000010271353313742400154340ustar0000000000000000use 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.6.20/examples/pipe.rs000064400000000000000000000015371353313743400151100ustar0000000000000000use generator::*; fn main() { // fn square<'a, T: Iterator + 'a>(input: T) -> impl Iterator + 'a { fn square<'a, T: Iterator + '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 + '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.6.20/examples/range.rs000064400000000000000000000004621353313744600152460ustar0000000000000000use 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.6.20/examples/send.rs000064400000000000000000000014041353313750700150760ustar0000000000000000#![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.6.20/examples/yield_from.rs000064400000000000000000000007141353313731500162760ustar0000000000000000#![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.6.20/LICENSE-APACHE000064400000000000000000000261331351350747400137150ustar0000000000000000 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.6.20/LICENSE-MIT000064400000000000000000000020371351350747400134220ustar0000000000000000Copyright (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.6.20/README.md000064400000000000000000000035611351350747400132500ustar0000000000000000[![Build Status](https://travis-ci.org/Xudong-Huang/generator-rs.svg?branch=master)](https://travis-ci.org/Xudong-Huang/generator-rs) [![Build status](https://ci.appveyor.com/api/projects/status/x3mix8q57ygwqcgb/branch/master?svg=true)](https://ci.appveyor.com/project/Xudong-Huang/generator-rs/branch/master) [![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 stackfull generator library ```toml [dependencies] generator = "0.6" ``` ## Usage ```rust #[macro_use] extern crate generator; use generator::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 ## 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.6.20/src/detail/asm/asm_x86_64_ms_pe_gas.asm000064400000000000000000000061761351350747400211240ustar0000000000000000.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.6.20/src/detail/asm/asm_x86_64_ms_pe_masm.asm000064400000000000000000000046611361623612600213010ustar0000000000000000.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.6.20/src/detail/asm/asm_x86_64_sysv_elf_gas.S000064400000000000000000000022001351350747400212550ustar0000000000000000.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 %rdi, (3*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 mov (3*8)(%rsi), %rdi ret .size bootstrap_green_task,.-bootstrap_green_task /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits generator-0.6.20/src/detail/asm/asm_x86_64_sysv_macho_gas.S000064400000000000000000000015211351350747400216030ustar0000000000000000.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 %rdi, (3*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 mov (3*8)(%rsi), %rdi ret generator-0.6.20/src/detail/mod.rs000064400000000000000000000035551353314374300151470ustar0000000000000000// 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; 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 isizes only #[inline] 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.6.20/src/detail/x86_64_unix.rs000064400000000000000000000075601351350747400163740ustar0000000000000000use 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) { asm!( "prefetcht1 $0" : // no output : "m"(*data) : : "volatile" ); } #[naked] #[inline(never)] pub unsafe extern "C" fn bootstrap_green_task() { 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 adrress " : // 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 asm!( "" : : "{rdi}"(out_regs), "{rsi}"(in_regs) : : ); // introduce this function to workaround rustc bug! (#6) #[naked] unsafe extern "C" fn _swap_reg() { // Save registers 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 %rdi, (3*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 mov (3*8)(%rsi), %rdi " : : //"{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.6.20/src/detail/x86_64_windows.rs000064400000000000000000000170731351350747400171030ustar0000000000000000use 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) { asm!( "prefetcht1 $0" : // no output : "m"(*data) : : "volatile" ); } #[naked] #[inline(never)] pub unsafe extern "C" fn bootstrap_green_task() { 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 adrress " : // 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 asm!( "" : : "{rcx}"(out_regs), "{rdx}"(in_regs) : : ); // introduce this function to workaround rustc bug! (#6) #[naked] unsafe extern "C" fn _swap_reg() { // Save registers 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))] #[cfg_attr(not(nightly), repr(C))] #[allow(non_camel_case_types)] #[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.6.20/src/gen_impl.rs000064400000000000000000000264521361623453300147210ustar0000000000000000//! # 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::yield_::yield_now; // default stack size, in usize // windows has a minimal size as 0x4a8!!!! pub const DEFAULT_STACK_SIZE: usize = 0x1000; /// Generator helper pub struct Gn { dummy: PhantomData, } /// the generator type pub type Generator<'a, A, T> = Box>; unsafe impl Send for GeneratorImpl<'static, A, T> {} 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 + 'a, T: 'a, A: 'a, { Self::new_scoped_opt(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 + 'a, T: 'a, A: 'a, { let mut g = GeneratorImpl::::new(size); g.scoped_init(f); g } } 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 + '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 + 'a, { let mut g = GeneratorImpl::::new(size); g.init_context(); g.init(f); g } } /// `GeneratorImpl` #[repr(C)] pub struct GeneratorImpl<'a, A, T> { // run time context context: Context, // save the input para: Option, // save the output ret: Option, // boxed functor f: Option>, } impl<'a, A: Any, T: Any> GeneratorImpl<'a, A, T> { /// create a new generator with default stack size pub 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 pub fn new(size: usize) -> Box { Box::new(GeneratorImpl { para: None, ret: None, f: None, context: Context::new(size), }) } /// prefetch the generator into cache #[inline] pub fn prefetch(&self) { self.context.regs.prefetch(); } /// init a heap based generator with scoped closure pub 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(move || f(scope)); } /// init a heap based generator // it's can be used to re-init a 'done' generator before it's get dropped pub fn init 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 { unsafe { self.cancel(); } } // 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 _; let context = &mut self.context as *mut Context; // windows box::new is quite slow than unix self.f = Some(Box::new(move || { let r = f(); let ret = unsafe { &mut *ret }; let _ref = unsafe { (*context)._ref }; if _ref == 0xf { ::std::mem::forget(r); *ret = None; // this is a done return } else { *ret = Some(r); // normal return } })); let stk = &self.context.stack; self.context .regs .init_with(gen_init, 0, &mut self.f as *mut _ as *mut usize, stk); } /// 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 #[cold] 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] pub fn set_para(&mut self, para: A) { self.para = Some(para); } /// set the generator local data #[inline] pub fn set_local_data(&mut self, data: *mut u8) { self.context.local_data = data; } /// get the generator local data #[inline] pub fn get_local_data(&self) -> *mut u8 { self.context.local_data } /// get the generator panic data #[inline] pub 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] pub fn resume(&mut self) -> Option { if self.is_done() { #[cold] 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] pub fn raw_send(&mut self, para: Option) -> Option { if self.is_done() { #[cold] 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 pub 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] unsafe 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, it's unsafe in that you must care about the resource pub unsafe 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] pub fn is_done(&self) -> bool { self.is_started() && (self.context._ref & 0x3) != 0 } /// get stack total size and used size in word pub fn stack_usage(&self) -> (usize, usize) { ( self.context.stack.size(), self.context.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() { warn!("generator is not done while drop"); unsafe { 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!"); panic!(Error::StackErr); } } } impl<'a, T> Iterator for GeneratorImpl<'a, (), T> { type Item = T; // The 'Iterator' trait only requires the 'next' method to be defined. The // return type is 'Option', 'None' is returned when the 'Iterator' is // over, otherwise the next value is returned wrapped in 'Some' fn next(&mut self) -> Option { self.resume() } } impl<'a, A, T> fmt::Debug for GeneratorImpl<'a, A, T> { #[cfg(nightly)] #[allow(unused_unsafe)] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use std::intrinsics::type_name; write!( f, "Generator<{}, Output={}> {{ ... }}", unsafe { type_name::() }, unsafe { type_name::() } ) } #[cfg(not(nightly))] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Generator {{ ... }}") } } /// 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(); }; fn check_err(cause: Box) { if let Some(&Error::Cancel) = cause.downcast_ref::() { // this is not an error at all, ignore it return; } error!("set panicked 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) = panic::catch_unwind(clo) { check_err(cause); } yield_now(); unreachable!("Should never comeback"); } generator-0.6.20/src/lib.rs000064400000000000000000000013411361623453300136630ustar0000000000000000//! # generator //! //! Rust generator library //! #![cfg_attr(nightly, feature(asm))] #![cfg_attr(nightly, feature(repr_simd))] #![cfg_attr(nightly, feature(core_intrinsics))] #![cfg_attr(nightly, feature(naked_functions))] #![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, GeneratorImpl, Gn}; 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.6.20/src/reg_context.rs000064400000000000000000000061301351350747400154420ustar0000000000000000use 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, // which we will then modify to call the given function when restoredtack 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!("swapping contexts"); let out_regs: &mut Registers = match *out_context { RegContext { regs: ref mut r, .. } => r, }; let in_regs: &Registers = match *in_context { RegContext { regs: ref r, .. } => r, }; // debug!("register raw swap"); unsafe { swap_registers(out_regs, in_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) }, unsafe { transmute(callback as usize) }, &stk, ); RegContext::swap(&mut cur, &ctx); unsafe { assert!(VAL); } } } generator-0.6.20/src/rt.rs000064400000000000000000000211761355377061700135620ustar0000000000000000//! # 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; use crate::stack::Stack; thread_local!( /// each thread has it's own generator context stack static ROOT_CONTEXT: Box = { let mut root = Box::new(Context::empty()); 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] 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 { /// 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, /// generator execution stack pub stack: Stack, /// 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 { // create for root empty context fn empty() -> Self { Context { regs: RegContext::empty(), stack: Stack::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(), } } /// return a default generator context pub fn new(size: usize) -> Context { Context { regs: RegContext::empty(), stack: Stack::new(size), 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(), #[cold] 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), // #[cold] // 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), #[cold] 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)] #[cold] #[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] pub fn current() -> ContextStack { unsafe { if ROOT_CONTEXT_P.is_null() { #[cold] init_root_p(); } ContextStack { root: ROOT_CONTEXT_P, } } } #[cfg(not(nightly))] #[inline] 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); } panic!(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.6.20/src/scope.rs000064400000000000000000000047031351350747400142360ustar0000000000000000//! # yield //! //! generator yield implementation //! // use generator::Generator; use crate::gen_impl::GeneratorImpl; 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() { #[cold] 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 { #[cold] panic!(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); self.get_yield() } /// `yield_from` /// the from generator must has the same type as itself pub fn yield_from(&mut self, mut g: Box>) -> Option { let env = ContextStack::current(); let context = env.top(); let mut p = self.get_yield(); while !g.is_done() { match g.raw_send(p) { #[cold] None => return None, Some(r) => self.raw_yield(&env, context, r), } p = self.get_yield(); } drop(g); // explicitly consume g p } } generator-0.6.20/src/stack/mod.rs000064400000000000000000000134071361623454000150050ustar0000000000000000//! # generator stack //! //! use std::error::Error; use std::fmt::{Display, Formatter, Result as FmtResult}; use std::io; use std::os::raw::c_void; use std::ptr; #[cfg(all(unix, target_arch = "x86_64"))] #[path = "unix.rs"] pub mod sys; #[cfg(all(windows, target_arch = "x86_64"))] #[path = "windows.rs"] pub mod sys; /// 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 Formatter) -> FmtResult { 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 pub struct Stack { buf: SysStack, } impl Stack { pub fn empty() -> Stack { Stack { buf: SysStack { top: ptr::null_mut(), bottom: ptr::null_mut(), }, } } /// 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); } 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 { self.buf.top() as *mut _ } /// Point to the low end of the allocated stack #[allow(dead_code)] pub fn begin(&self) -> *mut usize { self.buf.bottom() as *mut _ } } impl Drop for Stack { fn drop(&mut 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); } } } generator-0.6.20/src/stack/unix.rs000064400000000000000000000060021361623453300152040ustar0000000000000000use std::io; use std::mem; use std::os::raw::c_void; use std::sync::atomic::{AtomicUsize, Ordering}; use std::usize; use libc; 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; let limitret; unsafe { limit = mem::MaybeUninit::uninit().assume_init(); limitret = libc::getrlimit(libc::RLIMIT_STACK, &mut limit); } 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.6.20/src/stack/windows.rs000064400000000000000000000042731351350747400157260ustar0000000000000000use std::io; use std::mem; use std::os::raw::c_void; use std::sync::atomic::{AtomicUsize, Ordering}; use std::usize; use winapi::shared::basetsd::SIZE_T; use winapi::shared::minwindef::{DWORD, LPVOID}; use winapi::um::memoryapi::{VirtualAlloc, VirtualFree, VirtualProtect}; use winapi::um::sysinfoapi::GetSystemInfo; use winapi::um::winnt::{ MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_GUARD, PAGE_READONLY, PAGE_READWRITE, }; use super::SysStack; pub unsafe fn allocate_stack(size: usize) -> io::Result { const NULL: LPVOID = 0 as LPVOID; const PROT: DWORD = PAGE_READWRITE; const TYPE: DWORD = MEM_COMMIT | MEM_RESERVE; let ptr = VirtualAlloc(NULL, size as SIZE_T, TYPE, PROT); if ptr == 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 { const TYPE: DWORD = PAGE_READONLY | PAGE_GUARD; let page_size = page_size(); let mut old_prot: DWORD = 0; debug_assert!(stack.len() % page_size == 0 && stack.len() != 0); let ret = { let page_size = page_size as SIZE_T; VirtualProtect(stack.bottom() as LPVOID, page_size, TYPE, &mut old_prot) }; 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, _: usize) { VirtualFree(ptr as LPVOID, 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.6.20/src/yield_.rs000064400000000000000000000107441353313750700143720ustar0000000000000000//! # yield //! //! generator yield implementation //! use std::any::Any; 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"); // set the done bit for this special return ContextStack::current().top()._ref = 0xf; unsafe { std::mem::MaybeUninit::uninit().assume_init() } } /// 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() { #[cold] 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 { #[cold] panic!(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() { #[cold] { error!("get yield from none generator context"); panic!(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); 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) { #[cold] 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 { #[cold] panic!(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 { match ContextStack::current().co_ctx() { Some(ctx) => ctx.co_get_para(), #[cold] None => None, } } /// set current coroutine para in user space pub fn co_set_para(para: A) { match ContextStack::current().co_ctx() { Some(ctx) => ctx.co_set_para(para), #[cold] None => {} } } generator-0.6.20/tests/lib.rs000064400000000000000000000240561353314646300142500ustar0000000000000000#![allow(deprecated)] extern crate generator; use generator::*; #[test] fn test_return() { let mut g = Gn::new_scoped(|_s| { return 42; }); 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(move || { *x1.borrow_mut() = 20; 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_inner_ref() { let mut g = Gn::<()>::new_scoped(|mut s| { use std::mem; use std::ptr; // setup something let mut x: u32 = 10; // return interal ref not compiled becuase the // lifetime of interal 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 assosiated with the generator // however modify this interal 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 unsafe { &mut *ptr::null_mut() } }); // 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; { // rust 1.17 can't deduce the output type! let mut g: Generator<_, ()> = 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 { // rust 1.17 can't deduce the output type! let i: i32 = g.next().unwrap(); if i > 10 { unsafe { 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 = GeneratorImpl::new(0x800); g.scoped_init(clo()); assert_eq!(g.next(), Some(0)); assert_eq!(g.next(), Some(3)); assert_eq!(g.next(), Some(5)); assert_eq!(g.is_done(), true); // 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_eq!(g.is_done(), true); } #[test] #[should_panic] fn done_in_normal() { done!(); } #[test] #[should_panic] fn invaild_yield_in_scope() { let g = Gn::new_scoped(|_| { // invalid use raw yield API with scope yield_::(()); }); for () in g {} } generator-0.6.20/.cargo_vcs_info.json0000644000000001121361625175100132060ustar00{ "git": { "sha1": "1b7c84653804d496b929ca27cac2290f3f1858af" } } generator-0.6.20/Cargo.lock0000644000000072321361625175100111730ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "cc" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "generator" version = "0.6.20" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"