Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/generator/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/generator/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.lock":"210516860ceae1b28a6ad8d63bae5e2a8f6d3dd9d19cd3a91473fb83021260e7","Cargo.toml":"f314161b4d546e175b8950bfd4fc64a894c7934e5ed95a4cdee78d0d6d645810","LICENSE-APACHE":"7ecb6108c3b8570339bb27e0f3affbe19300263a0e7a7e8c5f59a028a6eb70b3","LICENSE-MIT":"d0efb39501d05934b5d141573627ab9ead815287bb49acb37855642cf6a7205b","README.md":"8a9030898c86133b9da78b90891ac84684bb55c1b9b0c6559e27dd63734dd657","build.rs":"39000b53f5cd6b2207c595bf440a61d5c57e2997a803a1e0a022e116c06915ae","examples/cd.rs":"d1277917416a34b38ded808c91b42d6996789bcc8b99389df3c48f81087d876f","examples/fib.rs":"751e7763c55495f2283f5d7f1810c613061c18ec25cc829c532ba73608d8da16","examples/get_yield.rs":"50b18798fb5893bcec3e3077e0b1071a5b16e017555eebfd6b923cfe045d38cf","examples/number.rs":"b8b3dea96270ce08679de92396aa1921fdfdbb30cb6e04ef2ff75030ba03d3b9","examples/pipe.rs":"38aefcc21ba5ea766916efe228f61a1602f420e1ffd2103a86b5be9833ae9e92","examples/range.rs":"913ab37d50e849e66d19d659880f2441fa0904e1dcd7b4cf905f910a0e32784e","examples/send.rs":"a6a321342c1054ae550a14cd1566a7a8df6bfbf3771ffea9a2a0e9e2ac9501c2","examples/yield_from.rs":"a3be106d41a551635cccd888f269378f5cdc0d55693aac9e500c655468ea94bc","src/detail/aarch64_unix.rs":"ad0faa8626e4b6ea8ba7462426c8522c1e8318e61662cd042761fb8009638f37","src/detail/asm/asm_aarch64_aapcs_elf_gas.S":"5937bbe18cd90edbf32d8fc303d89402948d2583d60d0c45e2ef5ab506065ab7","src/detail/asm/asm_aarch64_aapcs_macho_gas.S":"8a783bf57d21aa58951697124226ca633e9fd5b69168c94e6ba9aa82c756730d","src/detail/asm/asm_x86_64_ms_pe_gas.asm":"13fb2d121b18ed34ed274b558a3633b67e313bb902e0e247b58cdc69f277fc7b","src/detail/asm/asm_x86_64_ms_pe_masm.asm":"ebd73594c1f781190967e56f566b5370d29502898e9f67630d96f2b1ced8edbc","src/detail/asm/asm_x86_64_sysv_elf_gas.S":"fa782aac93166041984fe446ff935a540634e715ee15fca9f8607c95824ce34f","src/detail/asm/asm_x86_64_sysv_macho_gas.S":"c07bfd39c4dc3d92c0a4cbcbd3f2e854b4d1ed8af73143cb52c92fc51be5e323","src/detail/mod.rs":"9ed0ffaaf27ae8c5b4afa4107bf97d010e229cfc7c1b2720d973e7a97214538b","src/detail/x86_64_unix.rs":"532b904755bf20b225d544b3e7dfb5630045b77df0694b27390a2980a9988bb2","src/detail/x86_64_windows.rs":"fc1ccd4946386d3ff9d7b7a8e2df46b7e31dac0cea3d8178a31fc8ac5d311b32","src/gen_impl.rs":"12ebf408c52d839efd1cbb300f0f9cff9be58c36e92be2057aded1c6783a5848","src/lib.rs":"bf0be905e6618ce72460d4dc790cc0efacb9534580f29d590995cf8e6bde2de3","src/reg_context.rs":"48c3ca651328398621660ab08d9f3fac76cafba05e11d0ddf98a149af68191b9","src/rt.rs":"a182e876dd822f2c0f57ee1947fa08c873aa7f02e286d9c7d38902bf29b5f472","src/scope.rs":"bf0a8519ad5fa97c9bde7158a7e50246e08dbd3ef17b9622d19af4268e7552e3","src/stack/mod.rs":"828aadd7030b5e72df8e2562d2bff1c2d9b0bb4c5c35fa629511237ebcaa1b1d","src/stack/unix.rs":"307a53302b8854de7a452b37a9914e0d57dab315243baf6c9199eec2425cbae1","src/stack/windows.rs":"7d323c01771ae5141d957b8231ec954243303020f288733966d7f8f544b0c27d","src/yield_.rs":"436ee491897e3bef59528a505f261ea594239fb222cb944027094c4e285604a0","tests/lib.rs":"de77fbbd81fdfa0028c1527801f3203f48755aec6b440c3c91beafd38f389ab8"},"package":"5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"}
|
||||
104
third-party/vendor/generator/Cargo.lock
generated
vendored
Normal file
104
third-party/vendor/generator/Cargo.lock
generated
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "generator"
|
||||
version = "0.7.5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"log",
|
||||
"rustversion",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.146"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
64
third-party/vendor/generator/Cargo.toml
vendored
Normal file
64
third-party/vendor/generator/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# 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 = "2021"
|
||||
name = "generator"
|
||||
version = "0.7.5"
|
||||
authors = ["Xudong Huang <huangxu008@hotmail.com>"]
|
||||
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.48"
|
||||
features = [
|
||||
"Win32_System_Memory",
|
||||
"Win32_Foundation",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_System_Diagnostics_Debug",
|
||||
]
|
||||
201
third-party/vendor/generator/LICENSE-APACHE
vendored
Normal file
201
third-party/vendor/generator/LICENSE-APACHE
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
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.
|
||||
25
third-party/vendor/generator/LICENSE-MIT
vendored
Normal file
25
third-party/vendor/generator/LICENSE-MIT
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (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.
|
||||
87
third-party/vendor/generator/README.md
vendored
Normal file
87
third-party/vendor/generator/README.md
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
[](https://github.com/Xudong-Huang/generator-rs/actions?query=workflow%3ACI)
|
||||
[](https://crates.io/crates/generator)
|
||||
[](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
|
||||
- x86_64 Fuchsia
|
||||
- aarch64 Linux
|
||||
- aarch64 macOS
|
||||
- aarch64 Fuchsia
|
||||
|
||||
## 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)
|
||||
87
third-party/vendor/generator/build.rs
vendored
Normal file
87
third-party/vendor/generator/build.rs
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
extern 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);
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
config.file(path);
|
||||
|
||||
// create the static asm libary
|
||||
config.compile("libasm.a");
|
||||
}
|
||||
|
||||
#[rustversion::nightly]
|
||||
const NIGHTLY: bool = true;
|
||||
|
||||
#[rustversion::not(nightly)]
|
||||
const NIGHTLY: bool = false;
|
||||
64
third-party/vendor/generator/examples/cd.rs
vendored
Normal file
64
third-party/vendor/generator/examples/cd.rs
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use 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);
|
||||
}
|
||||
}
|
||||
17
third-party/vendor/generator/examples/fib.rs
vendored
Normal file
17
third-party/vendor/generator/examples/fib.rs
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
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;
|
||||
s.yield_(b);
|
||||
}
|
||||
done!();
|
||||
});
|
||||
|
||||
for i in g {
|
||||
println!("{i}");
|
||||
}
|
||||
}
|
||||
24
third-party/vendor/generator/examples/get_yield.rs
vendored
Normal file
24
third-party/vendor/generator/examples/get_yield.rs
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#![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::<u32>::new(|| sum(1));
|
||||
let mut i = 1u32;
|
||||
while !s.is_done() {
|
||||
i = s.send(i);
|
||||
println!("{i}");
|
||||
}
|
||||
}
|
||||
30
third-party/vendor/generator/examples/number.rs
vendored
Normal file
30
third-party/vendor/generator/examples/number.rs
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
use 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::<u32>() == *n)
|
||||
.fold((), |_, n| {
|
||||
println!("n = {n}");
|
||||
})
|
||||
}
|
||||
29
third-party/vendor/generator/examples/pipe.rs
vendored
Normal file
29
third-party/vendor/generator/examples/pipe.rs
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use generator::*;
|
||||
|
||||
fn main() {
|
||||
// fn square<'a, T: Iterator<Item = u32> + 'a>(input: T) -> impl Iterator<Item = u32> + 'a {
|
||||
fn square<'a, T: Iterator<Item = u32> + 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<Item = u32> + 'a>(input: T) -> impl Iterator<Item = u32> + 'a {
|
||||
fn sum<'a, T: Iterator<Item = u32> + 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_{i:<2} = {sum:^4}");
|
||||
}
|
||||
}
|
||||
16
third-party/vendor/generator/examples/range.rs
vendored
Normal file
16
third-party/vendor/generator/examples/range.rs
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use 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}");
|
||||
}
|
||||
33
third-party/vendor/generator/examples/send.rs
vendored
Normal file
33
third-party/vendor/generator/examples/send.rs
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#![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::<u32>::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}");
|
||||
}
|
||||
}
|
||||
26
third-party/vendor/generator/examples/yield_from.rs
vendored
Normal file
26
third-party/vendor/generator/examples/yield_from.rs
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#![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
|
||||
});
|
||||
}
|
||||
69
third-party/vendor/generator/src/detail/aarch64_unix.rs
vendored
Normal file
69
third-party/vendor/generator/src/detail/aarch64_unix.rs
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use 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) {
|
||||
let ptr = self.gpr[12] as *const usize;
|
||||
unsafe {
|
||||
prefetch(ptr); // RSP
|
||||
prefetch(ptr.add(8)); // RSP + 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
60
third-party/vendor/generator/src/detail/asm/asm_aarch64_aapcs_elf_gas.S
vendored
Normal file
60
third-party/vendor/generator/src/detail/asm/asm_aarch64_aapcs_elf_gas.S
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
.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]
|
||||
|
||||
br x30
|
||||
.size swap_registers,.-swap_registers
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
51
third-party/vendor/generator/src/detail/asm/asm_aarch64_aapcs_macho_gas.S
vendored
Normal file
51
third-party/vendor/generator/src/detail/asm/asm_aarch64_aapcs_macho_gas.S
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
.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]
|
||||
|
||||
br x30
|
||||
129
third-party/vendor/generator/src/detail/asm/asm_x86_64_ms_pe_gas.asm
vendored
Normal file
129
third-party/vendor/generator/src/detail/asm/asm_x86_64_ms_pe_gas.asm
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
.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
|
||||
prefetcht2 (%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
|
||||
pop %rax
|
||||
jmp %rax
|
||||
.seh_endproc
|
||||
|
||||
.section .drectve
|
||||
.ascii " -export:\"swap_registers\""
|
||||
110
third-party/vendor/generator/src/detail/asm/asm_x86_64_ms_pe_masm.asm
vendored
Normal file
110
third-party/vendor/generator/src/detail/asm/asm_x86_64_ms_pe_masm.asm
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
.code
|
||||
|
||||
prefetch_asm PROC FRAME
|
||||
.endprolog
|
||||
prefetcht2 [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]
|
||||
pop rax
|
||||
jmp rax
|
||||
swap_registers ENDP
|
||||
|
||||
END
|
||||
|
||||
48
third-party/vendor/generator/src/detail/asm/asm_x86_64_sysv_elf_gas.S
vendored
Normal file
48
third-party/vendor/generator/src/detail/asm/asm_x86_64_sysv_elf_gas.S
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
.text
|
||||
.globl prefetch
|
||||
.type prefetch,@function
|
||||
.align 16
|
||||
prefetch:
|
||||
prefetcht2 (%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
|
||||
|
||||
pop %rax
|
||||
jmp *%rax
|
||||
.size bootstrap_green_task,.-bootstrap_green_task
|
||||
|
||||
/* Mark that we don't need executable stack. */
|
||||
.section .note.GNU-stack,"",%progbits
|
||||
39
third-party/vendor/generator/src/detail/asm/asm_x86_64_sysv_macho_gas.S
vendored
Normal file
39
third-party/vendor/generator/src/detail/asm/asm_x86_64_sysv_macho_gas.S
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
.text
|
||||
.globl _prefetch
|
||||
.align 8
|
||||
_prefetch:
|
||||
prefetcht2 (%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
|
||||
|
||||
pop %rax
|
||||
jmp *%rax
|
||||
51
third-party/vendor/generator/src/detail/mod.rs
vendored
Normal file
51
third-party/vendor/generator/src/detail/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// 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<T>(ptr: *mut T, count: isize) -> *mut T {
|
||||
// use std::mem::size_of;
|
||||
// (ptr as isize + count * (size_of::<T>() as isize)) as *mut T
|
||||
unsafe { ptr.offset(count) }
|
||||
}
|
||||
149
third-party/vendor/generator/src/detail/x86_64_unix.rs
vendored
Normal file
149
third-party/vendor/generator/src/detail/x86_64_unix.rs
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
use 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) {
|
||||
let ptr = self.gpr[1] as *const usize;
|
||||
unsafe {
|
||||
prefetch(ptr); // RSP
|
||||
prefetch(ptr.add(8)); // RSP + 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
247
third-party/vendor/generator/src/detail/x86_64_windows.rs
vendored
Normal file
247
third-party/vendor/generator/src/detail/x86_64_windows.rs
vendored
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
use 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) {
|
||||
let ptr = self.gpr[1] as *const usize;
|
||||
unsafe {
|
||||
prefetch(ptr); // RSP
|
||||
prefetch(ptr.add(8)); // RSP + 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
577
third-party/vendor/generator/src/gen_impl.rs
vendored
Normal file
577
third-party/vendor/generator/src/gen_impl.rs
vendored
Normal file
|
|
@ -0,0 +1,577 @@
|
|||
//! # 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;
|
||||
|
||||
/// The default stack size for generators, in bytes.
|
||||
// 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<GeneratorImpl<'a, A, T>>,
|
||||
}
|
||||
|
||||
/// 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<A: Send, T: Send> 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<F: FnOnce(Scope<'a, A, T>) -> 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<F: FnOnce() -> 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<F: FnOnce(Scope<'a, A, T>) -> 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<Box<dyn Any + Send>> {
|
||||
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<T> {
|
||||
self.gen.resume()
|
||||
}
|
||||
|
||||
/// `raw_send`
|
||||
#[inline]
|
||||
pub fn raw_send(&mut self, para: Option<A>) -> Option<T> {
|
||||
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<T> {
|
||||
self.resume()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A, T, const LOCAL: bool> fmt::Debug for GeneratorObj<'a, A, T, LOCAL> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Generator<{}, Output={}, Local={}> {{ ... }}",
|
||||
std::any::type_name::<A>(),
|
||||
std::any::type_name::<T>(),
|
||||
LOCAL
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generator helper
|
||||
pub struct Gn<A = ()> {
|
||||
dummy: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A> Gn<A> {
|
||||
/// 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<A, T>) -> 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<A, T>) -> 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<A, T>) -> T + Send + 'a,
|
||||
T: Send + 'a,
|
||||
A: Send + 'a,
|
||||
{
|
||||
let mut gen = GeneratorImpl::<A, T>::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<A, T>) -> T + 'a,
|
||||
T: 'a,
|
||||
A: 'a,
|
||||
{
|
||||
let mut gen = GeneratorImpl::<A, T>::new(Stack::new(size));
|
||||
gen.scoped_init(f);
|
||||
LocalGenerator { gen }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Any> Gn<A> {
|
||||
/// 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::<A, T>::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<A>,
|
||||
// save the output
|
||||
ret: Option<T>,
|
||||
// boxed functor
|
||||
f: Option<Func>,
|
||||
// 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<Self> {
|
||||
// the stack box would finally dealloc the stack!
|
||||
unsafe {
|
||||
let mut stack_box = stack.alloc_uninit_box::<GeneratorImpl<'a, A, T>>();
|
||||
(*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<F: FnOnce(Scope<'a, A, T>) -> 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<F: FnOnce() -> 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<Box<dyn Any + Send>> {
|
||||
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<T> {
|
||||
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<A>) -> Option<T> {
|
||||
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::<F>(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<F: FnOnce() -> R + panic::UnwindSafe, R>(f: F) -> std::thread::Result<R> {
|
||||
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::<Error>() {
|
||||
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<Func> = unsafe { &mut *(f as *mut _) };
|
||||
let func = f.take().unwrap();
|
||||
func.call_once();
|
||||
};
|
||||
|
||||
fn check_err(cause: Box<dyn Any + Send + 'static>) {
|
||||
if let Some(e) = cause.downcast_ref::<Error>() {
|
||||
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");
|
||||
}
|
||||
27
third-party/vendor/generator/src/lib.rs
vendored
Normal file
27
third-party/vendor/generator/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//! # generator
|
||||
//!
|
||||
//! Rust generator library
|
||||
//!
|
||||
|
||||
#![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, DEFAULT_STACK_SIZE};
|
||||
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,
|
||||
};
|
||||
106
third-party/vendor/generator/src/reg_context.rs
vendored
Normal file
106
third-party/vendor/generator/src/reg_context.rs
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
use 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
304
third-party/vendor/generator/src/rt.rs
vendored
Normal file
304
third-party/vendor/generator/src/rt.rs
vendored
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
//! # 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<Context> = {
|
||||
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]
|
||||
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)]
|
||||
#[repr(align(128))]
|
||||
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<Box<dyn Any + Send>>,
|
||||
}
|
||||
|
||||
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<A>(&mut self) -> Option<A>
|
||||
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::<Option<A>>() {
|
||||
Some(v) => v.take(),
|
||||
None => type_error::<A>("get yield type mismatch error detected"),
|
||||
}
|
||||
}
|
||||
|
||||
/// get coroutine send para
|
||||
#[inline]
|
||||
pub fn co_get_para<A>(&mut self) -> Option<A> {
|
||||
let para = unsafe {
|
||||
let para_ptr = *self.para.as_mut_ptr();
|
||||
debug_assert!(!para_ptr.is_null());
|
||||
&mut *(para_ptr as *mut Option<A>)
|
||||
};
|
||||
para.take()
|
||||
}
|
||||
|
||||
/// set current generator send para
|
||||
// #[inline]
|
||||
// pub fn set_para<A>(&self, data: A)
|
||||
// where
|
||||
// A: Any,
|
||||
// {
|
||||
// let para = unsafe { &mut *self.para };
|
||||
// match para.downcast_mut::<Option<A>>() {
|
||||
// Some(v) => *v = Some(data),
|
||||
// None => type_error::<A>("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<A>(&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<A>)
|
||||
};
|
||||
*para = Some(data);
|
||||
}
|
||||
|
||||
/// set current generator return value
|
||||
#[inline]
|
||||
pub fn set_ret<T>(&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::<Option<T>>() {
|
||||
Some(r) => *r = Some(v),
|
||||
None => type_error::<T>("yield type mismatch error detected"),
|
||||
}
|
||||
}
|
||||
|
||||
/// set coroutine return value
|
||||
/// without check the data type for coroutine performance reason
|
||||
#[inline]
|
||||
pub fn co_set_ret<T>(&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<T>)
|
||||
};
|
||||
*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<A>(msg: &str) -> ! {
|
||||
error!("{msg}, expected type: {}", std::any::type_name::<A>());
|
||||
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());
|
||||
}
|
||||
}
|
||||
90
third-party/vendor/generator/src/scope.rs
vendored
Normal file
90
third-party/vendor/generator/src/scope.rs
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
//! # 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<A>,
|
||||
ret: &'a mut Option<T>,
|
||||
}
|
||||
|
||||
impl<'a, A, T> Scope<'a, A, T> {
|
||||
/// create a new scope object
|
||||
pub(crate) fn new(para: &'a mut Option<A>, ret: &'a mut Option<T>) -> 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<A> {
|
||||
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<A> {
|
||||
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<A, T>) -> Option<A> {
|
||||
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
|
||||
}
|
||||
}
|
||||
424
third-party/vendor/generator/src/stack/mod.rs
vendored
Normal file
424
third-party/vendor/generator/src/stack/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
//! # 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::<StackBoxHeader>();
|
||||
const HEADER_SIZE: usize = std::mem::size_of::<StackBoxHeader>() / std::mem::size_of::<usize>();
|
||||
|
||||
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<T> {
|
||||
// the stack memory
|
||||
ptr: ptr::NonNull<T>,
|
||||
}
|
||||
|
||||
impl<T> StackBox<T> {
|
||||
/// create uninit stack box
|
||||
fn new_uninit(stack: &mut Stack, need_drop: usize) -> MaybeUninit<Self> {
|
||||
let offset = unsafe { &mut *stack.get_offset() };
|
||||
// alloc the data
|
||||
let layout = std::alloc::Layout::new::<T>();
|
||||
let align = std::cmp::max(layout.align(), ALIGN);
|
||||
let size = ((layout.size() + align - 1) & !(align - 1)) / std::mem::size_of::<usize>();
|
||||
let u_align = align / std::mem::size_of::<usize>();
|
||||
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<T>) -> *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<F: FnOnce()> StackBox<F> {
|
||||
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<T> std::ops::Deref for StackBox<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { self.ptr.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::DerefMut for StackBox<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.ptr.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for StackBox<T> {
|
||||
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 {size} bytes for a stack"
|
||||
),
|
||||
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<SysStack, StackError> {
|
||||
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 = i32::from(protected);
|
||||
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::<usize>();
|
||||
// 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::<usize>()
|
||||
}
|
||||
|
||||
/// 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<T>(&mut self) -> MaybeUninit<StackBox<T>> {
|
||||
// the first obj should set need drop to non zero
|
||||
StackBox::<T>::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 })
|
||||
}
|
||||
}
|
||||
123
third-party/vendor/generator/src/stack/unix.rs
vendored
Normal file
123
third-party/vendor/generator/src/stack/unix.rs
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
use 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<SysStack> {
|
||||
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<SysStack> {
|
||||
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()
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "fuchsia"))]
|
||||
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
|
||||
}
|
||||
|
||||
#[cfg(target_os = "fuchsia")]
|
||||
pub fn max_stack_size() -> usize {
|
||||
// Fuchsia doesn't have a platform defined hard cap.
|
||||
usize::MAX
|
||||
}
|
||||
82
third-party/vendor/generator/src/stack/windows.rs
vendored
Normal file
82
third-party/vendor/generator/src/stack/windows.rs
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use 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<SysStack> {
|
||||
let ptr = VirtualAlloc(
|
||||
Some(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<SysStack> {
|
||||
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
|
||||
}
|
||||
162
third-party/vendor/generator/src/yield_.rs
vendored
Normal file
162
third-party/vendor/generator/src/yield_.rs
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
//! # 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>() -> 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<T: Any>(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<T: Any>(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<A: Any>() -> Option<A> {
|
||||
let context = ContextStack::current().top();
|
||||
raw_get_yield(context)
|
||||
}
|
||||
|
||||
/// get the passed in para from context
|
||||
#[inline]
|
||||
fn raw_get_yield<A: Any>(context: &mut Context) -> Option<A> {
|
||||
// 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_<A: Any, T: Any>(v: T) -> Option<A> {
|
||||
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<A: Any, T: Any>(mut g: Generator<A, T>) -> Option<A> {
|
||||
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<T: Any>(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<A: Any>() -> Option<A> {
|
||||
ContextStack::current()
|
||||
.co_ctx()
|
||||
.and_then(|ctx| ctx.co_get_para())
|
||||
}
|
||||
|
||||
/// set current coroutine para in user space
|
||||
pub fn co_set_para<A: Any>(para: A) {
|
||||
if let Some(ctx) = ContextStack::current().co_ctx() {
|
||||
ctx.co_set_para(para)
|
||||
}
|
||||
}
|
||||
546
third-party/vendor/generator/tests/lib.rs
vendored
Normal file
546
third-party/vendor/generator/tests/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,546 @@
|
|||
#![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::<i32>::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::<i32>(10);
|
||||
yield_with(10u32);
|
||||
20i32
|
||||
});
|
||||
|
||||
g.next();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_get_yield_type_error() {
|
||||
let mut g = Gn::<u32>::new(|| {
|
||||
get_yield::<i32>();
|
||||
});
|
||||
|
||||
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::<u32>::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::<u32>::new(|| {
|
||||
let g1 = Gn::<u32>::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::<u32>::new(|| {
|
||||
let g1 = Gn::<u32>::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_::<String, _>(());
|
||||
});
|
||||
|
||||
for () in g {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_yield_float() {
|
||||
let mut g = Gn::<f64>::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());
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue