├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ ├── feature-request.yml │ ├── question.yml │ ├── task.yml │ └── volunteer.yml ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── ci-preemptive.sh │ ├── ci.sh │ ├── ci.yml │ ├── coverage.yml │ └── pr-audit.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-THIRD-PARTY ├── README.md ├── README_ZH.md ├── core ├── Cargo.toml ├── build.rs ├── c_src │ └── version.c ├── docs │ ├── cn │ │ ├── coroutine-pool.md │ │ ├── coroutine.md │ │ ├── monitor.md │ │ ├── ordered-work-steal.md │ │ ├── overview.md │ │ ├── scalable-stack.md │ │ ├── why-better.md │ │ └── work-steal.md │ └── en │ │ ├── coroutine-pool.md │ │ ├── coroutine.md │ │ ├── monitor.md │ │ ├── ordered-work-steal.md │ │ ├── overview.md │ │ ├── scalable-stack.md │ │ ├── why-better.md │ │ └── work-steal.md ├── src │ ├── co_pool │ │ ├── creator.rs │ │ ├── mod.rs │ │ ├── state.rs │ │ └── task.rs │ ├── common │ │ ├── beans.rs │ │ ├── ci.rs │ │ ├── constants.rs │ │ ├── macros.rs │ │ ├── mod.rs │ │ ├── ordered_work_steal.rs │ │ └── work_steal.rs │ ├── config.rs │ ├── coroutine │ │ ├── korosensei.rs │ │ ├── listener.rs │ │ ├── local.rs │ │ ├── mod.rs │ │ ├── state.rs │ │ └── suspender.rs │ ├── lib.rs │ ├── monitor.rs │ ├── net │ │ ├── event_loop.rs │ │ ├── join.rs │ │ ├── mod.rs │ │ ├── operator │ │ │ ├── linux │ │ │ │ ├── mod.rs │ │ │ │ └── tests.rs │ │ │ ├── mod.rs │ │ │ └── windows │ │ │ │ ├── mod.rs │ │ │ │ └── tests.rs │ │ └── selector │ │ │ ├── mio_adapter.rs │ │ │ ├── mod.rs │ │ │ └── polling_adapter.rs │ ├── scheduler.rs │ └── syscall │ │ ├── mod.rs │ │ ├── unix │ │ ├── accept.rs │ │ ├── accept4.rs │ │ ├── close.rs │ │ ├── connect.rs │ │ ├── fsync.rs │ │ ├── link.rs │ │ ├── listen.rs │ │ ├── lseek.rs │ │ ├── mkdir.rs │ │ ├── mkdirat.rs │ │ ├── mod.rs │ │ ├── nanosleep.rs │ │ ├── poll.rs │ │ ├── pread.rs │ │ ├── preadv.rs │ │ ├── pthread_cond_timedwait.rs │ │ ├── pthread_mutex_lock.rs │ │ ├── pthread_mutex_trylock.rs │ │ ├── pthread_mutex_unlock.rs │ │ ├── pwrite.rs │ │ ├── pwritev.rs │ │ ├── read.rs │ │ ├── readv.rs │ │ ├── recv.rs │ │ ├── recvfrom.rs │ │ ├── recvmsg.rs │ │ ├── renameat.rs │ │ ├── renameat2.rs │ │ ├── rmdir.rs │ │ ├── select.rs │ │ ├── send.rs │ │ ├── sendmsg.rs │ │ ├── sendto.rs │ │ ├── setsockopt.rs │ │ ├── shutdown.rs │ │ ├── sleep.rs │ │ ├── socket.rs │ │ ├── unlink.rs │ │ ├── usleep.rs │ │ ├── write.rs │ │ └── writev.rs │ │ └── windows │ │ ├── CreateFileW.rs │ │ ├── SetFilePointerEx.rs │ │ ├── Sleep.rs │ │ ├── WSAAccept.rs │ │ ├── WSAPoll.rs │ │ ├── WSARecv.rs │ │ ├── WSASend.rs │ │ ├── WSASocketW.rs │ │ ├── WaitOnAddress.rs │ │ ├── accept.rs │ │ ├── connect.rs │ │ ├── ioctlsocket.rs │ │ ├── listen.rs │ │ ├── mod.rs │ │ ├── recv.rs │ │ ├── select.rs │ │ ├── send.rs │ │ ├── setsockopt.rs │ │ ├── shutdown.rs │ │ └── socket.rs └── tests │ ├── co_pool.rs │ ├── coroutine.rs │ └── scheduler.rs ├── deny.toml ├── docs ├── cn │ ├── background.md │ └── why-rust.md ├── en │ ├── background.md │ └── why-rust.md └── img │ ├── begin.jpg │ ├── good.jpeg │ ├── just_do_it.jpg │ ├── run.jpg │ ├── rust.jpeg │ ├── watching.png │ └── what_else_can_I_say.jpg ├── hook ├── Cargo.toml ├── docs │ ├── cn │ │ └── hook.md │ ├── en │ │ └── hook.md │ └── img │ │ └── result-on-macos.png └── src │ ├── lib.rs │ └── syscall │ ├── mod.rs │ ├── unix.rs │ └── windows.rs ├── macros ├── Cargo.toml └── src │ └── lib.rs ├── open-coroutine ├── Cargo.toml ├── build.rs ├── examples │ ├── file_co.rs │ ├── file_not_co.rs │ ├── preemptive.rs │ ├── scalable_stack.rs │ ├── sleep_co.rs │ ├── sleep_not_co.rs │ ├── socket_co.rs │ ├── socket_co_client.rs │ ├── socket_co_server.rs │ └── socket_not_co.rs ├── src │ └── lib.rs └── tests │ ├── file_co.rs │ ├── file_not_co.rs │ ├── preemptive.rs │ ├── scalable_stack.rs │ ├── sleep_co.rs │ ├── sleep_not_co.rs │ ├── socket_co.rs │ ├── socket_co_client.rs │ ├── socket_co_server.rs │ └── socket_not_co.rs ├── publish.sh └── rustfmt.toml /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug Report 2 | description: Problems and issues with code of open-coroutine 🤔. 3 | title: "[BUG] title" 4 | labels: ["type: bug"] 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: Is there an existing issue for this? 9 | description: Please search to see if an issue already exists for the bug you encountered. 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - type: textarea 14 | attributes: 15 | label: Current Behavior 16 | description: A concise description of what you're experiencing. 17 | validations: 18 | required: false 19 | - type: textarea 20 | attributes: 21 | label: Expected Behavior 22 | description: A concise description of what you expected to happen. 23 | validations: 24 | required: false 25 | - type: textarea 26 | attributes: 27 | label: Steps To Reproduce 28 | description: Steps to reproduce the behavior. 29 | placeholder: | 30 | 1. In this environment... 31 | 2. With this config... 32 | 3. Run '...' 33 | 4. See error... 34 | validations: 35 | required: false 36 | - type: textarea 37 | attributes: 38 | label: Environment 39 | description: | 40 | examples: 41 | - **open-coroutine version(s)**: 0.0.1 42 | value: "open-coroutine version(s):" 43 | render: markdown 44 | validations: 45 | required: false 46 | - type: textarea 47 | attributes: 48 | label: Debug logs 49 | description: | 50 | Add your debug logs here. 51 | validations: 52 | required: false 53 | - type: textarea 54 | attributes: 55 | label: Anything else? 56 | description: | 57 | Links? References? Anything that will give us more context about the issue you are encountering! 58 | 59 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 60 | validations: 61 | required: false 62 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature Request 2 | description: I have a suggestion (and may want to implement it 🙂)! 3 | title: "[Feature] title" 4 | labels: ["type: new feature"] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Feature Request 9 | description: > 10 | Tip: Have you checked the GitHub issues whether someone else has already reported your issue? Maybe the feature already exists? 11 | placeholder: > 12 | A concise description of what you're experiencing. 13 | validations: 14 | required: false 15 | - type: textarea 16 | attributes: 17 | label: Is your feature request related to a problem? Please describe 18 | description: A clear and concise description of what the problem is. Ex. I have an issue when [...] 19 | validations: 20 | required: false 21 | - type: textarea 22 | attributes: 23 | label: Describe the solution you'd like 24 | description: A clear and concise description of what you want to happen. Add any considered drawbacks. 25 | validations: 26 | required: false 27 | - type: textarea 28 | attributes: 29 | label: Describe alternatives you've considered 30 | description: A clear and concise description of any alternative solutions or features you've considered. 31 | validations: 32 | required: false 33 | - type: textarea 34 | attributes: 35 | label: Additional context 36 | validations: 37 | required: false 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.yml: -------------------------------------------------------------------------------- 1 | name: 🤔 Question 2 | description: Usage question that isn't answered in docs or discussion 3 | title: "[Question] title" 4 | labels: ["type: question"] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Question 9 | placeholder: > 10 | your question here; 11 | validations: 12 | required: true 13 | - type: markdown 14 | attributes: 15 | value: | 16 | Please pay attention on issues you submitted, because we maybe need more details. 17 | If no response anymore and we cannot reproduce it on current information, we will **close it**. 18 | 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/task.yml: -------------------------------------------------------------------------------- 1 | name: 🚀 Task 2 | description: Used to create tasks for the community. 3 | title: "[Task] title" 4 | labels: ["status: volunteer wanted"] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Description 9 | placeholder: A clear and concise description of Task 10 | validations: 11 | required: true 12 | - type: textarea 13 | attributes: 14 | label: Task List 15 | placeholder: | 16 | 1. Task1... 17 | 2. Task2... 18 | 3. Task3... 19 | 4. Task4... 20 | validations: 21 | required: false 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/volunteer.yml: -------------------------------------------------------------------------------- 1 | name: 🤔 Volunteer 2 | description: Some volunteer help 3 | title: "[Volunteer] title" 4 | labels: ["status: volunteer wanted"] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Volunteer help 9 | placeholder: > 10 | your need volunteer help here; 11 | validations: 12 | required: true 13 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | // Describe your PR here; eg. Fixes #issueNo 2 | 3 | 6 | Make sure that: 7 | - [ ] You submit test cases (unit or integration tests) that back your changes. 8 | - [ ] Your local test passed. 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | allow: 9 | - dependency-type: direct 10 | - dependency-type: indirect 11 | -------------------------------------------------------------------------------- /.github/workflows/ci-preemptive.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -ex 4 | 5 | CARGO=cargo 6 | if [ "${CROSS}" = "1" ]; then 7 | export CARGO_NET_RETRY=5 8 | export CARGO_NET_TIMEOUT=10 9 | 10 | cargo install cross --git https://github.com/cross-rs/cross --rev c7dee4d008475ce1c140773cbcd6078f4b86c2aa 11 | CARGO=cross 12 | fi 13 | 14 | # If a test crashes, we want to know which one it was. 15 | export RUST_TEST_THREADS=1 16 | export RUST_BACKTRACE=1 17 | 18 | # test open-coroutine-core mod 19 | cd "${PROJECT_DIR}"/core 20 | "${CARGO}" test --target "${TARGET}" --features preemptive,ci 21 | "${CARGO}" test --target "${TARGET}" --features preemptive,ci --release 22 | 23 | # test open-coroutine 24 | cd "${PROJECT_DIR}"/open-coroutine 25 | "${CARGO}" test --target "${TARGET}" --features preemptive,ci 26 | "${CARGO}" test --target "${TARGET}" --features preemptive,ci --release 27 | 28 | # test io_uring 29 | if [ "${TARGET}" = "x86_64-unknown-linux-gnu" ]; then 30 | cd "${PROJECT_DIR}"/core 31 | "${CARGO}" test --target "${TARGET}" --no-default-features --features io_uring,preemptive,ci 32 | "${CARGO}" test --target "${TARGET}" --no-default-features --features io_uring,preemptive,ci --release 33 | cd "${PROJECT_DIR}"/open-coroutine 34 | "${CARGO}" test --target "${TARGET}" --no-default-features --features io_uring,preemptive,ci 35 | "${CARGO}" test --target "${TARGET}" --no-default-features --features io_uring,preemptive,ci --release 36 | fi 37 | 38 | # test IOCP 39 | if [ "${OS}" = "windows-latest" ]; then 40 | cd "${PROJECT_DIR}"/core 41 | "${CARGO}" test --target "${TARGET}" --no-default-features --features iocp,preemptive,ci 42 | "${CARGO}" test --target "${TARGET}" --no-default-features --features iocp,preemptive,ci --release 43 | cd "${PROJECT_DIR}"/open-coroutine 44 | "${CARGO}" test --target "${TARGET}" --no-default-features --features iocp,preemptive,ci 45 | "${CARGO}" test --target "${TARGET}" --no-default-features --features iocp,preemptive,ci --release 46 | fi 47 | -------------------------------------------------------------------------------- /.github/workflows/ci.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -ex 4 | 5 | CARGO=cargo 6 | if [ "${CROSS}" = "1" ]; then 7 | export CARGO_NET_RETRY=5 8 | export CARGO_NET_TIMEOUT=10 9 | 10 | cargo install cross --git https://github.com/cross-rs/cross --rev c7dee4d008475ce1c140773cbcd6078f4b86c2aa 11 | CARGO=cross 12 | fi 13 | 14 | # If a test crashes, we want to know which one it was. 15 | export RUST_TEST_THREADS=1 16 | export RUST_BACKTRACE=1 17 | 18 | # test open-coroutine-core mod 19 | cd "${PROJECT_DIR}"/core 20 | "${CARGO}" test --target "${TARGET}" --features ci 21 | "${CARGO}" test --target "${TARGET}" --features ci --release 22 | 23 | # test open-coroutine 24 | cd "${PROJECT_DIR}"/open-coroutine 25 | "${CARGO}" test --target "${TARGET}" --features ci 26 | "${CARGO}" test --target "${TARGET}" --features ci --release 27 | 28 | # test io_uring 29 | if [ "${TARGET}" = "x86_64-unknown-linux-gnu" ]; then 30 | cd "${PROJECT_DIR}"/core 31 | "${CARGO}" test --target "${TARGET}" --no-default-features --features io_uring,ci 32 | "${CARGO}" test --target "${TARGET}" --no-default-features --features io_uring,ci --release 33 | cd "${PROJECT_DIR}"/open-coroutine 34 | "${CARGO}" test --target "${TARGET}" --no-default-features --features io_uring,ci 35 | "${CARGO}" test --target "${TARGET}" --no-default-features --features io_uring,ci --release 36 | fi 37 | 38 | # test IOCP 39 | if [ "${OS}" = "windows-latest" ]; then 40 | cd "${PROJECT_DIR}"/core 41 | "${CARGO}" test --target "${TARGET}" --no-default-features --features iocp,ci 42 | "${CARGO}" test --target "${TARGET}" --no-default-features --features iocp,ci --release 43 | cd "${PROJECT_DIR}"/open-coroutine 44 | "${CARGO}" test --target "${TARGET}" --no-default-features --features iocp,ci 45 | "${CARGO}" test --target "${TARGET}" --no-default-features --features iocp,ci --release 46 | fi 47 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Code Coverage 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**.md' 7 | - '**.png' 8 | pull_request: 9 | paths-ignore: 10 | - '**.md' 11 | - '**.png' 12 | 13 | env: 14 | CARGO_TERM_COLOR: always 15 | CODECOV_TOKEN: 86ae569f-70d3-4c7b-833e-6e8fc97ea9f3 16 | 17 | jobs: 18 | coverage: 19 | name: Run cargo coverage 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout sources 23 | uses: actions/checkout@v4 24 | - name: Install toolchain 25 | uses: actions-rs/toolchain@v1 26 | with: 27 | toolchain: stable 28 | profile: minimal 29 | override: true 30 | components: llvm-tools-preview 31 | - name: Install cargo-llvm-cov 32 | uses: taiki-e/install-action@cargo-llvm-cov 33 | - name: Generate code coverage 34 | run: bash -c "ulimit -Sl 512 && ulimit -Hl 512 && /home/runner/.cargo/bin/cargo llvm-cov --release --all --lcov --output-path lcov.info" 35 | - name: Generate code coverage with all features 36 | run: bash -c "ulimit -Sl 512 && ulimit -Hl 512 && /home/runner/.cargo/bin/cargo llvm-cov --all-features --release --all --lcov --output-path lcov-all-features.info" 37 | - name: Upload coverage to Codecov 38 | run: | 39 | bash <(curl -s https://codecov.io/bash) -f lcov.info -t ${{ env.CODECOV_TOKEN }} 40 | bash <(curl -s https://codecov.io/bash) -f lcov-all-features.info -t ${{ env.CODECOV_TOKEN }} 41 | -------------------------------------------------------------------------------- /.github/workflows/pr-audit.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Security Audit 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**/Cargo.toml' 7 | pull_request: 8 | paths: 9 | - '**/Cargo.toml' 10 | 11 | jobs: 12 | security-audit: 13 | runs-on: ubuntu-latest 14 | if: "!contains(github.event.head_commit.message, 'ci skip')" 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Install cargo-audit 19 | uses: actions-rs/cargo@v1 20 | with: 21 | command: install 22 | args: cargo-audit 23 | 24 | - name: Generate lockfile 25 | uses: actions-rs/cargo@v1 26 | with: 27 | command: generate-lockfile 28 | 29 | - name: Audit dependencies 30 | uses: actions-rs/cargo@v1 31 | with: 32 | command: audit -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | *.lock -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.7.x 2 | 3 | - [x] support cancel coroutine/task 4 | 5 | ### 0.6.x 6 | 7 | - [x] support custom task and coroutine priority. 8 | - [x] support scalable stack 9 | 10 | ### 0.5.x 11 | 12 | - [x] refactor syscall state, distinguish between state and innerState 13 | 14 | ### 0.4.x 15 | 16 | - [x] Supports and is compatible with io_uring in terms of local file IO 17 | - [x] elegant shutdown 18 | - [x] use log instead of println 19 | - [x] enhance `#[open_coroutine::main]` macro 20 | - [x] refactor hook impl, no need to publish dylibs now 21 | - [x] `Monitor` follow the `thread-per-core` guideline 22 | - [x] `EventLoop` follow the `thread-per-core` guideline 23 | 24 | ### 0.3.x 25 | 26 | - [x] ~~support `genawaiter` as low_level stackless coroutine (can't support it due to hook)~~ 27 | - [x] use `corosensei` as low_level coroutine 28 | - [x] support backtrace 29 | - [x] support `#[open_coroutine::co]` macro 30 | - [x] refactor `WorkStealQueue` 31 | 32 | ### 0.2.x 33 | 34 | - [x] use correct `epoll_event` struct 35 | - [x] use `rayon` for parallel computing 36 | - [x] support `#[open_coroutine::main]` macro 37 | - [x] hook almost all `read` syscall 38 |
39 | read syscalls 40 | 41 | - [x] recv 42 | - [x] readv 43 | - [x] pread 44 | - [x] preadv 45 | - [x] recvfrom 46 | - [x] recvmsg 47 | 48 |
49 | 50 | - [x] hook almost all `write` syscall 51 |
52 | write syscalls 53 | 54 | - [x] send 55 | - [x] writev 56 | - [x] sendto 57 | - [x] sendmsg 58 | - [x] pwrite 59 | - [x] pwritev 60 | 61 |
62 | 63 | - [x] hook other syscall 64 |
65 | other syscalls 66 | 67 | - [x] sleep 68 | - [x] usleep 69 | - [x] nanosleep 70 | - [x] connect 71 | - [x] listen 72 | - [x] accept 73 | - [x] shutdown 74 | - [x] poll 75 | - [x] select 76 | 77 |
78 | 79 | ### 0.1.x 80 | 81 | - [x] basic suspend/resume supported 82 | - [x] use jemalloc as memory pool 83 | - [x] higher level coroutine abstraction supported 84 | - [x] preemptive scheduling supported 85 | - [x] work stealing supported 86 | - [x] sleep syscall hooks supported 87 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "core", 5 | "hook", 6 | "macros", 7 | "open-coroutine" 8 | ] 9 | 10 | [workspace.package] 11 | version = "0.7.0" 12 | edition = "2021" 13 | authors = ["zhangzicheng@apache.org"] 14 | repository = "https://github.com/acl-dev/open-coroutine" 15 | license = "Apache-2.0" 16 | readme = "README.md" 17 | exclude = [ 18 | "**/*.DS_Store", 19 | "*.DS_Store" 20 | ] 21 | 22 | [workspace.dependencies] 23 | open-coroutine-core = { path = "core", version = "0.7.0" } 24 | open-coroutine-hook = { path = "hook", version = "0.7.0" } 25 | open-coroutine-macros = { path = "macros", version = "0.7.0" } 26 | 27 | tracing = { version = "0.1", default-features = false } 28 | tracing-subscriber = { version = "0.3", default-features = false } 29 | tracing-appender = { version = "0.2", default-features = false } 30 | cargo_metadata = { version = "0.19", default-features = false } 31 | mio = { version = "1.0", default-features = false } 32 | 33 | cfg-if = "1.0.0" 34 | polling = "2.8.0" 35 | educe = "0.6.0" 36 | 37 | libc = "0.2" 38 | rand = "0.9" 39 | st3 = "0.4" 40 | crossbeam-deque = "0.8" 41 | time = "0.3" 42 | corosensei = "0.2" 43 | core_affinity = "0.8" 44 | crossbeam-utils = "0.8" 45 | crossbeam-skiplist = "0.1" 46 | nix = "0.30" 47 | io-uring = "0.7" 48 | windows-sys = "0.59" 49 | anyhow = "1.0" 50 | slab = "0.4" 51 | backtrace = "0.3" 52 | minhook = "0.7" 53 | psm = "0.1" 54 | 55 | once_cell = "1" 56 | dashmap = "6" 57 | num_cpus = "1" 58 | uuid = "1" 59 | tempfile = "3" 60 | cc = "1" 61 | syn = "2" 62 | quote = "1" 63 | -------------------------------------------------------------------------------- /LICENSE-THIRD-PARTY: -------------------------------------------------------------------------------- 1 | Third party project code used by this project: Mio, Tokio, Tokio-uring. 2 | 3 | =============================================================================== 4 | 5 | Mio 6 | https://github.com/tokio-rs/mio/blob/master/LICENSE 7 | 8 | Copyright (c) 2014 Carl Lerche and other MIO contributors 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | 28 | 29 | =============================================================================== 30 | 31 | Tokio 32 | https://github.com/tokio-rs/tokio/blob/master/LICENSE 33 | 34 | Copyright (c) 2021 Tokio Contributors 35 | 36 | Permission is hereby granted, free of charge, to any 37 | person obtaining a copy of this software and associated 38 | documentation files (the "Software"), to deal in the 39 | Software without restriction, including without 40 | limitation the rights to use, copy, modify, merge, 41 | publish, distribute, sublicense, and/or sell copies of 42 | the Software, and to permit persons to whom the Software 43 | is furnished to do so, subject to the following 44 | conditions: 45 | 46 | The above copyright notice and this permission notice 47 | shall be included in all copies or substantial portions 48 | of the Software. 49 | 50 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 51 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 52 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 53 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 54 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 55 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 56 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 57 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 58 | DEALINGS IN THE SOFTWARE. 59 | 60 | 61 | =============================================================================== 62 | 63 | Tokio-Uring 64 | https://github.com/tokio-rs/tokio-uring/blob/master/LICENSE 65 | 66 | Copyright (c) 2021 Carl Lerche 67 | 68 | Permission is hereby granted, free of charge, to any 69 | person obtaining a copy of this software and associated 70 | documentation files (the "Software"), to deal in the 71 | Software without restriction, including without 72 | limitation the rights to use, copy, modify, merge, 73 | publish, distribute, sublicense, and/or sell copies of 74 | the Software, and to permit persons to whom the Software 75 | is furnished to do so, subject to the following 76 | conditions: 77 | 78 | The above copyright notice and this permission notice 79 | shall be included in all copies or substantial portions 80 | of the Software. 81 | 82 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 83 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 84 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 85 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 86 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 87 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 88 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 89 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 90 | DEALINGS IN THE SOFTWARE. 91 | 92 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "open-coroutine-core" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | description = "The open-coroutine is a simple, efficient and generic coroutine library." 7 | repository.workspace = true 8 | keywords = ["runtime", "coroutine", "hook", "preempt", "work-steal"] 9 | categories = ["concurrency", "asynchronous", "os", "network-programming", "wasm"] 10 | license.workspace = true 11 | readme.workspace = true 12 | exclude.workspace = true 13 | 14 | [dependencies] 15 | tracing = { workspace = true, default-features = false, optional = true } 16 | tracing-subscriber = { workspace = true, features = [ 17 | "fmt", 18 | "local-time" 19 | ], default-features = false, optional = true } 20 | time = { workspace = true, optional = true } 21 | corosensei = { workspace = true, optional = true } 22 | uuid = { workspace = true, features = [ 23 | "v4", 24 | "fast-rng", 25 | ], optional = true } 26 | educe = { workspace = true, optional = true } 27 | core_affinity = { workspace = true, optional = true } 28 | crossbeam-utils = { workspace = true, optional = true } 29 | cfg-if.workspace = true 30 | once_cell.workspace = true 31 | dashmap.workspace = true 32 | num_cpus.workspace = true 33 | rand.workspace = true 34 | st3.workspace = true 35 | crossbeam-deque.workspace = true 36 | crossbeam-skiplist.workspace = true 37 | psm.workspace = true 38 | 39 | [target.'cfg(unix)'.dependencies] 40 | mio = { workspace = true, features = [ 41 | "net", 42 | "os-poll", 43 | "os-ext", 44 | ], default-features = false, optional = true } 45 | nix = { workspace = true, features = ["signal"] } 46 | libc.workspace = true 47 | 48 | [target.'cfg(target_os = "linux")'.dependencies] 49 | io-uring = { workspace = true, optional = true } 50 | 51 | [target.'cfg(windows)'.dependencies] 52 | windows-sys = { workspace = true, features = [ 53 | "Win32_Security", 54 | "Win32_System_IO", 55 | "Win32_Foundation", 56 | "Win32_System_Kernel", 57 | "Win32_System_Threading", 58 | "Win32_Storage_FileSystem", 59 | "Win32_Networking_WinSock", 60 | "Win32_System_SystemInformation", 61 | "Win32_System_Diagnostics_Debug", 62 | "Win32_System_WindowsProgramming", 63 | ] } 64 | polling = { workspace = true, optional = true } 65 | 66 | [build-dependencies] 67 | cfg-if.workspace = true 68 | 69 | [target.'cfg(target_os = "linux")'.build-dependencies] 70 | cc.workspace = true 71 | 72 | [dev-dependencies] 73 | anyhow.workspace = true 74 | slab.workspace = true 75 | backtrace.workspace = true 76 | 77 | [features] 78 | default = ["log", "syscall"] 79 | 80 | # Print some help log. 81 | # Enable for default. 82 | log = ["tracing", "tracing-subscriber", "time"] 83 | 84 | # This feature only used in open-coroutine inner, don't use it in your project. 85 | ci = [] 86 | 87 | # low-level raw coroutine 88 | korosensei = ["corosensei", "uuid", "nix/pthread", "educe"] 89 | 90 | # Provide preemptive scheduling implementation. 91 | # Enable for default. 92 | preemptive = ["korosensei"] 93 | 94 | # Provide net API abstraction and implementation. 95 | net = ["korosensei", "polling", "mio", "crossbeam-utils", "core_affinity"] 96 | 97 | # Provide io_uring adaptation, this feature only works in linux. 98 | io_uring = ["net", "io-uring"] 99 | 100 | # Provide IOCP adaptation, this feature only works in windows. 101 | iocp = ["net"] 102 | 103 | # Provide completion IO adaptation 104 | completion_io = ["io_uring", "iocp"] 105 | 106 | # Provide syscall implementation. 107 | syscall = ["net"] 108 | -------------------------------------------------------------------------------- /core/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | cfg_if::cfg_if! { 3 | if #[cfg(target_os = "linux")] { 4 | cc::Build::new() 5 | .warnings(true) 6 | .file("c_src/version.c") 7 | .compile("version"); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /core/c_src/version.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int linux_version_code() { 4 | return LINUX_VERSION_CODE; 5 | } 6 | -------------------------------------------------------------------------------- /core/docs/cn/coroutine-pool.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Coroutine Pool Overview 3 | date: 2025-01-18 10:00:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # 协程池总览 8 | 9 | [English](../en/coroutine-pool.md) | 中文 10 | 11 | ## 使用方法 12 | 13 | ```rust 14 | use open_coroutine_core::co_pool::CoroutinePool; 15 | 16 | fn main() -> std::io::Result<()> { 17 | let mut pool = CoroutinePool::default(); 18 | assert!(pool.is_empty()); 19 | pool.submit_task( 20 | None, 21 | |_| { 22 | println!("Hello, world!"); 23 | Some(2) 24 | }, 25 | None, 26 | None, 27 | )?; 28 | assert!(!pool.is_empty()); 29 | pool.try_schedule_task() 30 | } 31 | ``` 32 | 33 | ## 为什么需要协程池? 34 | 35 | 使用协程池可以带来以下几个显著的优势: 36 | 37 | 1. 资源管理:协程池可以管理协程的创建、销毁和复用。通过使用协程池,可以预先创建一定数量的协程并存储在池中,以便在需要时使用。这样可以避免频繁创建和销毁协程,减少不必要的资源浪费,并提高系统性能。 38 | 39 | 2. 避免协程饥饿:在使用协程池时,协程会持续获得任务,避免了协程在完成任务后空闲的情况。 40 | 41 | 3. 并发控制:通过设置协程池的参数,可以限制并发协程的数量,避免因协程过多而导致系统过载。 42 | 43 | 4. 提高代码可维护性:使用协程池可以将任务执行与协程管理分离,使代码更加清晰和易于维护。任务的执行逻辑可以专注于任务本身,而协程的创建和管理则由协程池处理。 44 | 45 | ## 工作原理 46 | 47 | 在open-coroutine-core中,协程池是惰性的,这意味着如果你不调用`CoroutinePool::try_timeout_schedule_task`,任务将不会被执行。详情请参考以下时序图: 48 | 49 | ```mermaid 50 | sequenceDiagram 51 | actor Schedule Thread 52 | participant CoroutinePool 53 | participant WorkerCoroutine 54 | participant Task 55 | participant CoroutineCreator 56 | Schedule Thread ->>+ CoroutinePool: CoroutinePool::try_timeout_schedule_task 57 | alt 协程池已停止 58 | CoroutinePool ->>+ Schedule Thread: 返回错误 59 | end 60 | alt 协程池中的任务队列为空 61 | CoroutinePool ->>+ Schedule Thread: 返回成功 62 | end 63 | alt 创建工作协程 64 | CoroutinePool ->>+ WorkerCoroutine: 仅在协程池未达到最大池大小时创建工作协程 65 | end 66 | CoroutinePool ->>+ WorkerCoroutine: 调度工作协程 67 | alt 运行任务 68 | WorkerCoroutine ->>+ Task: 尝拉取任务 69 | alt 拉取成功 70 | Task ->>+ Task: 运行任务 71 | alt 执行中 72 | Task ->>+ WorkerCoroutine: 被抢占或进入系统调用 73 | WorkerCoroutine ->>+ WorkerCoroutine: 协程状态变为Suspend/Syscall 74 | WorkerCoroutine ->>+ CoroutineCreator: Listener::on_state_changed 75 | CoroutineCreator ->>+ WorkerCoroutine: 仅在协程池未达到最大池大小时创建工作协程 76 | end 77 | alt 运行成功 78 | Task ->>+ WorkerCoroutine: 任务正常退出 79 | end 80 | alt 运行失败 81 | Task ->>+ WorkerCoroutine: 任务异常退出 82 | WorkerCoroutine ->>+ WorkerCoroutine: 协程状态变为Error 83 | WorkerCoroutine ->>+ CoroutineCreator: Listener::on_state_changed 84 | CoroutineCreator ->>+ CoroutineCreator: 减少当前协程计数 85 | CoroutineCreator ->>+ WorkerCoroutine: 仅在协程池未达到最大池大小时重新创建工作协程 86 | end 87 | end 88 | alt 拉取失败 89 | Task ->>+ WorkerCoroutine: 增加拉取失败计数并让出给下一个协程 90 | WorkerCoroutine ->>+ WorkerCoroutine: 如果拉取失败计数已达到协程池的当前大小,则阻塞当前线程一段时间 91 | end 92 | WorkerCoroutine ->>+ WorkerCoroutine: 尝试拉取下一个任务 93 | end 94 | alt 回收协程 95 | WorkerCoroutine ->>+ WorkerCoroutine: 本次调度达到超时时刻 96 | WorkerCoroutine ->>+ CoroutinePool: 协程池是否超过最小池大小? 97 | CoroutinePool ->>+ WorkerCoroutine: 是 98 | WorkerCoroutine ->>+ WorkerCoroutine: 退出 99 | end 100 | WorkerCoroutine ->>+ CoroutinePool: 如果超时或调度失败则返回 101 | CoroutinePool ->>+ Schedule Thread: 本次调度结束 102 | Schedule Thread ->>+ Schedule Thread: ...... 103 | ``` 104 | -------------------------------------------------------------------------------- /core/docs/cn/coroutine.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 协程总览 3 | date: 2024-12-29 16:00:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # 协程总览 8 | 9 | [English](../en/coroutine.md) | 中文 10 | 11 | ## 使用方法 12 | 13 | ```rust 14 | use open_coroutine_core::common::constants::CoroutineState; 15 | use open_coroutine_core::coroutine::Coroutine; 16 | 17 | fn main() -> std::io::Result<()> { 18 | let mut co = Coroutine::new( 19 | // 可选的协程名称 20 | None, 21 | |suspender, input| { 22 | assert_eq!(1, input); 23 | assert_eq!(3, suspender.suspend_with(2)); 24 | 4 25 | }, 26 | // 可选的栈大小 27 | None, 28 | // 可选的协程优先级 29 | None, 30 | )?; 31 | // 宏`co!`等同于上面的代码 32 | // let mut co = open_coroutine_core::co!(|suspender, input| { 33 | // assert_eq!(1, input); 34 | // assert_eq!(3, suspender.suspend_with(2)); 35 | // 4 36 | // })?; 37 | assert_eq!(CoroutineState::Suspend(2, 0), co.resume_with(1)?); 38 | assert_eq!(CoroutineState::Complete(4), co.resume_with(3)?); 39 | Ok(()) 40 | } 41 | ``` 42 | 43 | ## 什么是协程? 44 | 45 | [协程](https://en.wikipedia.org/wiki/Coroutine)是一种可以暂停和恢复的函数,能够向调用者返回值。协程可以在其调用栈的任何位置挂起自己。除了从协程中接收返回值外,你还可以在每次恢复协程时向其传递数据。 46 | 47 | 以上内容摘自[corosensei](https://github.com/Amanieu/corosensei)。 48 | 49 | ## 协程 VS 线程 50 | 51 | | | 协程 | 线程 | 52 | |---------|----------------|----------| 53 | | 切换效率 | ✅ 更高 | ❌ 高 | 54 | | 内存用量 | ✅ Bytes/KB/MB | ❌ KB/MB | 55 | | 由OS调度 | ❌ | ✅ | 56 | | 可伸缩栈 | ✅ | ❌ | 57 | 58 | ## 有栈协程 VS 无栈协程 59 | 60 | | | 有栈协程 | 无栈协程 | 61 | |---------|----------|----------| 62 | | 切换效率 | ❌ 高 | ✅ 更高 | 63 | | 内存用量 | ❌ KB/MB | ✅ Bytes | 64 | | 使用限制 | ✅ 较少 | ❌ 较多 | 65 | 66 | 一般来说,如果对资源利用率和切换性能的要求不是非常严格,使用有栈协程会更加方便,代码也更容易维护。因此,`open-coroutine`选择了有栈协程。 67 | 68 | ## open-coroutine中的状态 69 | 70 | ```text 71 | Ready 72 | ↗ ↓ 73 | Suspend ← Running ⇄ Syscall 74 | ↙ ↘ 75 | Complete Error 76 | ``` 77 | 78 | 在open-coroutine中,创建的协程处于`Ready`状态,一旦调用`Coroutine::resume_with`方法,状态将从`Ready`变为`Running`。之后,协程可能会通过`Suspender::suspend_with`挂起,状态将从`Running`变为`Suspend`,`Suspend`状态还会记录可以被唤醒的时间戳,单位为纳秒。 79 | 80 | 当协程进入系统调用时,协程状态将从`Running`变为`Syscall`,系统调用完成后,状态将从`Syscall`变回`Running`(注意:如果你使用[open-coroutine-core](https://crates.io/crates/open-coroutine-core),你需要在适当的时候手动调用`Coroutine::syscall`和`Coroutine::running`来切换协程状态,这会增加大量工作量且容易出错,因此请使用[open-coroutine](https://crates.io/crates/open-coroutine)并启用`hook`)。此外,系统调用`Syscall`状态会记录系统调用的名称和一个用于open-coroutine内部的状态。 81 | 82 | 当协程成功完成时,状态将从`Running`变为`Complete`。如果在协程执行过程中发生panic且用户未处理该panic,状态将从`Running`变为`Error`同时记录panic信息。 83 | 84 | ## `Listener` 设计 85 | 86 | To enhance extension, we provide the `Listener` API, which notifies `Listener` whenever Coroutine state changes. 87 | 88 | 为了增强扩展性,我们提供了`Listener`API,每当协程状态发生变化时,都会通知`Listener`。 89 | 90 | ## `CoroutineLocal` 设计 91 | 92 | `ThreadLocal`的设计意图是解决多线程环境中的线程安全问题。在多线程程序中,多个线程可能同时访问和修改同一个共享变量,这可能导致线程安全问题,如数据不一致和竞态条件。为了解决这些问题,传统方法是通过锁来同步对共享资源的访问,但这可能导致性能下降,尤其是在高并发场景中。`ThreadLocal`提供了一种新的解决方案,它为每个线程提供变量的独立副本,从而避免了多个线程之间的共享变量冲突,确保了线程安全,并提高了程序的并发性能。 93 | 94 | 在协程环境中,尽管调度线程是单线程的,但由于工作窃取机制的存在,协程可能会在多个线程之间流动,这使得`ThreadLocal`失效。为了解决这个问题,我们不得不引入`CoroutineLocal`。它与`ThreadLocal`的副本提供方式类似,但`CoroutineLocal`将副本升级到了协程级别,这意味着每个协程都有自己的局部变量。这些局部变量将在协程被销毁时一起被丢弃。 95 | 96 | ## [可伸缩栈总览](scalable-stack.md) 97 | -------------------------------------------------------------------------------- /core/docs/cn/monitor.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Monitor总览 3 | date: 2025-01-02 08:35:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # Monitor总览 8 | 9 | [English](../en/monitor.md) | 中文 10 | 11 | ## 支持范围 12 | 13 | `preemptive`特性目前支持以下targets: 14 | 15 | | | ELF (Linux, BSD, bare metal, etc) | Darwin (macOS, iOS, etc) | Windows | 16 | |---------------|-----------------------------------|--------------------------|---------| 17 | | `x86_64` | ✅ | ✅ | ❌ | 18 | | `x86` | ✅ | ❌ | ❌ | 19 | | `AArch64` | ✅ | ✅ | ❌ | 20 | | `ARM` | ✅ | ❌ | ❌ | 21 | | `RISC-V` | ✅ | ❌ | ❌ | 22 | | `LoongArch64` | ✅ | ❌ | ❌ | 23 | 24 | ✅ 已测试且稳定;⚠️ 已测试但不稳定;❌ 不支持。 25 | 26 | ⚠️ 如果你想直接在`open-coroutine-core`中使用`preemptive`特性,你必须学习[Hook总览](../../../hook/docs/cn/hook.md)。 27 | 28 | ## 使用方法 29 | 30 | ```rust 31 | use open_coroutine_core::co; 32 | use open_coroutine_core::common::constants::CoroutineState; 33 | use open_coroutine_core::coroutine::Coroutine; 34 | 35 | fn main() -> std::io::Result<()> { 36 | // 模拟最极端的死循环,如果未启用preemptive特性,协程恢复后将一直卡在死循环中 37 | let mut coroutine: Coroutine<(), (), ()> = co!(|_, ()| { loop {} })?; 38 | assert_eq!(CoroutineState::Suspend((), 0), coroutine.resume()?); 39 | // 如果未启用抢占特性,将永远不会到达此处 40 | assert_eq!(CoroutineState::Suspend((), 0), coroutine.state()); 41 | Ok(()) 42 | } 43 | ``` 44 | 45 | ## 为什么需要抢占? 46 | 47 | 在调用`Coroutine::resume_with`后,协程可能会长时间占用调度线程,从而拖慢由该调度线程调度的其他协程。协程在两种情况下会长时间占用调度线程:陷入重度计算或系统调用。为了解决陷入重度计算的问题,我们引入抢占式调度,它会自动挂起长时间执行的协程,并允许其他协程执行。 48 | 49 | ## 什么是monitor? 50 | 51 | `monitor`是open-coroutine-core的一个模块,它实现了`preemptive`特性,这允许协程在长时间运行后被抢占。 52 | 53 | ## 工作原理 54 | 55 | ```mermaid 56 | sequenceDiagram 57 | actor 用户线程 58 | participant 协程 59 | participant MonitorListener 60 | participant Monitor线程 61 | 用户线程 ->>+ 协程: Coroutine::resume_with 62 | 协程 ->>+ MonitorListener: Listener::on_state_changed 63 | MonitorListener ->>+ Monitor线程: Monitor::submit 64 | Monitor线程 ->>+ Monitor线程: libc::sigaction 65 | alt 发生抢占 66 | 协程 ->> 协程: 协程状态变为Running超过10ms 67 | Monitor线程 ->>+ 用户线程: libc::pthread_kill 68 | 用户线程 ->>+ 用户线程: libc::pthread_sigmask 69 | 用户线程 ->>+ 协程: 挂起协程,具体实现可查看sigurg_handler 70 | 协程 ->> 用户线程: 协程已被抢占 71 | else 未发生抢占 72 | 协程 ->> 协程: 协程状态变为Suspend/Syscall/Complete/Error 73 | 协程 ->>+ MonitorListener: Listener::on_state_changed 74 | MonitorListener ->>+ Monitor线程: Monitor::remove 75 | Monitor线程 ->>+ MonitorListener: 返回 76 | MonitorListener ->>+ 协程: 返回 77 | 协程 ->> 用户线程: 返回 78 | end 79 | ``` 80 | -------------------------------------------------------------------------------- /core/docs/cn/ordered-work-steal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 有序工作窃取总览 3 | date: 2025-01-17 08:34:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # 有序工作窃取总览 8 | 9 | [English](../en/ordered-work-steal.md) | 中文 10 | 11 | ## 为什么需要有序工作窃取? 12 | 13 | 在现实世界中,总是有一些线程先完成自己的任务,而其他线程还有任务需要处理。于是,就会出现一核有难、多核围观的壮观场景。 14 | 15 |
16 | 17 |
18 | 19 | 显然,我们不希望这种情况发生。对于空闲的线程,与其让它们围观其他线程工作,不如让它们帮助其他线程工作。此外,我们希望根据优先级执行任务,优先级越高,任务越早被执行。 20 | 21 | ## 什么是有序工作窃取队列? 22 | 23 | 有序工作窃取队列由一个全局队列和多个本地队列组成,全局队列是无界的,而本地队列是一个有界的`SkipList`集合。为了确保高性能,本地队列的数量通常等于线程的数量。值得一提的是,如果所有线程都优先处理本地任务,可能会出现一种极端情况,即共享队列上的任务永远没有机会被调度。为了避免这种不平衡,参考goroutine的设计,每次线程从本地队列调度了60个任务后,强制从共享队列中弹出`优先级最高的任务`。 24 | 25 | ## `push`的工作原理 26 | 27 | ```mermaid 28 | flowchart TD 29 | Cond{本地队列是否已满?} 30 | PS[将任务推入本地队列] 31 | PTG[将本地队列中一半的低优先级任务推入全局队列] 32 | PG[将任务推入全局队列] 33 | push --> Cond 34 | Cond -- 否 --> PS 35 | Cond -- 是 --> PTG --- PG 36 | ``` 37 | 38 | ## `pop`的工作原理 39 | 40 | ```mermaid 41 | flowchart TD 42 | Cond1{本地队列中是否有任务?} 43 | Cond2{其他本地队列中是否有任务?} 44 | Cond3{全局队列中是否有任务?} 45 | PS[弹出优先级最高的任务] 46 | ST[从其他本地队列窃取高优先级任务] 47 | PF[未找到任务] 48 | pop --> Cond1 49 | Cond1 -- 是 --> PS 50 | Cond1 -- 否 --> Cond2 51 | Cond2 -- 是 --> ST --> PS 52 | Cond2 -- 否 --> Cond3 53 | Cond3 -- 是 --> PS 54 | Cond3 -- 否 --> PF 55 | ``` 56 | -------------------------------------------------------------------------------- /core/docs/cn/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: open-coroutine总览 3 | date: 2025-01-10 08:24:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # open-coroutine总览 8 | 9 | [English](../en/overview.md) | 中文 10 | 11 | [![crates.io](https://img.shields.io/crates/v/open-coroutine.svg)](https://crates.io/crates/open-coroutine) 12 | [![docs.rs](https://img.shields.io/badge/docs-release-blue)](https://docs.rs/open-coroutine) 13 | [![LICENSE](https://img.shields.io/github/license/acl-dev/open-coroutine.svg?style=flat-square)](https://github.com/acl-dev/open-coroutine/blob/master/LICENSE-APACHE) 14 | [![Build Status](https://github.com/acl-dev/open-coroutine/workflows/CI/badge.svg)](https://github.com/acl-dev/open-coroutine/actions) 15 | [![Codecov](https://codecov.io/github/acl-dev/open-coroutine/graph/badge.svg?token=MSM3R7CBEX)](https://codecov.io/github/acl-dev/open-coroutine) 16 | [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/acl-dev/open-coroutine.svg)](http://isitmaintained.com/project/acl-dev/open-coroutine "Average time to resolve an issue") 17 | [![Percentage of issues still open](http://isitmaintained.com/badge/open/acl-dev/open-coroutine.svg)](http://isitmaintained.com/project/acl-dev/open-coroutine "Percentage of issues still open") 18 | 19 | `open-coroutine`是一个简单、高效、通用的有栈协程库,您可以将其用作IO线程池的性能替代,查看[为什么更好](core/docs/en/why-better.md). 20 | 21 | - [诞生之因](../../../docs/cn/background.md) 22 | - [语言选择](../../../docs/cn/why-rust.md) 23 | - [为什么更好](../cn/why-better.md) 24 | - [快速开始](../../../README_ZH.md) 25 | - [协程总览](../cn/coroutine.md) 26 | - [可伸缩栈总览](../cn/scalable-stack.md) 27 | - [Monitor总览](../cn/monitor.md) 28 | - [工作窃取总览](../cn/work-steal.md) 29 | - [有序工作窃取总览](../cn/ordered-work-steal.md) 30 | - [协程池总览](../cn/coroutine-pool.md) 31 | - [Hook总览](../../../hook/docs/cn/hook.md) 32 | -------------------------------------------------------------------------------- /core/docs/cn/scalable-stack.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 可伸缩栈总览 3 | date: 2025-01-08 08:44:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # 可伸缩栈总览 8 | 9 | [English](../en/scalable-stack.md) | 中文 10 | 11 | ## 使用方法 12 | 13 | ```rust 14 | use open_coroutine_core::co; 15 | use open_coroutine_core::common::constants::CoroutineState; 16 | use open_coroutine_core::coroutine::suspender::Suspender; 17 | use open_coroutine_core::coroutine::Coroutine; 18 | 19 | fn main() -> std::io::Result<()> { 20 | let mut co = co!(|_: &Suspender<(), i32>, ()| { 21 | fn recurse(i: u32, p: &mut [u8; 10240]) { 22 | // 你也可以在线程中使用`maybe_grow` 23 | Coroutine::<(), i32, ()>::maybe_grow(|| { 24 | // 确保栈分配不会被优化掉 25 | unsafe { std::ptr::read_volatile(&p) }; 26 | if i > 0 { 27 | recurse(i - 1, &mut [0; 10240]); 28 | } 29 | }) 30 | .expect("allocate stack failed") 31 | } 32 | // 使用约500KB的栈 33 | recurse(50, &mut [0; 10240]); 34 | })?; 35 | assert_eq!(co.resume()?, CoroutineState::Complete(())); 36 | Ok(()) 37 | } 38 | ``` 39 | 40 | ## 为什么需要可伸缩栈? 41 | 42 | 协程的默认栈大小为128KB,这对于大多数场景来说已经足够,但仍有一些场景不适用,例如递归算法。可扩展栈允许在程序中标注可能需要更大栈的扩容点。如果栈达到其限制,则会溢出到堆中。 43 | 44 | ## 工作原理 45 | 46 | ```mermaid 47 | flowchart TD 48 | Cond1{在协程中?} 49 | Cond2{达到限制?} 50 | Cond3{第一次扩容?} 51 | C[创建新栈] 52 | RC[在当前栈上运行] 53 | RN1[在新栈上运行] 54 | maybe_grow --> Cond1 55 | Cond1 -- Yes --> Cond2 56 | Cond2 -- Yes --> C 57 | Cond2 -- No --> RC 58 | C --> RN1 59 | Cond1 -- No --> Cond3 60 | Cond3 -- Yes --> C 61 | Cond3 -- No --> Cond2 62 | ``` 63 | -------------------------------------------------------------------------------- /core/docs/cn/why-better.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 为什么更好 3 | date: 2025-01-10 08:28:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # 为什么更好 8 | 9 | [English](../en/why-better.md) | 中文 10 | 11 | ## 系统调用不会阻塞 12 | 13 | 首先,我们来看一下线程是如何与系统调用协作的。 14 | 15 | ```mermaid 16 | sequenceDiagram 17 | actor 用户线程 18 | participant 操作系统 19 | 用户线程 ->>+ 用户线程: 执行 20 | alt 用户线程被阻塞 21 | 用户线程 ->>+ 操作系统: 慢系统调用 22 | 操作系统 ->> 用户线程: 返回 23 | end 24 | 用户线程 ->>+ 用户线程: 执行 25 | ``` 26 | 27 | 如果系统调用是一个慢系统调用,例如默认阻塞的`accept`,线程将被长时间阻塞,直到操作系统返回为止,期间无法做任何事情。现在,我们来看一下 open-coroutine 是如何与系统调用协作的。 28 | 29 | ```mermaid 30 | sequenceDiagram 31 | actor EventLoop线程 32 | participant 协程1 33 | participant 协程2 34 | participant 被代理的系统调用 35 | participant 操作系统 36 | EventLoop线程 ->>+ 协程1: 调度 37 | alt 协程1逻辑上被阻塞 38 | 协程1 ->>+ 被代理的系统调用: 慢系统调用 39 | 被代理的系统调用 ->>+ 操作系统: 快系统调用 40 | 操作系统 ->> 被代理的系统调用: 返回错误码 41 | 被代理的系统调用 ->> 协程1: 挂起协程一段时间 42 | end 43 | 协程1 ->>+ EventLoop线程: 挂起 44 | EventLoop线程 ->>+ 协程2: 调度 45 | alt 协程2逻辑上被阻塞 46 | 协程2 ->>+ 被代理的系统调用: 慢系统调用 47 | 被代理的系统调用 ->>+ 操作系统: 快系统调用 48 | 操作系统 ->> 被代理的系统调用: 返回 49 | 被代理的系统调用 ->> 协程2: 返回 50 | end 51 | 协程2 ->>+ EventLoop线程: 返回 52 | EventLoop线程 ->>+ 协程1: 调度 53 | alt 协程1逻辑上被阻塞 54 | 协程1 ->>+ 被代理的系统调用: 从上次暂停处恢复 55 | 被代理的系统调用 ->>+ 操作系统: 快系统调用 56 | 操作系统 ->> 被代理的系统调用: 返回 57 | 被代理的系统调用 ->> 协程1: 返回 58 | end 59 | 协程1 ->>+ EventLoop线程: 返回 60 | EventLoop线程 ->>+ EventLoop线程: 调度其他协程 61 | ``` 62 | 63 | 如你所见,`被代理的系统调用`(hook)将`慢系统调用`转换为`快系统调用`。通过这种方式,尽管`EventLoop线程`在执行系统调用时仍然会被阻塞,但阻塞时间非常短。因此,与线程模型相比,`EventLoop线程`可以在相同的时间内做更多的事情。 64 | 65 | ## 重度计算不会阻塞 66 | 67 | 其次,我们来看一下线程如何处理重度计算。 68 | 69 | ```mermaid 70 | sequenceDiagram 71 | actor 用户线程 72 | alt 用户线程陷入循环 73 | 用户线程 ->>+ 用户线程: 执行循环 74 | end 75 | ``` 76 | 77 | 就像上面的系统调用一样,线程会一直阻塞在循环中。接下来,我们来看一下open-coroutine如何处理重度计算。 78 | 79 | ```mermaid 80 | sequenceDiagram 81 | actor EventLoop线程 82 | participant 协程1 83 | participant 协程2 84 | participant Monitor 85 | EventLoop线程 ->>+ 协程1: 调度 86 | alt 协程1进入循环 87 | 协程1 ->>+ 协程1: 执行循环一段时间 88 | Monitor ->> 协程1: 挂起协程 89 | end 90 | 协程1 ->>+ EventLoop线程: 挂起 91 | EventLoop线程 ->>+ 协程2: 调度 92 | alt 协程2进入循环 93 | 协程2 ->>+ 协程2: 执行循环一段时间 94 | Monitor ->> 协程1: 挂起协程 95 | end 96 | 协程2 ->>+ EventLoop线程: 挂起 97 | EventLoop线程 ->>+ 协程1: 调度 98 | alt 协程1进入循环 99 | 协程1 ->>+ 协程1: 从上次暂停处恢复 100 | end 101 | 协程1 ->>+ EventLoop线程: 返回 102 | EventLoop线程 ->>+ EventLoop线程: 调度其他协程 103 | ``` 104 | 105 | `Monitor`会监控协程的执行情况,一旦发现某个协程的执行时间过长,就会强制挂起该协程。因此,现在我们甚至可以[使用一个`EventLoop线程`来执行多个循环](https://github.com/loongs-zhang/open-coroutine/blob/master/open-coroutine/examples/preemptive.rs),这是单线程模型无法实现的。 106 | -------------------------------------------------------------------------------- /core/docs/cn/work-steal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 工作窃取总览 3 | date: 2025-01-17 08:34:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # 工作窃取总览 8 | 9 | [English](../en/work-steal.md) | 中文 10 | 11 | ## 为什么需要工作窃取? 12 | 13 | 在现实世界中,总是有一些线程先完成自己的任务,而其他线程还有任务需要处理。于是,就会出现一核有难、多核围观的壮观场景。 14 | 15 |
16 | 17 |
18 | 19 | 显然,我们不希望这种情况发生。对于空闲的线程,与其让它们围观其他线程工作,不如让它们帮助其他线程工作。 20 | 21 | ## 什么是工作窃取队列? 22 | 23 | 工作窃取队列由一个全局队列和多个本地队列组成,全局队列是无界的,而本地队列是一个有界的环形缓冲区(RingBuffer)。为了确保高性能,本地队列的数量通常等于线程的数量。值得一提的是,如果所有线程都优先处理本地任务,可能会出现一种极端情况,即共享队列上的任务永远没有机会被调度。为了避免这种不平衡,参考goroutine的设计,每次线程从本地队列调度了60个任务后,强制从共享队列中弹出一个任务。 24 | 25 | ## `push`的工作原理 26 | 27 | ```mermaid 28 | flowchart TD 29 | Cond{本地队列是否已满?} 30 | PS[将任务推入本地队列] 31 | PTG[将本地队列中一半的任务推入全局队列] 32 | PG[将任务推入全局队列] 33 | push --> Cond 34 | Cond -- 否 --> PS 35 | Cond -- 是 --> PTG --- PG 36 | ``` 37 | 38 | ## `pop`的工作原理 39 | 40 | ```mermaid 41 | flowchart TD 42 | Cond1{本地队列中是否有任务?} 43 | Cond2{其他本地队列中是否有任务?} 44 | Cond3{全局队列中是否有任务?} 45 | PS[弹出一个任务] 46 | ST[从其他本地队列窃取任务] 47 | PF[未找到任务] 48 | pop --> Cond1 49 | Cond1 -- 是 --> PS 50 | Cond1 -- 否 --> Cond2 51 | Cond2 -- 是 --> ST --> PS 52 | Cond2 -- 否 --> Cond3 53 | Cond3 -- 是 --> PS 54 | Cond3 -- 否 --> PF 55 | ``` 56 | -------------------------------------------------------------------------------- /core/docs/en/monitor.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Monitor Overview 3 | date: 2025-01-02 08:35:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # Monitor Overview 8 | 9 | English | [中文](../cn/monitor.md) 10 | 11 | ## Supported Targets 12 | 13 | The `preemptive` feature currently supports the following targets: 14 | 15 | | | ELF (Linux, BSD, bare metal, etc) | Darwin (macOS, iOS, etc) | Windows | 16 | |---------------|-----------------------------------|--------------------------|---------| 17 | | `x86_64` | ✅ | ✅ | ❌ | 18 | | `x86` | ✅ | ❌ | ❌ | 19 | | `AArch64` | ✅ | ✅ | ❌ | 20 | | `ARM` | ✅ | ❌ | ❌ | 21 | | `RISC-V` | ✅ | ❌ | ❌ | 22 | | `LoongArch64` | ✅ | ❌ | ❌ | 23 | 24 | ✅ Tested and stable; ⚠️ Tested but unstable; ❌ Not supported. 25 | 26 | ⚠️ If you want to use `preemptive` feature directly in `open-coroutine-core`, you must learn 27 | [Hook Overview](../../../hook/docs/en/hook.md). 28 | 29 | ## Usage 30 | 31 | ```rust 32 | use open_coroutine_core::co; 33 | use open_coroutine_core::common::constants::CoroutineState; 34 | use open_coroutine_core::coroutine::Coroutine; 35 | 36 | fn main() -> std::io::Result<()> { 37 | // Simulate the most extreme dead loop, if the preemptive feature is not enabled, 38 | // coroutine will remain stuck in a dead loop after resume. 39 | let mut coroutine: Coroutine<(), (), ()> = co!(|_, ()| { loop {} })?; 40 | assert_eq!(CoroutineState::Suspend((), 0), coroutine.resume()?); 41 | // will never reach if the preemptive feature is not enabled 42 | assert_eq!(CoroutineState::Suspend((), 0), coroutine.state()); 43 | Ok(()) 44 | } 45 | ``` 46 | 47 | ## Why preempt? 48 | 49 | After a `Coroutine::resume_with`, a coroutine may occupy the scheduling thread for a long time, thereby slowing down 50 | other coroutines scheduled by that scheduling thread. The coroutine occupies scheduling threads for a long time in two 51 | scenarios: getting stuck in heavy computing or syscall. To solve the problem of getting stuck in heavy computing, we 52 | introduce preemptive scheduling, which automatically suspends coroutines that are stuck in long-term execution and 53 | allows other coroutines to execute. 54 | 55 | ## What is monitor? 56 | 57 | The `monitor` is a module of open-routine-core that implements the `preemptive` feature, which allows the coroutine to 58 | be preempted when it has been running for a long time. 59 | 60 | ## How it works 61 | 62 | ```mermaid 63 | sequenceDiagram 64 | actor User Thread 65 | participant Coroutine 66 | participant MonitorListener 67 | participant Monitor Thread 68 | User Thread ->>+ Coroutine: Coroutine::resume_with 69 | Coroutine ->>+ MonitorListener: Listener::on_state_changed 70 | MonitorListener ->>+ Monitor Thread: Monitor::submit 71 | Monitor Thread ->>+ Monitor Thread: libc::sigaction 72 | alt Preempting has occurred 73 | Coroutine ->> Coroutine: Resumed and the coroutine state is Running for more than 10ms 74 | Monitor Thread ->>+ User Thread: libc::pthread_kill 75 | User Thread ->>+ User Thread: libc::pthread_sigmask 76 | User Thread ->>+ Coroutine: suspend the coroutine, see sigurg_handler 77 | Coroutine ->> User Thread: coroutine has been preempted 78 | else No preempting 79 | Coroutine ->> Coroutine: The coroutine state changes to Suspend/Syscall/Complete/Error 80 | Coroutine ->>+ MonitorListener: Listener::on_state_changed 81 | MonitorListener ->>+ Monitor Thread: Monitor::remove 82 | Monitor Thread ->>+ MonitorListener: return 83 | MonitorListener ->>+ Coroutine: return 84 | Coroutine ->> User Thread: return 85 | end 86 | ``` 87 | -------------------------------------------------------------------------------- /core/docs/en/ordered-work-steal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ordered Work Steal Overview 3 | date: 2025-01-17 08:34:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # Ordered Work Steal Overview 8 | 9 | English | [中文](../cn/ordered-work-steal.md) 10 | 11 | ## Why ordered work steal? 12 | 13 | In the real world, there are always threads that complete their own tasks first, while other threads have tasks to be 14 | processed. Then, a spectacular scene emerged where one core was in difficulty and other cores were watching. 15 | 16 |
17 | 18 |
19 | 20 | Obviously, we don't want this to happen. For idle threads, instead of letting them watch other threads working, it's 21 | better to let them help other threads work. In addition, we hope to execute tasks based on priority, the higher the 22 | priority, the earlier it will be executed. 23 | 24 | ## What is ordered work steal queue? 25 | 26 | An ordered work steal queue consists of a global queue and multiple local queues, the global queue is unbounded, while 27 | the local queue has a bounded `SkipList` with collections. To ensure high performance, the number of local queues is 28 | usually equal to the number of threads. I's worth mentioning that if all threads prioritize local tasks, there will be 29 | an extreme situation where tasks on the shared queue will never have a chance to be scheduled. To avoid this imbalance, 30 | refer to goroutine, every time a thread has scheduled 60 tasks from the local queue, it will be forced to pop the 31 | `highest priority task` from the shared queue. 32 | 33 | ## How `push` works 34 | 35 | ```mermaid 36 | flowchart TD 37 | Cond{Is the local queue full?} 38 | PS[Push to the local queue] 39 | PTG[Push half of the low priority tasks from the local queue to the global queue] 40 | PG[Push to the global queue] 41 | push --> Cond 42 | Cond -- No --> PS 43 | Cond -- Yes --> PTG --- PG 44 | ``` 45 | 46 | ## How `pop` works 47 | 48 | ```mermaid 49 | flowchart TD 50 | Cond1{Are there any tasks in the local queue?} 51 | Cond2{Are there any tasks in other local queues?} 52 | Cond3{Are there any tasks in the global queue?} 53 | PS[Pop the task with the highest priority] 54 | ST[Steal tasks with high priority from other local queues] 55 | PF[Task not found] 56 | pop --> Cond1 57 | Cond1 -- Yes --> PS 58 | Cond1 -- No --> Cond2 59 | Cond2 -- Yes --> ST --> PS 60 | Cond2 -- No --> Cond3 61 | Cond3 -- Yes --> PS 62 | Cond3 -- No --> PF 63 | ``` 64 | -------------------------------------------------------------------------------- /core/docs/en/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: open-coroutine Overview 3 | date: 2025-01-10 08:24:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # open-coroutine overview 8 | 9 | English | [中文](../cn/overview.md) 10 | 11 | [![crates.io](https://img.shields.io/crates/v/open-coroutine.svg)](https://crates.io/crates/open-coroutine) 12 | [![docs.rs](https://img.shields.io/badge/docs-release-blue)](https://docs.rs/open-coroutine) 13 | [![LICENSE](https://img.shields.io/github/license/acl-dev/open-coroutine.svg?style=flat-square)](https://github.com/acl-dev/open-coroutine/blob/master/LICENSE-APACHE) 14 | [![Build Status](https://github.com/acl-dev/open-coroutine/workflows/CI/badge.svg)](https://github.com/acl-dev/open-coroutine/actions) 15 | [![Codecov](https://codecov.io/github/acl-dev/open-coroutine/graph/badge.svg?token=MSM3R7CBEX)](https://codecov.io/github/acl-dev/open-coroutine) 16 | [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/acl-dev/open-coroutine.svg)](http://isitmaintained.com/project/acl-dev/open-coroutine "Average time to resolve an issue") 17 | [![Percentage of issues still open](http://isitmaintained.com/badge/open/acl-dev/open-coroutine.svg)](http://isitmaintained.com/project/acl-dev/open-coroutine "Percentage of issues still open") 18 | 19 | The `open-coroutine` is a simple, efficient and generic stackfull-coroutine library, you can use this as a performance 20 | replacement for IO thread pools, see [why better](../en/why-better.md). 21 | 22 | - [Background](../../../docs/en/background.md) 23 | - [Why Rust](../../../docs/en/why-rust.md) 24 | - [Why Better](../en/why-better.md) 25 | - [Quick Start](../../../README.md) 26 | - [Coroutine Overview](../en/coroutine.md) 27 | - [Scalable Stack Overview](../en/scalable-stack.md) 28 | - [Monitor Overview](../en/monitor.md) 29 | - [Work Steal Overview](../en/work-steal.md) 30 | - [Ordered Work Steal Overview](../en/ordered-work-steal.md) 31 | - [Coroutine Pool Overview](../en/coroutine-pool.md) 32 | - [Hook Overview](../../../hook/docs/en/hook.md) 33 | -------------------------------------------------------------------------------- /core/docs/en/scalable-stack.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Scalable Stack Overview 3 | date: 2025-01-08 08:44:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # Scalable Stack Overview 8 | 9 | English | [中文](../cn/scalable-stack.md) 10 | 11 | ## Usage 12 | 13 | ```rust 14 | use open_coroutine_core::co; 15 | use open_coroutine_core::common::constants::CoroutineState; 16 | use open_coroutine_core::coroutine::suspender::Suspender; 17 | use open_coroutine_core::coroutine::Coroutine; 18 | 19 | fn main() -> std::io::Result<()> { 20 | let mut co = co!(|_: &Suspender<(), i32>, ()| { 21 | fn recurse(i: u32, p: &mut [u8; 10240]) { 22 | // You can also use `maybe_grow` in thread. 23 | Coroutine::<(), i32, ()>::maybe_grow(|| { 24 | // Ensure the stack allocation isn't optimized away. 25 | unsafe { std::ptr::read_volatile(&p) }; 26 | if i > 0 { 27 | recurse(i - 1, &mut [0; 10240]); 28 | } 29 | }) 30 | .expect("allocate stack failed") 31 | } 32 | // Use ~500KB of stack. 33 | recurse(50, &mut [0; 10240]); 34 | })?; 35 | assert_eq!(co.resume()?, CoroutineState::Complete(())); 36 | Ok(()) 37 | } 38 | ``` 39 | 40 | ## Why scalable stack? 41 | 42 | The default stack size of coroutine is 128KB, which is sufficient for most scenarios, but there are still some scenarios 43 | that are not applicable, such as recursive algorithms. The scalable stack enables annotating fixed points in programs 44 | where the stack may want to grow larger. Spills over to the heap if the stack has hit its limit. 45 | 46 | ## How it works 47 | 48 | ```mermaid 49 | flowchart TD 50 | Cond1{In coroutine?} 51 | Cond2{Approach the limit?} 52 | Cond3{Is the first growing up?} 53 | C[Create a new stack] 54 | RC[Run code on current stack] 55 | RN1[Run code on new stack] 56 | maybe_grow --> Cond1 57 | Cond1 -- Yes --> Cond2 58 | Cond2 -- Yes --> C 59 | Cond2 -- No --> RC 60 | C --> RN1 61 | Cond1 -- No --> Cond3 62 | Cond3 -- Yes --> C 63 | Cond3 -- No --> Cond2 64 | ``` 65 | -------------------------------------------------------------------------------- /core/docs/en/work-steal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Work Steal Overview 3 | date: 2025-01-17 08:34:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # Work Steal Overview 8 | 9 | English | [中文](../cn/work-steal.md) 10 | 11 | ## Why work steal? 12 | 13 | In the real world, there are always threads that complete their own tasks first, while other threads have tasks to be 14 | processed. Then, a spectacular scene emerged where one core was in difficulty and other cores were watching. 15 | 16 |
17 | 18 |
19 | 20 | Obviously, we don't want this to happen. For idle threads, instead of letting them watch other threads working, it's 21 | better to let them help other threads work. 22 | 23 | ## What is work steal queue? 24 | 25 | A work steal queue consists of a global queue and multiple local queues, the global queue is unbounded, while the local 26 | queue has a bounded RingBuffer. To ensure high performance, the number of local queues is usually equal to the number of 27 | threads. I's worth mentioning that if all threads prioritize local tasks, there will be an extreme situation where tasks 28 | on the shared queue will never have a chance to be scheduled. To avoid this imbalance, refer to goroutine, every time a 29 | thread has scheduled 60 tasks from the local queue, it will be forced to pop a task from the shared queue. 30 | 31 | ## How `push` works 32 | 33 | ```mermaid 34 | flowchart TD 35 | Cond{Is the local queue full?} 36 | PS[Push to the local queue] 37 | PTG[Push half of the tasks from the local queue to the global queue] 38 | PG[Push to the global queue] 39 | push --> Cond 40 | Cond -- No --> PS 41 | Cond -- Yes --> PTG --- PG 42 | ``` 43 | 44 | ## How `pop` works 45 | 46 | ```mermaid 47 | flowchart TD 48 | Cond1{Are there any tasks in the local queue?} 49 | Cond2{Are there any tasks in other local queues?} 50 | Cond3{Are there any tasks in the global queue?} 51 | PS[Pop a stack] 52 | ST[Steal tasks from other local queues] 53 | PF[Task not found] 54 | pop --> Cond1 55 | Cond1 -- Yes --> PS 56 | Cond1 -- No --> Cond2 57 | Cond2 -- Yes --> ST --> PS 58 | Cond2 -- No --> Cond3 59 | Cond3 -- Yes --> PS 60 | Cond3 -- No --> PF 61 | ``` 62 | -------------------------------------------------------------------------------- /core/src/co_pool/creator.rs: -------------------------------------------------------------------------------- 1 | use crate::co_pool::CoroutinePool; 2 | use crate::common::constants::CoroutineState; 3 | use crate::coroutine::listener::Listener; 4 | use crate::coroutine::local::CoroutineLocal; 5 | use crate::scheduler::SchedulableCoroutineState; 6 | use std::sync::atomic::Ordering; 7 | 8 | #[repr(C)] 9 | #[derive(Debug, Default)] 10 | pub(crate) struct CoroutineCreator {} 11 | 12 | impl Listener<(), Option> for CoroutineCreator { 13 | fn on_state_changed( 14 | &self, 15 | _: &CoroutineLocal, 16 | _: SchedulableCoroutineState, 17 | new_state: SchedulableCoroutineState, 18 | ) { 19 | match new_state { 20 | CoroutineState::Suspend((), _) | CoroutineState::Syscall((), _, _) => { 21 | if let Some(pool) = CoroutinePool::current() { 22 | _ = pool.try_grow(); 23 | } 24 | } 25 | CoroutineState::Complete(_) => { 26 | if let Some(pool) = CoroutinePool::current() { 27 | //worker协程正常退出 28 | pool.running 29 | .store(pool.get_running_size().saturating_sub(1), Ordering::Release); 30 | } 31 | } 32 | CoroutineState::Cancelled | CoroutineState::Error(_) => { 33 | if let Some(pool) = CoroutinePool::current() { 34 | //worker协程异常退出,需要先回收再创建 35 | pool.running 36 | .store(pool.get_running_size().saturating_sub(1), Ordering::Release); 37 | _ = pool.try_grow(); 38 | } 39 | } 40 | _ => {} 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/co_pool/state.rs: -------------------------------------------------------------------------------- 1 | use crate::co_pool::CoroutinePool; 2 | use crate::common::constants::PoolState; 3 | use std::io::Error; 4 | 5 | impl CoroutinePool<'_> { 6 | /// running -> stopping 7 | /// 8 | /// # Errors 9 | /// if change state fails. 10 | pub(crate) fn stopping(&self) -> std::io::Result { 11 | self.change_state(PoolState::Running, PoolState::Stopping) 12 | } 13 | 14 | /// stopping -> stopped 15 | /// 16 | /// # Errors 17 | /// if change state fails. 18 | pub(crate) fn stopped(&self) -> std::io::Result { 19 | self.change_state(PoolState::Stopping, PoolState::Stopped) 20 | } 21 | 22 | /// Get the state of this coroutine. 23 | pub fn state(&self) -> PoolState { 24 | self.state.get() 25 | } 26 | 27 | fn change_state( 28 | &self, 29 | old_state: PoolState, 30 | new_state: PoolState, 31 | ) -> std::io::Result { 32 | let current = self.state(); 33 | if current == new_state { 34 | return Ok(old_state); 35 | } 36 | if current == old_state { 37 | assert_eq!(old_state, self.state.replace(new_state)); 38 | return Ok(old_state); 39 | } 40 | Err(Error::other(format!( 41 | "{} unexpected {current}->{:?}", 42 | self.name(), 43 | new_state 44 | ))) 45 | } 46 | } 47 | 48 | #[cfg(test)] 49 | mod tests { 50 | use super::*; 51 | 52 | #[test] 53 | fn test_state() -> std::io::Result<()> { 54 | let mut pool = CoroutinePool::default(); 55 | _ = pool.stopping()?; 56 | assert_eq!(PoolState::Stopping, pool.state()); 57 | _ = pool.stopping()?; 58 | assert_eq!(PoolState::Stopping, pool.state()); 59 | _ = pool.stopped()?; 60 | assert!(pool.stopped().is_ok()); 61 | assert!(pool.stopping().is_err()); 62 | assert!(pool.try_schedule_task().is_err()); 63 | Ok(()) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /core/src/co_pool/task.rs: -------------------------------------------------------------------------------- 1 | use crate::catch; 2 | use crate::common::ordered_work_steal::Ordered; 3 | use std::ffi::c_longlong; 4 | 5 | /// 做C兼容时会用到 6 | pub type UserTaskFunc = extern "C" fn(usize) -> usize; 7 | 8 | /// The task impls. 9 | #[repr(C)] 10 | #[derive(educe::Educe)] 11 | #[educe(Debug)] 12 | pub struct Task<'t> { 13 | name: String, 14 | #[educe(Debug(ignore))] 15 | func: Box) -> Option + 't>, 16 | param: Option, 17 | priority: Option, 18 | } 19 | 20 | impl<'t> Task<'t> { 21 | /// Create a new `Task` instance. 22 | pub fn new( 23 | name: String, 24 | func: impl FnOnce(Option) -> Option + 't, 25 | param: Option, 26 | priority: Option, 27 | ) -> Self { 28 | Task { 29 | name, 30 | func: Box::new(func), 31 | param, 32 | priority, 33 | } 34 | } 35 | 36 | /// get the task name. 37 | #[must_use] 38 | pub fn get_name(&self) -> &str { 39 | &self.name 40 | } 41 | 42 | /// execute the task 43 | /// 44 | /// # Errors 45 | /// if an exception occurred while executing this task. 46 | pub fn run<'e>(self) -> (String, Result, &'e str>) { 47 | ( 48 | self.name.clone(), 49 | catch!( 50 | || (self.func)(self.param), 51 | format!("task {} failed without message", self.name), 52 | format!("task {}", self.name) 53 | ), 54 | ) 55 | } 56 | } 57 | 58 | impl Ordered for Task<'_> { 59 | fn priority(&self) -> Option { 60 | self.priority 61 | } 62 | } 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | use crate::co_pool::task::Task; 67 | 68 | #[test] 69 | fn test() { 70 | let task = Task::new( 71 | String::from("test"), 72 | |p| { 73 | println!("hello"); 74 | p 75 | }, 76 | None, 77 | None, 78 | ); 79 | assert_eq!((String::from("test"), Ok(None)), task.run()); 80 | } 81 | 82 | #[test] 83 | fn test_panic() { 84 | let task = Task::new( 85 | String::from("test"), 86 | |_| { 87 | panic!("test panic, just ignore it"); 88 | }, 89 | None, 90 | None, 91 | ); 92 | assert_eq!( 93 | (String::from("test"), Err("test panic, just ignore it")), 94 | task.run() 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /core/src/common/beans.rs: -------------------------------------------------------------------------------- 1 | use dashmap::DashMap; 2 | use std::ffi::c_void; 3 | use std::sync::atomic::{AtomicUsize, Ordering}; 4 | 5 | /// Simple bean factory. 6 | #[repr(C)] 7 | #[derive(Debug, Default)] 8 | pub struct BeanFactory<'b>(DashMap<&'b str, usize>); 9 | 10 | impl BeanFactory<'_> { 11 | fn get_instance<'i>() -> &'i BeanFactory<'i> { 12 | static INSTANCE: AtomicUsize = AtomicUsize::new(0); 13 | let mut ret = INSTANCE.load(Ordering::Relaxed); 14 | if ret == 0 { 15 | let ptr: &'i mut BeanFactory = Box::leak(Box::default()); 16 | ret = std::ptr::from_mut(ptr) as usize; 17 | INSTANCE.store(ret, Ordering::Relaxed); 18 | } 19 | unsafe { &*(ret as *mut BeanFactory) } 20 | } 21 | 22 | /// Init bean if not exists. 23 | pub fn init_bean(bean_name: &str, bean: B) { 24 | let factory = Self::get_instance(); 25 | if factory.0.get(bean_name).is_none() { 26 | let bean: &B = Box::leak(Box::new(bean)); 27 | assert!(factory 28 | .0 29 | .insert( 30 | Box::leak(Box::from(bean_name)), 31 | std::ptr::from_ref(bean) as usize, 32 | ) 33 | .is_none()); 34 | } 35 | } 36 | 37 | /// Remove bean if exists. 38 | #[must_use] 39 | pub fn remove_bean(bean_name: &str) -> Option { 40 | Self::get_instance() 41 | .0 42 | .remove(bean_name) 43 | .map(|(_, ptr)| unsafe { *Box::from_raw((ptr as *mut c_void).cast::()) }) 44 | } 45 | 46 | /// Get the bean by name. 47 | #[must_use] 48 | pub fn get_bean(bean_name: &str) -> Option<&B> { 49 | Self::get_instance() 50 | .0 51 | .get(bean_name) 52 | .map(|ptr| unsafe { &*(*ptr as *mut c_void).cast::() }) 53 | } 54 | 55 | /// Get the bean by name. 56 | /// 57 | /// # Safety 58 | /// Only one mutable reference can be held for a given bean at a time. 59 | #[allow(clippy::mut_from_ref)] 60 | #[must_use] 61 | pub unsafe fn get_mut_bean(bean_name: &str) -> Option<&mut B> { 62 | Self::get_instance() 63 | .0 64 | .get_mut(bean_name) 65 | .map(|ptr| &mut *(*ptr as *mut c_void).cast::()) 66 | } 67 | 68 | /// Get the bean by name, create bean if not exists. 69 | #[must_use] 70 | pub fn get_or_default(bean_name: &str) -> &B { 71 | let factory = Self::get_instance(); 72 | factory.0.get(bean_name).map_or_else( 73 | || { 74 | let bean: &B = Box::leak(Box::default()); 75 | _ = factory.0.insert( 76 | Box::leak(Box::from(bean_name)), 77 | std::ptr::from_ref(bean) as usize, 78 | ); 79 | bean 80 | }, 81 | |ptr| unsafe { &*(*ptr as *mut c_void).cast::() }, 82 | ) 83 | } 84 | 85 | /// Get the bean by name, create bean if not exists. 86 | /// 87 | /// # Safety 88 | /// Only one mutable reference can be held for a given bean at a time. 89 | #[must_use] 90 | #[allow(clippy::mut_from_ref)] 91 | pub unsafe fn get_mut_or_default(bean_name: &str) -> &mut B { 92 | let factory = Self::get_instance(); 93 | factory.0.get_mut(bean_name).map_or_else( 94 | || { 95 | let bean: &mut B = Box::leak(Box::default()); 96 | _ = factory.0.insert( 97 | Box::leak(Box::from(bean_name)), 98 | std::ptr::from_ref(bean) as usize, 99 | ); 100 | bean 101 | }, 102 | |ptr| &mut *(*ptr as *mut c_void).cast::(), 103 | ) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /core/src/common/ci.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Duration, Instant}; 2 | 3 | /// just for CI 4 | pub fn init() { 5 | let _ = std::thread::spawn(|| { 6 | // exit after 600 seconds, just for CI 7 | let sleep_time = Duration::from_secs(600); 8 | let start_time = Instant::now(); 9 | std::thread::sleep(sleep_time); 10 | let cost = Instant::now().saturating_duration_since(start_time); 11 | assert!(cost >= sleep_time, "CI time consumption less than expected"); 12 | std::process::exit(-1); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /core/src/config.rs: -------------------------------------------------------------------------------- 1 | use crate::common::constants::{cpu_count, DEFAULT_STACK_SIZE}; 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] 5 | pub struct Config { 6 | event_loop_size: usize, 7 | stack_size: usize, 8 | min_size: usize, 9 | max_size: usize, 10 | keep_alive_time: u64, 11 | min_memory_count: usize, 12 | memory_keep_alive_time: u64, 13 | hook: bool, 14 | } 15 | 16 | impl Config { 17 | #[must_use] 18 | pub fn single() -> Self { 19 | Self::new(1, DEFAULT_STACK_SIZE, 0, 65536, 0, 0, 0, true) 20 | } 21 | 22 | #[allow(clippy::too_many_arguments)] 23 | #[must_use] 24 | pub fn new( 25 | event_loop_size: usize, 26 | stack_size: usize, 27 | min_size: usize, 28 | max_size: usize, 29 | keep_alive_time: u64, 30 | min_memory_count: usize, 31 | memory_keep_alive_time: u64, 32 | hook: bool, 33 | ) -> Self { 34 | Self { 35 | event_loop_size, 36 | stack_size, 37 | min_size, 38 | max_size, 39 | keep_alive_time, 40 | min_memory_count, 41 | memory_keep_alive_time, 42 | hook, 43 | } 44 | } 45 | 46 | #[must_use] 47 | pub fn event_loop_size(&self) -> usize { 48 | self.event_loop_size 49 | } 50 | 51 | #[must_use] 52 | pub fn stack_size(&self) -> usize { 53 | self.stack_size 54 | } 55 | 56 | #[must_use] 57 | pub fn min_size(&self) -> usize { 58 | self.min_size 59 | } 60 | 61 | #[must_use] 62 | pub fn max_size(&self) -> usize { 63 | self.max_size 64 | } 65 | 66 | #[must_use] 67 | pub fn keep_alive_time(&self) -> u64 { 68 | self.keep_alive_time 69 | } 70 | 71 | #[must_use] 72 | pub fn min_memory_count(&self) -> usize { 73 | self.min_memory_count 74 | } 75 | 76 | #[must_use] 77 | pub fn memory_keep_alive_time(&self) -> u64 { 78 | self.memory_keep_alive_time 79 | } 80 | 81 | #[must_use] 82 | pub fn hook(&self) -> bool { 83 | self.hook 84 | } 85 | 86 | pub fn set_event_loop_size(&mut self, event_loop_size: usize) -> &mut Self { 87 | assert!( 88 | event_loop_size > 0, 89 | "event_loop_size must be greater than 0" 90 | ); 91 | self.event_loop_size = event_loop_size; 92 | self 93 | } 94 | 95 | pub fn set_stack_size(&mut self, stack_size: usize) -> &mut Self { 96 | assert!(stack_size > 0, "stack_size must be greater than 0"); 97 | self.stack_size = stack_size; 98 | self 99 | } 100 | 101 | pub fn set_min_size(&mut self, min_size: usize) -> &mut Self { 102 | self.min_size = min_size; 103 | self 104 | } 105 | 106 | pub fn set_max_size(&mut self, max_size: usize) -> &mut Self { 107 | assert!(max_size > 0, "max_size must be greater than 0"); 108 | assert!( 109 | max_size >= self.min_size, 110 | "max_size must be greater than or equal to min_size" 111 | ); 112 | self.max_size = max_size; 113 | self 114 | } 115 | 116 | pub fn set_keep_alive_time(&mut self, keep_alive_time: u64) -> &mut Self { 117 | self.keep_alive_time = keep_alive_time; 118 | self 119 | } 120 | 121 | pub fn set_min_memory_count(&mut self, min_memory_count: usize) -> &mut Self { 122 | self.min_memory_count = min_memory_count; 123 | self 124 | } 125 | 126 | pub fn set_memory_keep_alive_time(&mut self, memory_keep_alive_time: u64) -> &mut Self { 127 | self.memory_keep_alive_time = memory_keep_alive_time; 128 | self 129 | } 130 | 131 | pub fn set_hook(&mut self, hook: bool) -> &mut Self { 132 | self.hook = hook; 133 | self 134 | } 135 | } 136 | 137 | impl Default for Config { 138 | fn default() -> Self { 139 | Self::new(cpu_count(), DEFAULT_STACK_SIZE, 0, 65536, 0, 0, 0, true) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /core/src/coroutine/listener.rs: -------------------------------------------------------------------------------- 1 | use crate::common::constants::CoroutineState; 2 | use crate::coroutine::local::CoroutineLocal; 3 | use crate::coroutine::Coroutine; 4 | use std::fmt::Debug; 5 | 6 | /// A trait mainly used for monitors. 7 | #[allow(unused_variables)] 8 | pub trait Listener: Debug { 9 | /// Callback after changing the status of coroutine. 10 | fn on_state_changed( 11 | &self, 12 | local: &CoroutineLocal, 13 | old_state: CoroutineState, 14 | new_state: CoroutineState, 15 | ) { 16 | } 17 | 18 | /// Callback after changing the coroutine status to ready. 19 | fn on_ready(&self, local: &CoroutineLocal, old_state: CoroutineState) {} 20 | 21 | /// Callback after changing the coroutine status to running. 22 | fn on_running(&self, local: &CoroutineLocal, old_state: CoroutineState) {} 23 | 24 | /// Callback after changing the coroutine status to suspend. 25 | fn on_suspend(&self, local: &CoroutineLocal, old_state: CoroutineState) {} 26 | 27 | /// callback when the coroutine enters syscall. 28 | fn on_syscall(&self, local: &CoroutineLocal, old_state: CoroutineState) {} 29 | 30 | /// Callback when the coroutine is cancelled. 31 | fn on_cancel(&self, local: &CoroutineLocal, old_state: CoroutineState) {} 32 | 33 | /// Callback when the coroutine is completed. 34 | fn on_complete( 35 | &self, 36 | local: &CoroutineLocal, 37 | old_state: CoroutineState, 38 | result: Return, 39 | ) { 40 | } 41 | 42 | /// Callback when the coroutine is completed with errors, usually, panic occurs. 43 | fn on_error( 44 | &self, 45 | local: &CoroutineLocal, 46 | old_state: CoroutineState, 47 | message: &str, 48 | ) { 49 | } 50 | } 51 | 52 | macro_rules! broadcast { 53 | ($impl_method_name: ident($($arg: ident : $arg_type: ty),*), $method_name:expr) => { 54 | fn $impl_method_name(&self, $($arg: $arg_type),*) { 55 | for listener in &self.listeners { 56 | _ = $crate::catch!( 57 | || listener.$impl_method_name($($arg, )*), 58 | format!("Listener {} failed without message", $method_name), 59 | format!("{} invoke {}", self.name(), $method_name) 60 | ); 61 | } 62 | } 63 | } 64 | } 65 | 66 | impl Listener for Coroutine<'_, Param, Yield, Return> 67 | where 68 | Yield: Debug + Copy, 69 | Return: Debug + Copy, 70 | { 71 | broadcast!(on_state_changed( 72 | local: &CoroutineLocal, 73 | old_state: CoroutineState, 74 | new_state: CoroutineState 75 | ), "on_state_changed"); 76 | 77 | broadcast!(on_ready( 78 | local: &CoroutineLocal, 79 | old_state: CoroutineState 80 | ), "on_ready"); 81 | 82 | broadcast!(on_running( 83 | local: &CoroutineLocal, 84 | old_state: CoroutineState 85 | ), "on_running"); 86 | 87 | broadcast!(on_suspend( 88 | local: &CoroutineLocal, 89 | old_state: CoroutineState 90 | ), "on_suspend"); 91 | 92 | broadcast!(on_syscall( 93 | local: &CoroutineLocal, 94 | old_state: CoroutineState 95 | ), "on_syscall"); 96 | 97 | broadcast!(on_cancel( 98 | local: &CoroutineLocal, 99 | old_state: CoroutineState 100 | ), "on_cancel"); 101 | 102 | broadcast!(on_complete( 103 | local: &CoroutineLocal, 104 | old_state: CoroutineState, 105 | result: Return 106 | ), "on_complete"); 107 | 108 | broadcast!(on_error( 109 | local: &CoroutineLocal, 110 | old_state: CoroutineState, 111 | message: &str 112 | ), "on_error"); 113 | } 114 | -------------------------------------------------------------------------------- /core/src/coroutine/local.rs: -------------------------------------------------------------------------------- 1 | use crate::impl_display_by_debug; 2 | use dashmap::DashMap; 3 | use std::ffi::c_void; 4 | use std::fmt::Debug; 5 | 6 | /// todo provide macro like [`std::thread_local`] 7 | /// A struct for coroutines handles local args. 8 | #[repr(C)] 9 | #[derive(Debug, Default)] 10 | pub struct CoroutineLocal<'c>(DashMap<&'c str, usize>); 11 | 12 | #[allow(clippy::must_use_candidate)] 13 | impl<'c> CoroutineLocal<'c> { 14 | /// Put a value into the coroutine local. 15 | pub fn put(&self, key: &'c str, val: V) -> Option { 16 | let v = Box::leak(Box::new(val)); 17 | self.0 18 | .insert(key, std::ptr::from_mut(v) as usize) 19 | .map(|ptr| unsafe { *Box::from_raw((ptr as *mut c_void).cast::()) }) 20 | } 21 | 22 | /// Get a value ref from the coroutine local. 23 | pub fn get(&self, key: &'c str) -> Option<&V> { 24 | self.0 25 | .get(key) 26 | .map(|ptr| unsafe { &*(*ptr as *mut c_void).cast::() }) 27 | } 28 | 29 | /// Get a mut value ref from the coroutine local. 30 | #[allow(clippy::mut_from_ref)] 31 | pub fn get_mut(&self, key: &'c str) -> Option<&mut V> { 32 | self.0 33 | .get(key) 34 | .map(|ptr| unsafe { &mut *(*ptr as *mut c_void).cast::() }) 35 | } 36 | 37 | /// Remove a key from the coroutine local. 38 | pub fn remove(&self, key: &'c str) -> Option { 39 | self.0 40 | .remove(key) 41 | .map(|ptr| unsafe { *Box::from_raw((ptr.1 as *mut c_void).cast::()) }) 42 | } 43 | } 44 | 45 | impl_display_by_debug!(CoroutineLocal<'c>); 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use super::*; 50 | 51 | #[test] 52 | fn test_local() { 53 | let local = CoroutineLocal::default(); 54 | assert!(local.put("1", 1).is_none()); 55 | assert_eq!(Some(1), local.put("1", 2)); 56 | assert_eq!(2, *local.get::("1").unwrap()); 57 | *local.get_mut("1").unwrap() = 3; 58 | assert_eq!(Some(3), local.remove("1")); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny( 2 | // The following are allowed by default lints according to 3 | // https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html 4 | absolute_paths_not_starting_with_crate, 5 | explicit_outlives_requirements, 6 | macro_use_extern_crate, 7 | redundant_lifetimes, 8 | anonymous_parameters, 9 | bare_trait_objects, 10 | // elided_lifetimes_in_paths, // allow anonymous lifetime 11 | missing_copy_implementations, 12 | missing_debug_implementations, 13 | missing_docs, 14 | // single_use_lifetimes, // TODO: fix lifetime names only used once 15 | // trivial_casts, 16 | trivial_numeric_casts, 17 | unreachable_pub, 18 | // unsafe_code, 19 | unstable_features, 20 | // unused_crate_dependencies, 21 | unused_lifetimes, 22 | unused_macro_rules, 23 | unused_extern_crates, 24 | unused_import_braces, 25 | unused_qualifications, 26 | unused_results, 27 | variant_size_differences, 28 | 29 | warnings, // treat all wanings as errors 30 | 31 | clippy::all, 32 | // clippy::restriction, 33 | clippy::pedantic, 34 | // clippy::nursery, // It's still under development 35 | clippy::cargo, 36 | )] 37 | #![allow( 38 | // Some explicitly allowed Clippy lints, must have clear reason to allow 39 | clippy::blanket_clippy_restriction_lints, // allow clippy::restriction 40 | clippy::implicit_return, // actually omitting the return keyword is idiomatic Rust code 41 | clippy::module_name_repetitions, // repeation of module name in a struct name is not big deal 42 | clippy::multiple_crate_versions, // multi-version dependency crates is not able to fix 43 | clippy::missing_errors_doc, // TODO: add error docs 44 | clippy::missing_panics_doc, // TODO: add panic docs 45 | clippy::panic_in_result_fn, 46 | clippy::shadow_same, // Not too much bad 47 | clippy::shadow_reuse, // Not too much bad 48 | clippy::exhaustive_enums, 49 | clippy::exhaustive_structs, 50 | clippy::indexing_slicing, 51 | clippy::separated_literal_suffix, // conflicts with clippy::unseparated_literal_suffix 52 | clippy::single_char_lifetime_names, // TODO: change lifetime names 53 | unknown_lints, // for windows nightly 54 | linker_messages, // for windows nightly 55 | unused_attributes, // for windows nightly 56 | )] 57 | #![doc = include_str!("../docs/en/overview.md")] 58 | 59 | /// Common traits and impl. 60 | pub mod common; 61 | 62 | /// Configuration for `EventLoops`. 63 | #[allow(missing_docs)] 64 | pub mod config; 65 | 66 | #[doc = include_str!("../docs/en/coroutine.md")] 67 | pub mod coroutine; 68 | 69 | #[cfg(all(unix, feature = "preemptive"))] 70 | #[doc = include_str!("../docs/en/monitor.md")] 71 | mod monitor; 72 | 73 | /// Schedule many coroutines. 74 | pub mod scheduler; 75 | 76 | /// Coroutine pool abstraction and impl. 77 | #[doc = include_str!("../docs/en/coroutine-pool.md")] 78 | pub mod co_pool; 79 | 80 | /// net abstraction and impl. 81 | #[allow(dead_code)] 82 | #[cfg(feature = "net")] 83 | pub mod net; 84 | 85 | /// Syscall impl. 86 | #[allow( 87 | missing_docs, 88 | clippy::similar_names, 89 | clippy::not_unsafe_ptr_arg_deref, 90 | clippy::many_single_char_names, 91 | clippy::useless_conversion, 92 | clippy::unnecessary_cast, 93 | trivial_numeric_casts 94 | )] 95 | #[cfg(feature = "syscall")] 96 | pub mod syscall; 97 | -------------------------------------------------------------------------------- /core/src/net/join.rs: -------------------------------------------------------------------------------- 1 | use crate::net::event_loop::EventLoop; 2 | use std::ffi::{c_char, CStr, CString}; 3 | use std::io::{Error, ErrorKind}; 4 | use std::sync::Arc; 5 | use std::time::Duration; 6 | 7 | #[allow(missing_docs)] 8 | #[repr(C)] 9 | #[derive(Debug)] 10 | pub struct JoinHandle(&'static Arc>, *const c_char); 11 | 12 | impl Drop for JoinHandle { 13 | fn drop(&mut self) { 14 | if let Ok(name) = self.get_name() { 15 | self.0.clean_task_result(name); 16 | } 17 | } 18 | } 19 | 20 | impl JoinHandle { 21 | /// create `JoinHandle` instance. 22 | pub(crate) fn err(pool: &'static Arc>) -> Self { 23 | Self::new(pool, "") 24 | } 25 | 26 | /// create `JoinHandle` instance. 27 | pub(crate) fn new(pool: &'static Arc>, name: &str) -> Self { 28 | let boxed: &'static mut CString = Box::leak(Box::from( 29 | CString::new(name).expect("init JoinHandle failed!"), 30 | )); 31 | let cstr: &'static CStr = boxed.as_c_str(); 32 | JoinHandle(pool, cstr.as_ptr()) 33 | } 34 | 35 | /// get the task name. 36 | /// 37 | /// # Errors 38 | /// if the task name is invalid. 39 | pub fn get_name(&self) -> std::io::Result<&str> { 40 | unsafe { CStr::from_ptr(self.1) } 41 | .to_str() 42 | .map_err(|_| Error::new(ErrorKind::InvalidInput, "Invalid task name")) 43 | } 44 | 45 | /// join with `Duration`. 46 | /// 47 | /// # Errors 48 | /// see `timeout_at_join`. 49 | pub fn timeout_join(&self, dur: Duration) -> std::io::Result, &str>> { 50 | self.timeout_at_join(crate::common::get_timeout_time(dur)) 51 | } 52 | 53 | /// join. 54 | /// 55 | /// # Errors 56 | /// see `timeout_at_join`. 57 | pub fn join(&self) -> std::io::Result, &str>> { 58 | self.timeout_at_join(u64::MAX) 59 | } 60 | 61 | /// join with timeout. 62 | /// 63 | /// # Errors 64 | /// if join failed. 65 | pub fn timeout_at_join( 66 | &self, 67 | timeout_time: u64, 68 | ) -> std::io::Result, &str>> { 69 | let name = self.get_name()?; 70 | if name.is_empty() { 71 | return Err(Error::new(ErrorKind::InvalidInput, "Invalid task name")); 72 | } 73 | self.0.wait_task_result( 74 | name, 75 | Duration::from_nanos(timeout_time.saturating_sub(crate::common::now())), 76 | ) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /core/src/net/operator/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all(target_os = "linux", feature = "io_uring"))] 2 | mod linux; 3 | #[cfg(all(target_os = "linux", feature = "io_uring"))] 4 | pub(crate) use linux::*; 5 | 6 | #[allow(non_snake_case)] 7 | #[cfg(all(windows, feature = "iocp"))] 8 | mod windows; 9 | #[cfg(all(windows, feature = "iocp"))] 10 | pub(crate) use windows::*; 11 | -------------------------------------------------------------------------------- /core/src/net/selector/mio_adapter.rs: -------------------------------------------------------------------------------- 1 | use crate::common::CondvarBlocker; 2 | use crossbeam_utils::atomic::AtomicCell; 3 | use mio::event::Event; 4 | use mio::unix::SourceFd; 5 | use mio::{Events, Interest, Poll, Token}; 6 | use std::ffi::c_int; 7 | use std::ops::Deref; 8 | use std::sync::atomic::AtomicBool; 9 | use std::time::Duration; 10 | 11 | impl super::Interest for Interest { 12 | fn read(_: usize) -> Self { 13 | Interest::READABLE 14 | } 15 | 16 | fn write(_: usize) -> Self { 17 | Interest::WRITABLE 18 | } 19 | 20 | fn read_and_write(_: usize) -> Self { 21 | Interest::READABLE.add(Interest::WRITABLE) 22 | } 23 | } 24 | 25 | impl super::Event for Event { 26 | fn get_token(&self) -> usize { 27 | self.token().0 28 | } 29 | 30 | fn readable(&self) -> bool { 31 | self.is_readable() 32 | } 33 | 34 | fn writable(&self) -> bool { 35 | self.is_writable() 36 | } 37 | } 38 | 39 | impl super::EventIterator for Events { 40 | fn iterator<'a>(&'a self) -> impl Iterator 41 | where 42 | Event: 'a, 43 | { 44 | self.iter() 45 | } 46 | } 47 | 48 | #[repr(C)] 49 | #[derive(educe::Educe)] 50 | #[educe(Debug)] 51 | pub(crate) struct Poller { 52 | waiting: AtomicBool, 53 | blocker: CondvarBlocker, 54 | #[educe(Debug(ignore))] 55 | inner: AtomicCell, 56 | } 57 | 58 | impl Poller { 59 | pub(crate) fn new() -> std::io::Result { 60 | Ok(Self { 61 | waiting: AtomicBool::new(false), 62 | blocker: CondvarBlocker::default(), 63 | inner: AtomicCell::new(Poll::new()?), 64 | }) 65 | } 66 | } 67 | 68 | impl Deref for Poller { 69 | type Target = Poll; 70 | 71 | fn deref(&self) -> &Self::Target { 72 | unsafe { &*self.inner.as_ptr() } 73 | } 74 | } 75 | 76 | impl super::Selector for Poller { 77 | fn waiting(&self) -> &AtomicBool { 78 | &self.waiting 79 | } 80 | 81 | fn blocker(&self) -> &CondvarBlocker { 82 | &self.blocker 83 | } 84 | 85 | fn do_select(&self, events: &mut Events, timeout: Option) -> std::io::Result<()> { 86 | let inner = unsafe { &mut *self.inner.as_ptr() }; 87 | inner.poll(events, timeout) 88 | } 89 | 90 | fn do_register(&self, fd: c_int, token: usize, interests: Interest) -> std::io::Result<()> { 91 | self.registry() 92 | .register(&mut SourceFd(&fd), Token(token), interests) 93 | } 94 | 95 | fn do_reregister(&self, fd: c_int, token: usize, interests: Interest) -> std::io::Result<()> { 96 | self.registry() 97 | .reregister(&mut SourceFd(&fd), Token(token), interests) 98 | } 99 | 100 | fn do_deregister(&self, fd: c_int, _: usize) -> std::io::Result<()> { 101 | self.registry().deregister(&mut SourceFd(&fd)) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /core/src/net/selector/polling_adapter.rs: -------------------------------------------------------------------------------- 1 | use crate::common::CondvarBlocker; 2 | use polling::{Event, PollMode}; 3 | use std::ffi::c_int; 4 | use std::ops::{Deref, DerefMut}; 5 | use std::sync::atomic::AtomicBool; 6 | use std::time::Duration; 7 | 8 | pub(crate) type Events = Vec; 9 | 10 | impl super::Interest for Event { 11 | fn read(token: usize) -> Self { 12 | Event::readable(token) 13 | } 14 | 15 | fn write(token: usize) -> Self { 16 | Event::writable(token) 17 | } 18 | 19 | fn read_and_write(token: usize) -> Self { 20 | Event::all(token) 21 | } 22 | } 23 | 24 | impl super::Event for Event { 25 | fn get_token(&self) -> usize { 26 | self.key 27 | } 28 | 29 | fn readable(&self) -> bool { 30 | self.readable 31 | } 32 | 33 | fn writable(&self) -> bool { 34 | self.writable 35 | } 36 | } 37 | 38 | impl super::EventIterator for Events { 39 | fn iterator<'a>(&'a self) -> impl Iterator 40 | where 41 | Event: 'a, 42 | { 43 | self.iter() 44 | } 45 | } 46 | 47 | #[repr(C)] 48 | #[derive(Debug)] 49 | pub(crate) struct Poller { 50 | waiting: AtomicBool, 51 | blocker: CondvarBlocker, 52 | inner: polling::Poller, 53 | } 54 | 55 | impl Poller { 56 | pub(crate) fn new() -> std::io::Result { 57 | Ok(Self { 58 | waiting: AtomicBool::new(false), 59 | blocker: CondvarBlocker::default(), 60 | inner: polling::Poller::new()?, 61 | }) 62 | } 63 | } 64 | 65 | impl Deref for Poller { 66 | type Target = polling::Poller; 67 | 68 | fn deref(&self) -> &Self::Target { 69 | &self.inner 70 | } 71 | } 72 | 73 | impl DerefMut for Poller { 74 | fn deref_mut(&mut self) -> &mut Self::Target { 75 | &mut self.inner 76 | } 77 | } 78 | 79 | impl super::Selector for Poller { 80 | fn waiting(&self) -> &AtomicBool { 81 | &self.waiting 82 | } 83 | 84 | fn blocker(&self) -> &CondvarBlocker { 85 | &self.blocker 86 | } 87 | 88 | fn do_select(&self, events: &mut Events, timeout: Option) -> std::io::Result<()> { 89 | self.wait(events, timeout).map(|_| ()) 90 | } 91 | 92 | fn do_register(&self, fd: c_int, _: usize, interests: Event) -> std::io::Result<()> { 93 | cfg_if::cfg_if! { 94 | if #[cfg(windows)] { 95 | let source = std::os::windows::io::RawSocket::from(u32::try_from(fd).expect("overflow")); 96 | } else { 97 | let source = fd; 98 | } 99 | } 100 | self.add_with_mode( 101 | source, 102 | interests, 103 | if self.supports_edge() { 104 | PollMode::Edge 105 | } else { 106 | PollMode::Level 107 | }, 108 | ) 109 | } 110 | 111 | fn do_reregister(&self, fd: c_int, _: usize, interests: Event) -> std::io::Result<()> { 112 | cfg_if::cfg_if! { 113 | if #[cfg(windows)] { 114 | let source = std::os::windows::io::RawSocket::from(u32::try_from(fd).expect("overflow")); 115 | } else { 116 | let source = fd; 117 | } 118 | } 119 | self.modify_with_mode( 120 | source, 121 | interests, 122 | if self.supports_edge() { 123 | PollMode::Edge 124 | } else { 125 | PollMode::Level 126 | }, 127 | ) 128 | } 129 | 130 | fn do_deregister(&self, fd: c_int, _: usize) -> std::io::Result<()> { 131 | cfg_if::cfg_if! { 132 | if #[cfg(windows)] { 133 | let source = std::os::windows::io::RawSocket::from(u32::try_from(fd).expect("overflow")); 134 | } else { 135 | let source = fd; 136 | } 137 | } 138 | self.delete(source) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /core/src/syscall/mod.rs: -------------------------------------------------------------------------------- 1 | macro_rules! syscall_mod { 2 | ($($mod_name: ident);*$(;)?) => { 3 | $( 4 | pub use $mod_name::$mod_name; 5 | mod $mod_name; 6 | )* 7 | } 8 | } 9 | 10 | #[cfg(unix)] 11 | pub use unix::*; 12 | 13 | #[cfg(unix)] 14 | mod unix; 15 | 16 | #[cfg(windows)] 17 | pub use windows::*; 18 | 19 | #[allow(non_snake_case)] 20 | #[cfg(windows)] 21 | mod windows; 22 | -------------------------------------------------------------------------------- /core/src/syscall/unix/accept.rs: -------------------------------------------------------------------------------- 1 | use libc::{sockaddr, socklen_t}; 2 | use std::ffi::c_int; 3 | 4 | trait AcceptSyscall { 5 | extern "C" fn accept( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *mut sockaddr, *mut socklen_t) -> c_int>, 8 | fd: c_int, 9 | address: *mut sockaddr, 10 | address_len: *mut socklen_t, 11 | ) -> c_int; 12 | } 13 | 14 | impl_syscall!(AcceptSyscallFacade, IoUringAcceptSyscall, NioAcceptSyscall, RawAcceptSyscall, 15 | accept(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int 16 | ); 17 | 18 | impl_facade!(AcceptSyscallFacade, AcceptSyscall, 19 | accept(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int 20 | ); 21 | 22 | impl_io_uring_read!(IoUringAcceptSyscall, AcceptSyscall, 23 | accept(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int 24 | ); 25 | 26 | impl_nio_read!(NioAcceptSyscall, AcceptSyscall, 27 | accept(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int 28 | ); 29 | 30 | impl_raw!(RawAcceptSyscall, AcceptSyscall, 31 | accept(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int 32 | ); 33 | -------------------------------------------------------------------------------- /core/src/syscall/unix/accept4.rs: -------------------------------------------------------------------------------- 1 | use libc::{sockaddr, socklen_t}; 2 | use std::ffi::c_int; 3 | 4 | trait Accept4Syscall { 5 | extern "C" fn accept4( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int>, 8 | fd: c_int, 9 | addr: *mut sockaddr, 10 | len: *mut socklen_t, 11 | flg: c_int, 12 | ) -> c_int; 13 | } 14 | 15 | impl_syscall!(Accept4SyscallFacade, IoUringAccept4Syscall, NioAccept4Syscall, RawAccept4Syscall, 16 | accept4(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t, flg: c_int) -> c_int 17 | ); 18 | 19 | impl_facade!(Accept4SyscallFacade, Accept4Syscall, 20 | accept4(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t, flg: c_int) -> c_int 21 | ); 22 | 23 | impl_io_uring!(IoUringAccept4Syscall, Accept4Syscall, 24 | accept4(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t, flg: c_int) -> c_int 25 | ); 26 | 27 | impl_nio_read!(NioAccept4Syscall, Accept4Syscall, 28 | accept4(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t, flg: c_int) -> c_int 29 | ); 30 | 31 | impl_raw!(RawAccept4Syscall, Accept4Syscall, 32 | accept4(fd: c_int, address: *mut sockaddr, address_len: *mut socklen_t, flg: c_int) -> c_int 33 | ); 34 | -------------------------------------------------------------------------------- /core/src/syscall/unix/close.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use std::ffi::c_int; 3 | 4 | trait CloseSyscall { 5 | extern "C" fn close(&self, fn_ptr: Option<&extern "C" fn(c_int) -> c_int>, fd: c_int) -> c_int; 6 | } 7 | 8 | impl_syscall!(CloseSyscallFacade, IoUringCloseSyscall, NioCloseSyscall, RawCloseSyscall, 9 | close(fd: c_int) -> c_int 10 | ); 11 | 12 | impl_facade!(CloseSyscallFacade, CloseSyscall, close(fd: c_int) -> c_int); 13 | 14 | impl_io_uring!(IoUringCloseSyscall, CloseSyscall, close(fd: c_int) -> c_int); 15 | 16 | #[repr(C)] 17 | #[derive(Debug, Default)] 18 | struct NioCloseSyscall { 19 | inner: I, 20 | } 21 | 22 | impl CloseSyscall for NioCloseSyscall { 23 | extern "C" fn close(&self, fn_ptr: Option<&extern "C" fn(c_int) -> c_int>, fd: c_int) -> c_int { 24 | _ = EventLoops::del_event(fd); 25 | self.inner.close(fn_ptr, fd) 26 | } 27 | } 28 | 29 | impl_raw!(RawCloseSyscall, CloseSyscall, close(fd: c_int) -> c_int); 30 | -------------------------------------------------------------------------------- /core/src/syscall/unix/connect.rs: -------------------------------------------------------------------------------- 1 | use crate::common::now; 2 | use crate::net::EventLoops; 3 | use crate::syscall::{is_blocking, reset_errno, send_time_limit, set_blocking, set_errno, set_non_blocking}; 4 | use libc::{sockaddr, socklen_t}; 5 | use std::ffi::{c_int, c_void}; 6 | use std::io::Error; 7 | 8 | trait ConnectSyscall { 9 | extern "C" fn connect( 10 | &self, 11 | fn_ptr: Option<&extern "C" fn(c_int, *const sockaddr, socklen_t) -> c_int>, 12 | fd: c_int, 13 | address: *const sockaddr, 14 | len: socklen_t, 15 | ) -> c_int; 16 | } 17 | 18 | impl_syscall!(ConnectSyscallFacade, IoUringConnectSyscall, NioConnectSyscall, RawConnectSyscall, 19 | connect(fd: c_int, address: *const sockaddr, len: socklen_t) -> c_int 20 | ); 21 | 22 | impl_facade!(ConnectSyscallFacade, ConnectSyscall, 23 | connect(fd: c_int, address: *const sockaddr, len: socklen_t) -> c_int 24 | ); 25 | 26 | impl_io_uring_write!(IoUringConnectSyscall, ConnectSyscall, 27 | connect(fd: c_int, address: *const sockaddr, len: socklen_t) -> c_int 28 | ); 29 | 30 | #[repr(C)] 31 | #[derive(Debug, Default)] 32 | struct NioConnectSyscall { 33 | inner: I, 34 | } 35 | 36 | impl ConnectSyscall for NioConnectSyscall { 37 | extern "C" fn connect( 38 | &self, 39 | fn_ptr: Option<&extern "C" fn(c_int, *const sockaddr, socklen_t) -> c_int>, 40 | fd: c_int, 41 | address: *const sockaddr, 42 | len: socklen_t, 43 | ) -> c_int { 44 | let blocking = is_blocking(fd); 45 | if blocking { 46 | set_non_blocking(fd); 47 | } 48 | let start_time = now(); 49 | let mut left_time = send_time_limit(fd); 50 | let mut r = self.inner.connect(fn_ptr, fd, address, len); 51 | while left_time > 0 { 52 | if r == 0 { 53 | reset_errno(); 54 | break; 55 | } 56 | let errno = Error::last_os_error().raw_os_error(); 57 | if errno == Some(libc::EINPROGRESS) || errno == Some(libc::EALREADY) || errno == Some(libc::EWOULDBLOCK) { 58 | //阻塞,直到写事件发生 59 | left_time = start_time 60 | .saturating_add(send_time_limit(fd)) 61 | .saturating_sub(now()); 62 | let wait_time = std::time::Duration::from_nanos(left_time) 63 | .min(crate::common::constants::SLICE); 64 | if EventLoops::wait_write_event(fd, Some(wait_time)).is_err() 65 | { 66 | break; 67 | } 68 | let mut err = 0; 69 | unsafe { 70 | let mut len = socklen_t::try_from(size_of_val(&err)).expect("overflow"); 71 | r = libc::getsockopt( 72 | fd, 73 | libc::SOL_SOCKET, 74 | libc::SO_ERROR, 75 | std::ptr::addr_of_mut!(err).cast::(), 76 | &raw mut len, 77 | ); 78 | } 79 | if r != 0 { 80 | r = -1; 81 | break; 82 | } 83 | if err != 0 { 84 | set_errno(err); 85 | r = -1; 86 | break; 87 | } 88 | unsafe { 89 | let mut address = std::mem::zeroed(); 90 | let mut address_len = socklen_t::try_from(size_of_val(&address)).expect("overflow"); 91 | r = libc::getpeername(fd, &raw mut address, &raw mut address_len); 92 | } 93 | } else if errno != Some(libc::EINTR) { 94 | break; 95 | } 96 | } 97 | if r == -1 && Error::last_os_error().raw_os_error() == Some(libc::ETIMEDOUT) { 98 | set_errno(libc::EINPROGRESS); 99 | } 100 | if blocking { 101 | set_blocking(fd); 102 | } 103 | r 104 | } 105 | } 106 | 107 | impl_raw!(RawConnectSyscall, ConnectSyscall, 108 | connect(fd: c_int, address: *const sockaddr, len: socklen_t) -> c_int 109 | ); 110 | -------------------------------------------------------------------------------- /core/src/syscall/unix/fsync.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | 3 | trait FsyncSyscall { 4 | extern "C" fn fsync( 5 | &self, 6 | fn_ptr: Option<&extern "C" fn(c_int) -> c_int>, 7 | fd: c_int, 8 | ) -> c_int; 9 | } 10 | 11 | impl_syscall2!(FsyncSyscallFacade, IoUringFsyncSyscall, RawFsyncSyscall, fsync(fd: c_int) -> c_int); 12 | 13 | impl_facade!(FsyncSyscallFacade, FsyncSyscall, fsync(fd: c_int) -> c_int); 14 | 15 | impl_io_uring!(IoUringFsyncSyscall, FsyncSyscall, fsync(fd: c_int) -> c_int); 16 | 17 | impl_raw!(RawFsyncSyscall, FsyncSyscall, fsync(fd: c_int) -> c_int); 18 | -------------------------------------------------------------------------------- /core/src/syscall/unix/link.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_char, c_int}; 2 | 3 | trait LinkSyscall { 4 | extern "C" fn link( 5 | &self, 6 | fn_ptr: Option<&extern "C" fn(*const c_char, *const c_char) -> c_int>, 7 | src: *const c_char, 8 | dst: *const c_char 9 | ) -> c_int; 10 | } 11 | 12 | impl_syscall!(LinkSyscallFacade, RawLinkSyscall, 13 | link(src: *const c_char, dst: *const c_char) -> c_int 14 | ); 15 | 16 | impl_facade!(LinkSyscallFacade, LinkSyscall, 17 | link(src: *const c_char, dst: *const c_char) -> c_int 18 | ); 19 | 20 | impl_raw!(RawLinkSyscall, LinkSyscall, 21 | link(src: *const c_char, dst: *const c_char) -> c_int 22 | ); 23 | -------------------------------------------------------------------------------- /core/src/syscall/unix/listen.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | 3 | trait ListenSyscall { 4 | extern "C" fn listen( 5 | &self, 6 | fn_ptr: Option<&extern "C" fn(c_int, c_int) -> c_int>, 7 | fd: c_int, 8 | backlog: c_int, 9 | ) -> c_int; 10 | } 11 | 12 | impl_syscall!(ListenSyscallFacade, RawListenSyscall, listen(fd: c_int, backlog: c_int) -> c_int); 13 | 14 | impl_facade!(ListenSyscallFacade, ListenSyscall, listen(fd: c_int, backlog: c_int) -> c_int); 15 | 16 | impl_raw!(RawListenSyscall, ListenSyscall, listen(fd: c_int, backlog: c_int) -> c_int); 17 | -------------------------------------------------------------------------------- /core/src/syscall/unix/lseek.rs: -------------------------------------------------------------------------------- 1 | use libc::off_t; 2 | use std::ffi::c_int; 3 | 4 | trait LseekSyscall { 5 | extern "C" fn lseek( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, off_t, c_int) -> off_t>, 8 | fd: c_int, 9 | offset: off_t, 10 | whence: c_int, 11 | ) -> off_t; 12 | } 13 | 14 | impl_syscall!(LseekSyscallFacade, RawLseekSyscall, 15 | lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t 16 | ); 17 | 18 | impl_facade!(LseekSyscallFacade, LseekSyscall, 19 | lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t 20 | ); 21 | 22 | impl_raw!(RawLseekSyscall, LseekSyscall, 23 | lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t 24 | ); 25 | -------------------------------------------------------------------------------- /core/src/syscall/unix/mkdir.rs: -------------------------------------------------------------------------------- 1 | use libc::mode_t; 2 | use std::ffi::{c_char, c_int}; 3 | 4 | trait MkdirSyscall { 5 | extern "C" fn mkdir( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(*const c_char, mode_t) -> c_int>, 8 | path: *const c_char, 9 | mode: mode_t, 10 | ) -> c_int; 11 | } 12 | 13 | impl_syscall!(MkdirSyscallFacade, RawMkdirSyscall, 14 | mkdir(path: *const c_char, mode: mode_t) -> c_int 15 | ); 16 | 17 | impl_facade!(MkdirSyscallFacade, MkdirSyscall, 18 | mkdir(path: *const c_char, mode: mode_t) -> c_int 19 | ); 20 | 21 | impl_raw!(RawMkdirSyscall, MkdirSyscall, 22 | mkdir(path: *const c_char, mode: mode_t) -> c_int 23 | ); 24 | -------------------------------------------------------------------------------- /core/src/syscall/unix/mkdirat.rs: -------------------------------------------------------------------------------- 1 | use libc::mode_t; 2 | use std::ffi::{c_char, c_int}; 3 | 4 | trait MkdiratSyscall { 5 | extern "C" fn mkdirat( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *const c_char, mode_t) -> c_int>, 8 | dirfd: c_int, 9 | pathname: *const c_char, 10 | mode: mode_t, 11 | ) -> c_int; 12 | } 13 | 14 | impl_syscall2!(MkdiratSyscallFacade, IoUringMkdiratSyscall, RawMkdiratSyscall, 15 | mkdirat(dirfd: c_int, pathname: *const c_char, mode: mode_t) -> c_int 16 | ); 17 | 18 | impl_facade!(MkdiratSyscallFacade, MkdiratSyscall, 19 | mkdirat(dirfd: c_int, pathname: *const c_char, mode: mode_t) -> c_int 20 | ); 21 | 22 | impl_io_uring!(IoUringMkdiratSyscall, MkdiratSyscall, 23 | mkdirat(dirfd: c_int, pathname: *const c_char, mode: mode_t) -> c_int 24 | ); 25 | 26 | impl_raw!(RawMkdiratSyscall, MkdiratSyscall, 27 | mkdirat(dirfd: c_int, pathname: *const c_char, mode: mode_t) -> c_int 28 | ); 29 | -------------------------------------------------------------------------------- /core/src/syscall/unix/nanosleep.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use crate::syscall::{reset_errno, set_errno}; 3 | use libc::timespec; 4 | use std::ffi::c_int; 5 | use std::time::Duration; 6 | 7 | trait NanosleepSyscall { 8 | extern "C" fn nanosleep( 9 | &self, 10 | fn_ptr: Option<&extern "C" fn(*const timespec, *mut timespec) -> c_int>, 11 | rqtp: *const timespec, 12 | rmtp: *mut timespec, 13 | ) -> c_int; 14 | } 15 | 16 | impl_syscall!(NanosleepSyscallFacade, NioNanosleepSyscall, 17 | nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int 18 | ); 19 | 20 | impl_facade!(NanosleepSyscallFacade, NanosleepSyscall, 21 | nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int 22 | ); 23 | 24 | #[repr(C)] 25 | #[derive(Debug, Copy, Clone, Default)] 26 | struct NioNanosleepSyscall {} 27 | 28 | impl NanosleepSyscall for NioNanosleepSyscall { 29 | extern "C" fn nanosleep( 30 | &self, 31 | _: Option<&extern "C" fn(*const timespec, *mut timespec) -> c_int>, 32 | rqtp: *const timespec, 33 | rmtp: *mut timespec, 34 | ) -> c_int { 35 | let rqtp = unsafe { *rqtp }; 36 | if rqtp.tv_sec < 0 || rqtp.tv_nsec < 0 || rqtp.tv_nsec > 999_999_999 { 37 | set_errno(libc::EINVAL); 38 | return -1; 39 | } 40 | let time = Duration::new( 41 | rqtp.tv_sec.try_into().expect("overflow"), 42 | rqtp.tv_nsec.try_into().expect("overflow") 43 | ); 44 | if let Some(co) = crate::scheduler::SchedulableCoroutine::current() { 45 | let syscall = crate::common::constants::SyscallName::nanosleep; 46 | let new_state = crate::common::constants::SyscallState::Suspend( 47 | crate::common::get_timeout_time(time), 48 | ); 49 | if co.syscall((), syscall, new_state).is_err() { 50 | crate::error!( 51 | "{} change to syscall {} {} failed !", 52 | co.name(), 53 | syscall, 54 | new_state 55 | ); 56 | } 57 | } 58 | //等待事件到来 59 | _ = EventLoops::wait_event(Some(time)); 60 | reset_errno(); 61 | if !rmtp.is_null() { 62 | unsafe { 63 | (*rmtp).tv_sec = 0; 64 | (*rmtp).tv_nsec = 0; 65 | } 66 | } 67 | 0 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /core/src/syscall/unix/poll.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use libc::{nfds_t, pollfd}; 3 | use std::ffi::c_int; 4 | use std::time::Duration; 5 | 6 | trait PollSyscall { 7 | extern "C" fn poll( 8 | &self, 9 | fn_ptr: Option<&extern "C" fn(*mut pollfd, nfds_t, c_int) -> c_int>, 10 | fds: *mut pollfd, 11 | nfds: nfds_t, 12 | timeout: c_int, 13 | ) -> c_int; 14 | } 15 | 16 | impl_syscall!(PollSyscallFacade, NioPollSyscall, RawPollSyscall, 17 | poll(fds: *mut pollfd, nfds: nfds_t, timeout: c_int) -> c_int 18 | ); 19 | 20 | impl_facade!(PollSyscallFacade, PollSyscall, 21 | poll(fds: *mut pollfd, nfds: nfds_t, timeout: c_int) -> c_int 22 | ); 23 | 24 | #[repr(C)] 25 | #[derive(Debug, Default)] 26 | struct NioPollSyscall { 27 | inner: I, 28 | } 29 | 30 | impl PollSyscall for NioPollSyscall { 31 | extern "C" fn poll( 32 | &self, 33 | fn_ptr: Option<&extern "C" fn(*mut pollfd, nfds_t, c_int) -> c_int>, 34 | fds: *mut pollfd, 35 | nfds: nfds_t, 36 | timeout: c_int, 37 | ) -> c_int { 38 | let mut t = if timeout < 0 { c_int::MAX } else { timeout }; 39 | let mut x = 1; 40 | let mut r; 41 | // just check poll every x ms 42 | loop { 43 | r = self.inner.poll(fn_ptr, fds, nfds, 0); 44 | if r != 0 || t == 0 { 45 | break; 46 | } 47 | _ = EventLoops::wait_event(Some(Duration::from_millis(t.min(x).try_into().expect("overflow")))); 48 | if t != c_int::MAX { 49 | t = if t > x { t - x } else { 0 }; 50 | } 51 | if x < 16 { 52 | x <<= 1; 53 | } 54 | } 55 | r 56 | } 57 | } 58 | 59 | impl_raw!(RawPollSyscall, PollSyscall, 60 | poll(fds: *mut pollfd, nfds: nfds_t, timeout: c_int) -> c_int 61 | ); 62 | -------------------------------------------------------------------------------- /core/src/syscall/unix/pread.rs: -------------------------------------------------------------------------------- 1 | use libc::{off_t, size_t, ssize_t}; 2 | use std::ffi::{c_int, c_void}; 3 | 4 | trait PreadSyscall { 5 | extern "C" fn pread( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *mut c_void, size_t, off_t) -> ssize_t>, 8 | fd: c_int, 9 | buf: *mut c_void, 10 | len: size_t, 11 | offset: off_t, 12 | ) -> ssize_t; 13 | } 14 | 15 | impl_syscall!(PreadSyscallFacade, IoUringPreadSyscall, NioPreadSyscall, RawPreadSyscall, 16 | pread(fd: c_int, buf: *mut c_void, len: size_t, offset: off_t) -> ssize_t 17 | ); 18 | 19 | impl_facade!(PreadSyscallFacade, PreadSyscall, 20 | pread(fd: c_int, buf: *mut c_void, len: size_t, offset: off_t) -> ssize_t 21 | ); 22 | 23 | impl_io_uring_read!(IoUringPreadSyscall, PreadSyscall, 24 | pread(fd: c_int, buf: *mut c_void, len: size_t, offset: off_t) -> ssize_t 25 | ); 26 | 27 | impl_nio_read_buf!(NioPreadSyscall, PreadSyscall, 28 | pread(fd: c_int, buf: *mut c_void, len: size_t, offset: off_t) -> ssize_t 29 | ); 30 | 31 | impl_raw!(RawPreadSyscall, PreadSyscall, 32 | pread(fd: c_int, buf: *mut c_void, len: size_t, offset: off_t) -> ssize_t 33 | ); 34 | -------------------------------------------------------------------------------- /core/src/syscall/unix/preadv.rs: -------------------------------------------------------------------------------- 1 | use libc::{iovec, off_t, ssize_t}; 2 | use std::ffi::c_int; 3 | 4 | trait PreadvSyscall { 5 | extern "C" fn preadv( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *const iovec, c_int, off_t) -> ssize_t>, 8 | fd: c_int, 9 | iov: *const iovec, 10 | iovcnt: c_int, 11 | offset: off_t, 12 | ) -> ssize_t; 13 | } 14 | 15 | impl_syscall!(PreadvSyscallFacade, IoUringPreadvSyscall, NioPreadvSyscall, RawPreadvSyscall, 16 | preadv(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t 17 | ); 18 | 19 | impl_facade!(PreadvSyscallFacade, PreadvSyscall, 20 | preadv(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t 21 | ); 22 | 23 | impl_io_uring_read!(IoUringPreadvSyscall, PreadvSyscall, 24 | preadv(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t 25 | ); 26 | 27 | impl_nio_read_iovec!(NioPreadvSyscall, PreadvSyscall, 28 | preadv(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t 29 | ); 30 | 31 | impl_raw!(RawPreadvSyscall, PreadvSyscall, 32 | preadv(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t 33 | ); 34 | -------------------------------------------------------------------------------- /core/src/syscall/unix/pthread_mutex_lock.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use crate::scheduler::SchedulableCoroutine; 3 | use libc::pthread_mutex_t; 4 | use std::ffi::c_int; 5 | 6 | trait PthreadMutexLockSyscall { 7 | extern "C" fn pthread_mutex_lock( 8 | &self, 9 | fn_ptr: Option<&extern "C" fn(*mut pthread_mutex_t) -> c_int>, 10 | lock: *mut pthread_mutex_t, 11 | ) -> c_int; 12 | } 13 | 14 | impl_syscall!(PthreadMutexLockSyscallFacade, NioPthreadMutexLockSyscall, RawPthreadMutexLockSyscall, 15 | pthread_mutex_lock(lock: *mut pthread_mutex_t) -> c_int 16 | ); 17 | 18 | impl_facade!(PthreadMutexLockSyscallFacade, PthreadMutexLockSyscall, 19 | pthread_mutex_lock(lock: *mut pthread_mutex_t) -> c_int 20 | ); 21 | 22 | #[repr(C)] 23 | #[derive(Debug, Copy, Clone, Default)] 24 | struct NioPthreadMutexLockSyscall { 25 | inner: I, 26 | } 27 | 28 | impl PthreadMutexLockSyscall for NioPthreadMutexLockSyscall { 29 | extern "C" fn pthread_mutex_lock( 30 | &self, 31 | fn_ptr: Option<&extern "C" fn(*mut pthread_mutex_t) -> c_int>, 32 | lock: *mut pthread_mutex_t, 33 | ) -> c_int { 34 | if SchedulableCoroutine::current().is_none() { 35 | return self.inner.pthread_mutex_lock(fn_ptr, lock); 36 | } 37 | loop { 38 | let r = unsafe { libc::pthread_mutex_trylock(lock) }; 39 | if 0 == r 40 | || r != libc::EBUSY 41 | || EventLoops::wait_event(Some(crate::common::constants::SLICE)).is_err() 42 | { 43 | return r; 44 | } 45 | } 46 | } 47 | } 48 | 49 | impl_raw!(RawPthreadMutexLockSyscall, PthreadMutexLockSyscall, 50 | pthread_mutex_lock(lock: *mut pthread_mutex_t) -> c_int 51 | ); 52 | -------------------------------------------------------------------------------- /core/src/syscall/unix/pthread_mutex_trylock.rs: -------------------------------------------------------------------------------- 1 | use libc::pthread_mutex_t; 2 | use std::ffi::c_int; 3 | 4 | trait PthreadMutexTrylockSyscall { 5 | extern "C" fn pthread_mutex_trylock( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(*mut pthread_mutex_t) -> c_int>, 8 | lock: *mut pthread_mutex_t, 9 | ) -> c_int; 10 | } 11 | 12 | impl_syscall!(PthreadMutexTrylockSyscallFacade, RawPthreadMutexTrylockSyscall, 13 | pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> c_int 14 | ); 15 | 16 | impl_facade!(PthreadMutexTrylockSyscallFacade, PthreadMutexTrylockSyscall, 17 | pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> c_int 18 | ); 19 | 20 | impl_raw!(RawPthreadMutexTrylockSyscall, PthreadMutexTrylockSyscall, 21 | pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> c_int 22 | ); 23 | -------------------------------------------------------------------------------- /core/src/syscall/unix/pthread_mutex_unlock.rs: -------------------------------------------------------------------------------- 1 | use libc::pthread_mutex_t; 2 | use std::ffi::c_int; 3 | 4 | trait PthreadMutexUnlockSyscall { 5 | extern "C" fn pthread_mutex_unlock( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(*mut pthread_mutex_t) -> c_int>, 8 | lock: *mut pthread_mutex_t, 9 | ) -> c_int; 10 | } 11 | 12 | impl_syscall!(PthreadMutexUnlockSyscallFacade, RawPthreadMutexUnlockSyscall, 13 | pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> c_int 14 | ); 15 | 16 | impl_facade!(PthreadMutexUnlockSyscallFacade, PthreadMutexUnlockSyscall, 17 | pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> c_int 18 | ); 19 | 20 | impl_raw!(RawPthreadMutexUnlockSyscall, PthreadMutexUnlockSyscall, 21 | pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> c_int 22 | ); 23 | -------------------------------------------------------------------------------- /core/src/syscall/unix/pwrite.rs: -------------------------------------------------------------------------------- 1 | use libc::{off_t, size_t, ssize_t}; 2 | use std::ffi::{c_int, c_void}; 3 | 4 | trait PwriteSyscall { 5 | extern "C" fn pwrite( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *const c_void, size_t, off_t) -> ssize_t>, 8 | fd: c_int, 9 | buf: *const c_void, 10 | count: size_t, 11 | offset: off_t, 12 | ) -> ssize_t; 13 | } 14 | 15 | impl_syscall!(PwriteSyscallFacade, IoUringPwriteSyscall, NioPwriteSyscall, RawPwriteSyscall, 16 | pwrite(fd: c_int, buf: *const c_void, len: size_t, offset: off_t) -> ssize_t 17 | ); 18 | 19 | impl_facade!(PwriteSyscallFacade, PwriteSyscall, 20 | pwrite(fd: c_int, buf: *const c_void, len: size_t, offset: off_t) -> ssize_t 21 | ); 22 | 23 | impl_io_uring_write!(IoUringPwriteSyscall, PwriteSyscall, 24 | pwrite(fd: c_int, buf: *const c_void, len: size_t, offset: off_t) -> ssize_t 25 | ); 26 | 27 | impl_nio_write_buf!(NioPwriteSyscall, PwriteSyscall, 28 | pwrite(fd: c_int, buf: *const c_void, len: size_t, offset: off_t) -> ssize_t 29 | ); 30 | 31 | impl_raw!(RawPwriteSyscall, PwriteSyscall, 32 | pwrite(fd: c_int, buf: *const c_void, len: size_t, offset: off_t) -> ssize_t 33 | ); 34 | -------------------------------------------------------------------------------- /core/src/syscall/unix/pwritev.rs: -------------------------------------------------------------------------------- 1 | use libc::{iovec, off_t, ssize_t}; 2 | use std::ffi::c_int; 3 | 4 | trait PwritevSyscall { 5 | extern "C" fn pwritev( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *const iovec, c_int, off_t) -> ssize_t>, 8 | fd: c_int, 9 | iov: *const iovec, 10 | iovcnt: c_int, 11 | offset: off_t, 12 | ) -> ssize_t; 13 | } 14 | 15 | impl_syscall!(PwritevSyscallFacade, IoUringPwritevSyscall, NioPwritevSyscall, RawPwritevSyscall, 16 | pwritev(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t 17 | ); 18 | 19 | impl_facade!(PwritevSyscallFacade, PwritevSyscall, 20 | pwritev(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t 21 | ); 22 | 23 | impl_io_uring_write!(IoUringPwritevSyscall, PwritevSyscall, 24 | pwritev(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t 25 | ); 26 | 27 | impl_nio_write_iovec!(NioPwritevSyscall, PwritevSyscall, 28 | pwritev(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t 29 | ); 30 | 31 | impl_raw!(RawPwritevSyscall, PwritevSyscall, 32 | pwritev(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t 33 | ); 34 | -------------------------------------------------------------------------------- /core/src/syscall/unix/read.rs: -------------------------------------------------------------------------------- 1 | use libc::{size_t, ssize_t}; 2 | use std::ffi::{c_int, c_void}; 3 | 4 | trait ReadSyscall { 5 | extern "C" fn read( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *mut c_void, size_t) -> ssize_t>, 8 | fd: c_int, 9 | buf: *mut c_void, 10 | len: size_t, 11 | ) -> ssize_t; 12 | } 13 | 14 | impl_syscall!(ReadSyscallFacade, IoUringReadSyscall, NioReadSyscall, RawReadSyscall, 15 | read(fd: c_int, buf: *mut c_void, len: size_t) -> ssize_t 16 | ); 17 | 18 | impl_facade!(ReadSyscallFacade, ReadSyscall, 19 | read(fd: c_int, buf: *mut c_void, len: size_t) -> ssize_t 20 | ); 21 | 22 | impl_io_uring_read!(IoUringReadSyscall, ReadSyscall, 23 | read(fd: c_int, buf: *mut c_void, len: size_t) -> ssize_t 24 | ); 25 | 26 | impl_nio_read_buf!(NioReadSyscall, ReadSyscall, 27 | read(fd: c_int, buf: *mut c_void, len: size_t) -> ssize_t 28 | ); 29 | 30 | impl_raw!(RawReadSyscall, ReadSyscall, 31 | read(fd: c_int, buf: *mut c_void, len: size_t) -> ssize_t 32 | ); 33 | -------------------------------------------------------------------------------- /core/src/syscall/unix/readv.rs: -------------------------------------------------------------------------------- 1 | use libc::{iovec, ssize_t}; 2 | use std::ffi::c_int; 3 | 4 | trait ReadvSyscall { 5 | extern "C" fn readv( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *const iovec, c_int) -> ssize_t>, 8 | fd: c_int, 9 | iov: *const iovec, 10 | iovcnt: c_int, 11 | ) -> ssize_t; 12 | } 13 | 14 | impl_syscall!(ReadvSyscallFacade, IoUringReadvSyscall, NioReadvSyscall, RawReadvSyscall, 15 | readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t 16 | ); 17 | 18 | impl_facade!(ReadvSyscallFacade, ReadvSyscall, 19 | readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t 20 | ); 21 | 22 | impl_io_uring_read!(IoUringReadvSyscall, ReadvSyscall, 23 | readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t 24 | ); 25 | 26 | impl_nio_read_iovec!(NioReadvSyscall, ReadvSyscall, 27 | readv(fd: c_int, iov: *const iovec, iovcnt: c_int,) -> ssize_t 28 | ); 29 | 30 | impl_raw!(RawReadvSyscall, ReadvSyscall, 31 | readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t 32 | ); 33 | -------------------------------------------------------------------------------- /core/src/syscall/unix/recv.rs: -------------------------------------------------------------------------------- 1 | use libc::{size_t, ssize_t}; 2 | use std::ffi::{c_int, c_void}; 3 | 4 | trait RecvSyscall { 5 | extern "C" fn recv( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *mut c_void, size_t, c_int) -> ssize_t>, 8 | fd: c_int, 9 | buf: *mut c_void, 10 | len: size_t, 11 | flags: c_int, 12 | ) -> ssize_t; 13 | } 14 | 15 | impl_syscall!(RecvSyscallFacade, IoUringRecvSyscall, NioRecvSyscall, RawRecvSyscall, 16 | recv(fd: c_int, buf: *mut c_void, len: size_t, flags: c_int) -> ssize_t 17 | ); 18 | 19 | impl_facade!(RecvSyscallFacade, RecvSyscall, 20 | recv(fd: c_int, buf: *mut c_void, len: size_t, flags: c_int) -> ssize_t 21 | ); 22 | 23 | impl_io_uring_read!(IoUringRecvSyscall, RecvSyscall, 24 | recv(fd: c_int, buf: *mut c_void, len: size_t, flags: c_int) -> ssize_t 25 | ); 26 | 27 | impl_nio_read_buf!(NioRecvSyscall, RecvSyscall, 28 | recv(fd: c_int, buf: *mut c_void, len: size_t, flags: c_int) -> ssize_t 29 | ); 30 | 31 | impl_raw!(RawRecvSyscall, RecvSyscall, 32 | recv(fd: c_int, buf: *mut c_void, len: size_t, flags: c_int) -> ssize_t 33 | ); 34 | -------------------------------------------------------------------------------- /core/src/syscall/unix/recvfrom.rs: -------------------------------------------------------------------------------- 1 | use libc::{size_t, sockaddr, socklen_t, ssize_t}; 2 | use std::ffi::{c_int, c_void}; 3 | 4 | trait RecvfromSyscall { 5 | extern "C" fn recvfrom( 6 | &self, 7 | fn_ptr: Option< 8 | &extern "C" fn( 9 | c_int, 10 | *mut c_void, 11 | size_t, 12 | c_int, 13 | *mut sockaddr, 14 | *mut socklen_t, 15 | ) -> ssize_t, 16 | >, 17 | fd: c_int, 18 | buf: *mut c_void, 19 | len: size_t, 20 | flags: c_int, 21 | addr: *mut sockaddr, 22 | addrlen: *mut socklen_t, 23 | ) -> ssize_t; 24 | } 25 | 26 | impl_syscall!(RecvfromSyscallFacade, NioRecvfromSyscall, RawRecvfromSyscall, 27 | recvfrom( 28 | fd: c_int, 29 | buf: *mut c_void, 30 | len: size_t, 31 | flags: c_int, 32 | addr: *mut sockaddr, 33 | addrlen: *mut socklen_t 34 | ) -> ssize_t 35 | ); 36 | 37 | impl_facade!(RecvfromSyscallFacade, RecvfromSyscall, 38 | recvfrom( 39 | fd: c_int, 40 | buf: *mut c_void, 41 | len: size_t, 42 | flags: c_int, 43 | addr: *mut sockaddr, 44 | addrlen: *mut socklen_t 45 | ) -> ssize_t 46 | ); 47 | 48 | impl_nio_read_buf!(NioRecvfromSyscall, RecvfromSyscall, 49 | recvfrom( 50 | fd: c_int, 51 | buf: *mut c_void, 52 | len: size_t, 53 | flags: c_int, 54 | addr: *mut sockaddr, 55 | addrlen: *mut socklen_t 56 | ) -> ssize_t 57 | ); 58 | 59 | impl_raw!(RawRecvfromSyscall, RecvfromSyscall, 60 | recvfrom( 61 | fd: c_int, 62 | buf: *mut c_void, 63 | len: size_t, 64 | flags: c_int, 65 | addr: *mut sockaddr, 66 | addrlen: *mut socklen_t 67 | ) -> ssize_t 68 | ); 69 | -------------------------------------------------------------------------------- /core/src/syscall/unix/renameat.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_char, c_int}; 2 | 3 | trait RenameatSyscall { 4 | extern "C" fn renameat( 5 | &self, 6 | fn_ptr: Option<&extern "C" fn(c_int, *const c_char, c_int, *const c_char) -> c_int>, 7 | olddirfd: c_int, 8 | oldpath: *const c_char, 9 | newdirfd: c_int, 10 | newpath: *const c_char, 11 | ) -> c_int; 12 | } 13 | 14 | impl_syscall2!(RenameatSyscallFacade, IoUringRenameatSyscall, RawRenameatSyscall, 15 | renameat(olddirfd: c_int, oldpath: *const c_char, newdirfd: c_int, newpath: *const c_char) -> c_int 16 | ); 17 | 18 | impl_facade!(RenameatSyscallFacade, RenameatSyscall, 19 | renameat(olddirfd: c_int, oldpath: *const c_char, newdirfd: c_int, newpath: *const c_char) -> c_int 20 | ); 21 | 22 | impl_io_uring!(IoUringRenameatSyscall, RenameatSyscall, 23 | renameat(olddirfd: c_int, oldpath: *const c_char, newdirfd: c_int, newpath: *const c_char) -> c_int 24 | ); 25 | 26 | impl_raw!(RawRenameatSyscall, RenameatSyscall, 27 | renameat(olddirfd: c_int, oldpath: *const c_char, newdirfd: c_int, newpath: *const c_char) -> c_int 28 | ); 29 | -------------------------------------------------------------------------------- /core/src/syscall/unix/renameat2.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_char, c_int, c_uint}; 2 | 3 | trait Renameat2Syscall { 4 | extern "C" fn renameat2( 5 | &self, 6 | fn_ptr: Option<&extern "C" fn(c_int, *const c_char, c_int, *const c_char, c_uint) -> c_int>, 7 | olddirfd: c_int, 8 | oldpath: *const c_char, 9 | newdirfd: c_int, 10 | newpath: *const c_char, 11 | flags: c_uint, 12 | ) -> c_int; 13 | } 14 | 15 | impl_syscall2!(Renameat2SyscallFacade, IoUringRenameat2Syscall, RawRenameat2Syscall, 16 | renameat2( 17 | olddirfd: c_int, 18 | oldpath: *const c_char, 19 | newdirfd: c_int, 20 | newpath: *const c_char, 21 | flags: c_uint, 22 | ) -> c_int 23 | ); 24 | 25 | impl_facade!(Renameat2SyscallFacade, Renameat2Syscall, 26 | renameat2( 27 | olddirfd: c_int, 28 | oldpath: *const c_char, 29 | newdirfd: c_int, 30 | newpath: *const c_char, 31 | flags: c_uint, 32 | ) -> c_int 33 | ); 34 | 35 | impl_io_uring!(IoUringRenameat2Syscall, Renameat2Syscall, 36 | renameat2( 37 | olddirfd: c_int, 38 | oldpath: *const c_char, 39 | newdirfd: c_int, 40 | newpath: *const c_char, 41 | flags: c_uint, 42 | ) -> c_int 43 | ); 44 | 45 | impl_raw!(RawRenameat2Syscall, Renameat2Syscall, 46 | renameat2( 47 | olddirfd: c_int, 48 | oldpath: *const c_char, 49 | newdirfd: c_int, 50 | newpath: *const c_char, 51 | flags: c_uint, 52 | ) -> c_int 53 | ); 54 | -------------------------------------------------------------------------------- /core/src/syscall/unix/rmdir.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_char, c_int}; 2 | 3 | trait RmdirSyscall { 4 | extern "C" fn rmdir( 5 | &self, 6 | fn_ptr: Option<&extern "C" fn(*const c_char) -> c_int>, 7 | path: *const c_char, 8 | ) -> c_int; 9 | } 10 | 11 | impl_syscall!(RmdirSyscallFacade, RawRmdirSyscall, 12 | rmdir(path: *const c_char) -> c_int 13 | ); 14 | 15 | impl_facade!(RmdirSyscallFacade, RmdirSyscall, 16 | rmdir(path: *const c_char) -> c_int 17 | ); 18 | 19 | impl_raw!(RawRmdirSyscall, RmdirSyscall, 20 | rmdir(path: *const c_char) -> c_int 21 | ); 22 | -------------------------------------------------------------------------------- /core/src/syscall/unix/select.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use libc::{fd_set, timeval}; 3 | use std::ffi::{c_int, c_uint}; 4 | use std::time::Duration; 5 | 6 | trait SelectSyscall { 7 | extern "C" fn select( 8 | &self, 9 | fn_ptr: Option< 10 | &extern "C" fn(c_int, *mut fd_set, *mut fd_set, *mut fd_set, *mut timeval) -> c_int, 11 | >, 12 | nfds: c_int, 13 | readfds: *mut fd_set, 14 | writefds: *mut fd_set, 15 | errorfds: *mut fd_set, 16 | timeout: *mut timeval, 17 | ) -> c_int; 18 | } 19 | 20 | impl_syscall!(SelectSyscallFacade, NioSelectSyscall, RawSelectSyscall, 21 | select( 22 | nfds: c_int, 23 | readfds: *mut fd_set, 24 | writefds: *mut fd_set, 25 | errorfds: *mut fd_set, 26 | timeout: *mut timeval 27 | ) -> c_int 28 | ); 29 | 30 | impl_facade!(SelectSyscallFacade, SelectSyscall, 31 | select( 32 | nfds: c_int, 33 | readfds: *mut fd_set, 34 | writefds: *mut fd_set, 35 | errorfds: *mut fd_set, 36 | timeout: *mut timeval 37 | ) -> c_int 38 | ); 39 | 40 | #[repr(C)] 41 | #[derive(Debug, Default)] 42 | struct NioSelectSyscall { 43 | inner: I, 44 | } 45 | 46 | impl SelectSyscall for NioSelectSyscall { 47 | extern "C" fn select( 48 | &self, 49 | fn_ptr: Option< 50 | &extern "C" fn(c_int, *mut fd_set, *mut fd_set, *mut fd_set, *mut timeval) -> c_int, 51 | >, 52 | nfds: c_int, 53 | readfds: *mut fd_set, 54 | writefds: *mut fd_set, 55 | errorfds: *mut fd_set, 56 | timeout: *mut timeval, 57 | ) -> c_int { 58 | let mut t = if timeout.is_null() { 59 | c_uint::MAX 60 | } else { 61 | unsafe { 62 | c_uint::try_from((*timeout).tv_sec).expect("overflow") 63 | .saturating_mul(1_000_000) 64 | .saturating_add(c_uint::try_from((*timeout).tv_usec).expect("overflow")) 65 | } 66 | }; 67 | let mut o = timeval { 68 | tv_sec: 0, 69 | tv_usec: 0, 70 | }; 71 | let mut s: [fd_set; 3] = unsafe { std::mem::zeroed() }; 72 | if !readfds.is_null() { 73 | s[0] = unsafe { *readfds }; 74 | } 75 | if !writefds.is_null() { 76 | s[1] = unsafe { *writefds }; 77 | } 78 | if !errorfds.is_null() { 79 | s[2] = unsafe { *errorfds }; 80 | } 81 | let mut x = 1; 82 | let mut r; 83 | // just check select every x ms 84 | loop { 85 | r = self 86 | .inner 87 | .select(fn_ptr, nfds, readfds, writefds, errorfds, &raw mut o); 88 | if r != 0 || t == 0 { 89 | break; 90 | } 91 | _ = EventLoops::wait_event(Some(Duration::from_millis(u64::from(t.min(x))))); 92 | if t != c_uint::MAX { 93 | t = t.saturating_sub(x); 94 | } 95 | if x < 16 { 96 | x <<= 1; 97 | } 98 | 99 | if !readfds.is_null() { 100 | unsafe { *readfds = s[0] }; 101 | } 102 | if !writefds.is_null() { 103 | unsafe { *writefds = s[1] }; 104 | } 105 | if !errorfds.is_null() { 106 | unsafe { *errorfds = s[2] }; 107 | } 108 | o.tv_sec = 0; 109 | o.tv_usec = 0; 110 | } 111 | r 112 | } 113 | } 114 | 115 | impl_raw!(RawSelectSyscall, SelectSyscall, 116 | select( 117 | nfds: c_int, 118 | readfds: *mut fd_set, 119 | writefds: *mut fd_set, 120 | errorfds: *mut fd_set, 121 | timeout: *mut timeval 122 | ) -> c_int 123 | ); 124 | -------------------------------------------------------------------------------- /core/src/syscall/unix/send.rs: -------------------------------------------------------------------------------- 1 | use libc::{size_t, ssize_t}; 2 | use std::ffi::{c_int, c_void}; 3 | 4 | trait SendSyscall { 5 | extern "C" fn send( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *const c_void, size_t, c_int) -> ssize_t>, 8 | fd: c_int, 9 | buf: *const c_void, 10 | len: size_t, 11 | flags: c_int, 12 | ) -> ssize_t; 13 | } 14 | 15 | impl_syscall!(SendSyscallFacade, IoUringSendSyscall, NioSendSyscall, RawSendSyscall, 16 | send(fd: c_int, buf: *const c_void, len: size_t, flags: c_int) -> ssize_t 17 | ); 18 | 19 | impl_facade!(SendSyscallFacade, SendSyscall, 20 | send(fd: c_int, buf: *const c_void, len: size_t, flags: c_int) -> ssize_t 21 | ); 22 | 23 | impl_io_uring_write!(IoUringSendSyscall, SendSyscall, 24 | send(fd: c_int, buf: *const c_void, len: size_t, flags: c_int) -> ssize_t 25 | ); 26 | 27 | impl_nio_write_buf!(NioSendSyscall, SendSyscall, 28 | send(fd: c_int, buf: *const c_void, len: size_t, flags: c_int) -> ssize_t 29 | ); 30 | 31 | impl_raw!(RawSendSyscall, SendSyscall, 32 | send(fd: c_int, buf: *const c_void, len: size_t, flags: c_int) -> ssize_t 33 | ); 34 | -------------------------------------------------------------------------------- /core/src/syscall/unix/sendto.rs: -------------------------------------------------------------------------------- 1 | use libc::{size_t, sockaddr, socklen_t, ssize_t}; 2 | use std::ffi::{c_int, c_void}; 3 | 4 | trait SendtoSyscall { 5 | extern "C" fn sendto( 6 | &self, 7 | fn_ptr: Option< 8 | &extern "C" fn( 9 | c_int, 10 | *const c_void, 11 | size_t, 12 | c_int, 13 | *const sockaddr, 14 | socklen_t, 15 | ) -> ssize_t, 16 | >, 17 | fd: c_int, 18 | buf: *const c_void, 19 | len: size_t, 20 | flags: c_int, 21 | addr: *const sockaddr, 22 | addrlen: socklen_t, 23 | ) -> ssize_t; 24 | } 25 | 26 | impl_syscall!(SendtoSyscallFacade, IoUringSendtoSyscall, NioSendtoSyscall, RawSendtoSyscall, 27 | sendto( 28 | fd: c_int, 29 | buf: *const c_void, 30 | len: size_t, 31 | flags: c_int, 32 | addr: *const sockaddr, 33 | addrlen: socklen_t 34 | ) -> ssize_t 35 | ); 36 | 37 | impl_facade!(SendtoSyscallFacade, SendtoSyscall, 38 | sendto( 39 | fd: c_int, 40 | buf: *const c_void, 41 | len: size_t, 42 | flags: c_int, 43 | addr: *const sockaddr, 44 | addrlen: socklen_t 45 | ) -> ssize_t 46 | ); 47 | 48 | impl_io_uring_write!(IoUringSendtoSyscall, SendtoSyscall, 49 | sendto( 50 | fd: c_int, 51 | buf: *const c_void, 52 | len: size_t, 53 | flags: c_int, 54 | addr: *const sockaddr, 55 | addrlen: socklen_t 56 | ) -> ssize_t 57 | ); 58 | 59 | impl_nio_write_buf!(NioSendtoSyscall, SendtoSyscall, 60 | sendto( 61 | fd: c_int, 62 | buf: *const c_void, 63 | len: size_t, 64 | flags: c_int, 65 | addr: *const sockaddr, 66 | addrlen: socklen_t 67 | ) -> ssize_t 68 | ); 69 | 70 | impl_raw!(RawSendtoSyscall, SendtoSyscall, 71 | sendto( 72 | fd: c_int, 73 | buf: *const c_void, 74 | len: size_t, 75 | flags: c_int, 76 | addr: *const sockaddr, 77 | addrlen: socklen_t 78 | ) -> ssize_t 79 | ); 80 | -------------------------------------------------------------------------------- /core/src/syscall/unix/setsockopt.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_int, c_void}; 2 | use libc::{socklen_t, timeval}; 3 | use crate::syscall::get_time_limit; 4 | use crate::syscall::unix::{RECV_TIME_LIMIT, SEND_TIME_LIMIT}; 5 | 6 | trait SetsockoptSyscall { 7 | extern "C" fn setsockopt( 8 | &self, 9 | fn_ptr: Option<&extern "C" fn(c_int, c_int, c_int, *const c_void, socklen_t) -> c_int>, 10 | socket: c_int, 11 | level: c_int, 12 | name: c_int, 13 | value: *const c_void, 14 | option_len: socklen_t 15 | ) -> c_int; 16 | } 17 | 18 | impl_syscall!(SetsockoptSyscallFacade, NioSetsockoptSyscall, RawSetsockoptSyscall, 19 | setsockopt(socket: c_int, level: c_int, name: c_int, value: *const c_void, option_len: socklen_t) -> c_int 20 | ); 21 | 22 | impl_facade!(SetsockoptSyscallFacade, SetsockoptSyscall, 23 | setsockopt(socket: c_int, level: c_int, name: c_int, value: *const c_void, option_len: socklen_t) -> c_int 24 | ); 25 | 26 | #[repr(C)] 27 | #[derive(Debug, Default)] 28 | struct NioSetsockoptSyscall { 29 | inner: I, 30 | } 31 | 32 | impl SetsockoptSyscall for NioSetsockoptSyscall { 33 | extern "C" fn setsockopt( 34 | &self, 35 | fn_ptr: Option<&extern "C" fn(c_int, c_int, c_int, *const c_void, socklen_t) -> c_int>, 36 | socket: c_int, 37 | level: c_int, 38 | name: c_int, 39 | value: *const c_void, 40 | option_len: socklen_t 41 | ) -> c_int { 42 | let r= self.inner.setsockopt(fn_ptr, socket, level, name, value, option_len); 43 | if 0 == r && libc::SOL_SOCKET == level { 44 | if libc::SO_SNDTIMEO == name { 45 | assert!(SEND_TIME_LIMIT.insert(socket, get_time_limit(unsafe { &*value.cast::() })).is_none()); 46 | } else if libc::SO_RCVTIMEO == name { 47 | assert!(RECV_TIME_LIMIT.insert(socket, get_time_limit(unsafe { &*value.cast::() })).is_none()); 48 | } 49 | } 50 | r 51 | } 52 | } 53 | 54 | impl_raw!(RawSetsockoptSyscall, SetsockoptSyscall, 55 | setsockopt(socket: c_int, level: c_int, name: c_int, value: *const c_void, option_len: socklen_t) -> c_int 56 | ); 57 | -------------------------------------------------------------------------------- /core/src/syscall/unix/shutdown.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use crate::syscall::set_errno; 3 | use std::ffi::c_int; 4 | 5 | trait ShutdownSyscall { 6 | extern "C" fn shutdown( 7 | &self, 8 | fn_ptr: Option<&extern "C" fn(c_int, c_int) -> c_int>, 9 | fd: c_int, 10 | how: c_int, 11 | ) -> c_int; 12 | } 13 | 14 | impl_syscall!(ShutdownSyscallFacade, IoUringShutdownSyscall, NioShutdownSyscall, RawShutdownSyscall, 15 | shutdown(fd: c_int, how: c_int) -> c_int 16 | ); 17 | 18 | impl_facade!(ShutdownSyscallFacade, ShutdownSyscall, shutdown(fd: c_int, how: c_int) -> c_int); 19 | 20 | impl_io_uring!(IoUringShutdownSyscall, ShutdownSyscall, shutdown(fd: c_int, how: c_int) -> c_int); 21 | 22 | #[repr(C)] 23 | #[derive(Debug, Default)] 24 | struct NioShutdownSyscall { 25 | inner: I, 26 | } 27 | 28 | impl ShutdownSyscall for NioShutdownSyscall { 29 | extern "C" fn shutdown( 30 | &self, 31 | fn_ptr: Option<&extern "C" fn(c_int, c_int) -> c_int>, 32 | fd: c_int, 33 | how: c_int, 34 | ) -> c_int { 35 | _ = match how { 36 | libc::SHUT_RD => EventLoops::del_read_event(fd), 37 | libc::SHUT_WR => EventLoops::del_write_event(fd), 38 | libc::SHUT_RDWR => EventLoops::del_event(fd), 39 | _ => { 40 | set_errno(libc::EINVAL); 41 | return -1; 42 | } 43 | }; 44 | self.inner.shutdown(fn_ptr, fd, how) 45 | } 46 | } 47 | 48 | impl_raw!(RawShutdownSyscall, ShutdownSyscall, shutdown(fd: c_int, how: c_int) -> c_int); 49 | -------------------------------------------------------------------------------- /core/src/syscall/unix/sleep.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use crate::syscall::reset_errno; 3 | use std::ffi::c_uint; 4 | use std::time::Duration; 5 | 6 | trait SleepSyscall { 7 | extern "C" fn sleep( 8 | &self, 9 | fn_ptr: Option<&extern "C" fn(c_uint) -> c_uint>, 10 | secs: c_uint, 11 | ) -> c_uint; 12 | } 13 | 14 | impl_syscall!(SleepSyscallFacade, NioSleepSyscall, sleep(secs: c_uint) -> c_uint); 15 | 16 | impl_facade!(SleepSyscallFacade, SleepSyscall, sleep(secs: c_uint) -> c_uint); 17 | 18 | #[repr(C)] 19 | #[derive(Debug, Copy, Clone, Default)] 20 | struct NioSleepSyscall {} 21 | 22 | impl SleepSyscall for NioSleepSyscall { 23 | extern "C" fn sleep( 24 | &self, 25 | _: Option<&extern "C" fn(c_uint) -> c_uint>, 26 | secs: c_uint, 27 | ) -> c_uint { 28 | let time = Duration::from_secs(u64::from(secs)); 29 | if let Some(co) = crate::scheduler::SchedulableCoroutine::current() { 30 | let syscall = crate::common::constants::SyscallName::sleep; 31 | let new_state = crate::common::constants::SyscallState::Suspend( 32 | crate::common::get_timeout_time(time), 33 | ); 34 | if co.syscall((), syscall, new_state).is_err() { 35 | crate::error!( 36 | "{} change to syscall {} {} failed !", 37 | co.name(), 38 | syscall, 39 | new_state 40 | ); 41 | } 42 | } 43 | _ = EventLoops::wait_event(Some(time)); 44 | reset_errno(); 45 | 0 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/syscall/unix/socket.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | 3 | trait SocketSyscall { 4 | extern "C" fn socket( 5 | &self, 6 | fn_ptr: Option<&extern "C" fn(c_int, c_int, c_int) -> c_int>, 7 | domain: c_int, 8 | ty: c_int, 9 | protocol: c_int, 10 | ) -> c_int; 11 | } 12 | 13 | impl_syscall2!(SocketSyscallFacade, IoUringSocketSyscall, RawSocketSyscall, 14 | socket(domain: c_int, ty: c_int, protocol: c_int) -> c_int 15 | ); 16 | 17 | impl_facade!(SocketSyscallFacade, SocketSyscall, 18 | socket(domain: c_int, ty: c_int, protocol: c_int) -> c_int 19 | ); 20 | 21 | impl_io_uring!(IoUringSocketSyscall, SocketSyscall, 22 | socket(domain: c_int, ty: c_int, protocol: c_int) -> c_int 23 | ); 24 | 25 | impl_raw!(RawSocketSyscall, SocketSyscall, 26 | socket(domain: c_int, ty: c_int, protocol: c_int) -> c_int 27 | ); 28 | -------------------------------------------------------------------------------- /core/src/syscall/unix/unlink.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_char, c_int}; 2 | 3 | trait LinkSyscall { 4 | extern "C" fn unlink( 5 | &self, 6 | fn_ptr: Option<&extern "C" fn(*const c_char) -> c_int>, 7 | src: *const c_char 8 | ) -> c_int; 9 | } 10 | 11 | impl_syscall!(UnlinkSyscallFacade, RawUnlinkSyscall, 12 | unlink(src: *const c_char) -> c_int 13 | ); 14 | 15 | impl_facade!(UnlinkSyscallFacade, LinkSyscall, 16 | unlink(src: *const c_char) -> c_int 17 | ); 18 | 19 | impl_raw!(RawUnlinkSyscall, LinkSyscall, 20 | unlink(src: *const c_char) -> c_int 21 | ); 22 | -------------------------------------------------------------------------------- /core/src/syscall/unix/usleep.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use crate::syscall::reset_errno; 3 | use std::ffi::{c_int, c_uint}; 4 | use std::time::Duration; 5 | 6 | trait UsleepSyscall { 7 | extern "C" fn usleep( 8 | &self, 9 | fn_ptr: Option<&extern "C" fn(c_uint) -> c_int>, 10 | microseconds: c_uint, 11 | ) -> c_int; 12 | } 13 | 14 | impl_syscall!(UsleepSyscallFacade, NioUsleepSyscall, 15 | usleep(microseconds: c_uint) -> c_int 16 | ); 17 | 18 | impl_facade!(UsleepSyscallFacade, UsleepSyscall, 19 | usleep(microseconds: c_uint) -> c_int 20 | ); 21 | 22 | #[repr(C)] 23 | #[derive(Debug, Copy, Clone, Default)] 24 | struct NioUsleepSyscall {} 25 | 26 | impl UsleepSyscall for NioUsleepSyscall { 27 | extern "C" fn usleep( 28 | &self, 29 | _: Option<&extern "C" fn(c_uint) -> c_int>, 30 | microseconds: c_uint, 31 | ) -> c_int { 32 | let time = match u64::from(microseconds).checked_mul(1_000) { 33 | Some(v) => Duration::from_nanos(v), 34 | None => Duration::MAX, 35 | }; 36 | if let Some(co) = crate::scheduler::SchedulableCoroutine::current() { 37 | let syscall = crate::common::constants::SyscallName::usleep; 38 | let new_state = crate::common::constants::SyscallState::Suspend( 39 | crate::common::get_timeout_time(time), 40 | ); 41 | if co.syscall((), syscall, new_state).is_err() { 42 | crate::error!( 43 | "{} change to syscall {} {} failed !", 44 | co.name(), 45 | syscall, 46 | new_state 47 | ); 48 | } 49 | } 50 | _ = EventLoops::wait_event(Some(time)); 51 | reset_errno(); 52 | 0 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/src/syscall/unix/write.rs: -------------------------------------------------------------------------------- 1 | use libc::{size_t, ssize_t}; 2 | use std::ffi::{c_int, c_void}; 3 | 4 | trait WriteSyscall { 5 | extern "C" fn write( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *const c_void, size_t) -> ssize_t>, 8 | fd: c_int, 9 | buf: *const c_void, 10 | len: size_t, 11 | ) -> ssize_t; 12 | } 13 | 14 | impl_syscall!(WriteSyscallFacade, IoUringWriteSyscall, NioWriteSyscall, RawWriteSyscall, 15 | write(fd: c_int, buf: *const c_void, len: size_t) -> ssize_t 16 | ); 17 | 18 | impl_facade!(WriteSyscallFacade, WriteSyscall, 19 | write(fd: c_int, buf: *const c_void, len: size_t) -> ssize_t 20 | ); 21 | 22 | impl_io_uring_write!(IoUringWriteSyscall, WriteSyscall, 23 | write(fd: c_int, buf: *const c_void, len: size_t) -> ssize_t 24 | ); 25 | 26 | impl_nio_write_buf!(NioWriteSyscall, WriteSyscall, 27 | write(fd: c_int, buf: *const c_void, len: size_t) -> ssize_t 28 | ); 29 | 30 | impl_raw!(RawWriteSyscall, WriteSyscall, 31 | write(fd: c_int, buf: *const c_void, len: size_t) -> ssize_t 32 | ); 33 | -------------------------------------------------------------------------------- /core/src/syscall/unix/writev.rs: -------------------------------------------------------------------------------- 1 | use libc::{iovec, ssize_t}; 2 | use std::ffi::c_int; 3 | 4 | trait WritevSyscall { 5 | extern "C" fn writev( 6 | &self, 7 | fn_ptr: Option<&extern "C" fn(c_int, *const iovec, c_int) -> ssize_t>, 8 | fd: c_int, 9 | iov: *const iovec, 10 | iovcnt: c_int, 11 | ) -> ssize_t; 12 | } 13 | 14 | impl_syscall!(WritevSyscallFacade, IoUringWritevSyscall, NioWritevSyscall, RawWritevSyscall, 15 | writev(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t 16 | ); 17 | 18 | impl_facade!(WritevSyscallFacade, WritevSyscall, 19 | writev(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t 20 | ); 21 | 22 | impl_io_uring_write!(IoUringWritevSyscall, WritevSyscall, 23 | writev(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t 24 | ); 25 | 26 | impl_nio_write_iovec!(NioWritevSyscall, WritevSyscall, 27 | writev(fd: c_int, iov: *const iovec, iovcnt: c_int,) -> ssize_t 28 | ); 29 | 30 | impl_raw!(RawWritevSyscall, WritevSyscall, 31 | writev(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t 32 | ); 33 | -------------------------------------------------------------------------------- /core/src/syscall/windows/CreateFileW.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_uint; 2 | use windows_sys::core::PCWSTR; 3 | use windows_sys::Win32::Foundation::HANDLE; 4 | use windows_sys::Win32::Security::SECURITY_ATTRIBUTES; 5 | use windows_sys::Win32::Storage::FileSystem::{ 6 | FILE_CREATION_DISPOSITION, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, 7 | }; 8 | 9 | trait CreateFileWSyscall { 10 | extern "system" fn CreateFileW( 11 | &self, 12 | fn_ptr: Option< 13 | &extern "system" fn( 14 | PCWSTR, 15 | c_uint, 16 | FILE_SHARE_MODE, 17 | *const SECURITY_ATTRIBUTES, 18 | FILE_CREATION_DISPOSITION, 19 | FILE_FLAGS_AND_ATTRIBUTES, 20 | HANDLE, 21 | ) -> HANDLE, 22 | >, 23 | lpfilename: PCWSTR, 24 | dwdesiredaccess: c_uint, 25 | dwsharemode: FILE_SHARE_MODE, 26 | lpsecurityattributes: *const SECURITY_ATTRIBUTES, 27 | dwcreationdisposition: FILE_CREATION_DISPOSITION, 28 | dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, 29 | htemplatefile: HANDLE, 30 | ) -> HANDLE; 31 | } 32 | 33 | impl_syscall!(CreateFileWSyscallFacade, RawCreateFileWSyscall, 34 | CreateFileW( 35 | lpfilename: PCWSTR, 36 | dwdesiredaccess: c_uint, 37 | dwsharemode: FILE_SHARE_MODE, 38 | lpsecurityattributes: *const SECURITY_ATTRIBUTES, 39 | dwcreationdisposition: FILE_CREATION_DISPOSITION, 40 | dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, 41 | htemplatefile: HANDLE 42 | ) -> HANDLE 43 | ); 44 | 45 | impl_facade!(CreateFileWSyscallFacade, CreateFileWSyscall, 46 | CreateFileW( 47 | lpfilename: PCWSTR, 48 | dwdesiredaccess: c_uint, 49 | dwsharemode: FILE_SHARE_MODE, 50 | lpsecurityattributes: *const SECURITY_ATTRIBUTES, 51 | dwcreationdisposition: FILE_CREATION_DISPOSITION, 52 | dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, 53 | htemplatefile: HANDLE 54 | ) -> HANDLE 55 | ); 56 | 57 | impl_raw!(RawCreateFileWSyscall, CreateFileWSyscall, windows_sys::Win32::Storage::FileSystem, 58 | CreateFileW( 59 | lpfilename: PCWSTR, 60 | dwdesiredaccess: c_uint, 61 | dwsharemode: FILE_SHARE_MODE, 62 | lpsecurityattributes: *const SECURITY_ATTRIBUTES, 63 | dwcreationdisposition: FILE_CREATION_DISPOSITION, 64 | dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, 65 | htemplatefile: HANDLE 66 | ) -> HANDLE 67 | ); -------------------------------------------------------------------------------- /core/src/syscall/windows/SetFilePointerEx.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_longlong; 2 | use windows_sys::Win32::Foundation::{BOOL, HANDLE}; 3 | use windows_sys::Win32::Storage::FileSystem::SET_FILE_POINTER_MOVE_METHOD; 4 | 5 | trait SetFilePointerExSyscall { 6 | extern "system" fn SetFilePointerEx( 7 | &self, 8 | fn_ptr: Option<&extern "system" fn(HANDLE, c_longlong, *mut c_longlong, SET_FILE_POINTER_MOVE_METHOD) -> BOOL>, 9 | hfile: HANDLE, 10 | lidistancetomove: c_longlong, 11 | lpnewfilepointer: *mut c_longlong, 12 | dwmovemethod: SET_FILE_POINTER_MOVE_METHOD 13 | ) -> BOOL; 14 | } 15 | 16 | impl_syscall!(SetFilePointerExSyscallFacade, RawSetFilePointerExSyscall, 17 | SetFilePointerEx( 18 | hfile: HANDLE, 19 | lidistancetomove: c_longlong, 20 | lpnewfilepointer: *mut c_longlong, 21 | dwmovemethod: SET_FILE_POINTER_MOVE_METHOD 22 | ) -> BOOL 23 | ); 24 | 25 | impl_facade!(SetFilePointerExSyscallFacade, SetFilePointerExSyscall, 26 | SetFilePointerEx( 27 | hfile: HANDLE, 28 | lidistancetomove: c_longlong, 29 | lpnewfilepointer: *mut c_longlong, 30 | dwmovemethod: SET_FILE_POINTER_MOVE_METHOD 31 | ) -> BOOL 32 | ); 33 | 34 | impl_raw!(RawSetFilePointerExSyscall, SetFilePointerExSyscall, windows_sys::Win32::Storage::FileSystem, 35 | SetFilePointerEx( 36 | hfile: HANDLE, 37 | lidistancetomove: c_longlong, 38 | lpnewfilepointer: *mut c_longlong, 39 | dwmovemethod: SET_FILE_POINTER_MOVE_METHOD 40 | ) -> BOOL 41 | ); -------------------------------------------------------------------------------- /core/src/syscall/windows/Sleep.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use once_cell::sync::Lazy; 3 | use std::time::Duration; 4 | 5 | trait SleepSyscall { 6 | extern "system" fn Sleep(&self, fn_ptr: Option<&extern "system" fn(u32)>, dw_milliseconds: u32); 7 | } 8 | 9 | pub extern "system" fn Sleep(fn_ptr: Option<&extern "system" fn(u32)>, dw_milliseconds: u32) { 10 | static CHAIN: Lazy> = Lazy::new(Default::default); 11 | CHAIN.Sleep(fn_ptr, dw_milliseconds); 12 | } 13 | 14 | impl_facade!(SleepSyscallFacade, SleepSyscall, 15 | Sleep(dw_milliseconds: u32) -> () 16 | ); 17 | 18 | #[repr(C)] 19 | #[derive(Debug, Copy, Clone, Default)] 20 | struct NioSleepSyscall {} 21 | 22 | impl SleepSyscall for NioSleepSyscall { 23 | extern "system" fn Sleep(&self, _: Option<&extern "system" fn(u32)>, dw_milliseconds: u32) { 24 | let time = Duration::from_millis(u64::from(dw_milliseconds)); 25 | if let Some(co) = crate::scheduler::SchedulableCoroutine::current() { 26 | let syscall = crate::common::constants::SyscallName::Sleep; 27 | let new_state = crate::common::constants::SyscallState::Suspend( 28 | crate::common::get_timeout_time(time), 29 | ); 30 | if co.syscall((), syscall, new_state).is_err() { 31 | crate::error!( 32 | "{} change to syscall {} {} failed !", 33 | co.name(), 34 | syscall, 35 | new_state 36 | ); 37 | } 38 | } 39 | _ = EventLoops::wait_event(Some(time)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/syscall/windows/WSAAccept.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | use windows_sys::Win32::Networking::WinSock::{LPCONDITIONPROC, SOCKADDR, SOCKET}; 3 | 4 | trait WSAAcceptSyscall { 5 | extern "system" fn WSAAccept( 6 | &self, 7 | fn_ptr: Option< 8 | &extern "system" fn( 9 | SOCKET, 10 | *mut SOCKADDR, 11 | *mut c_int, 12 | LPCONDITIONPROC, 13 | usize 14 | ) -> SOCKET 15 | >, 16 | fd: SOCKET, 17 | address: *mut SOCKADDR, 18 | address_len: *mut c_int, 19 | lpfncondition: LPCONDITIONPROC, 20 | dwcallbackdata: usize, 21 | ) -> SOCKET; 22 | } 23 | 24 | impl_syscall!(WSAAcceptSyscallFacade, NioWSAAcceptSyscall, RawWSAAcceptSyscall, 25 | WSAAccept( 26 | fd: SOCKET, 27 | address: *mut SOCKADDR, 28 | address_len: *mut c_int, 29 | lpfncondition: LPCONDITIONPROC, 30 | dwcallbackdata: usize 31 | ) -> SOCKET 32 | ); 33 | 34 | impl_facade!(WSAAcceptSyscallFacade, WSAAcceptSyscall, 35 | WSAAccept( 36 | fd: SOCKET, 37 | address: *mut SOCKADDR, 38 | address_len: *mut c_int, 39 | lpfncondition: LPCONDITIONPROC, 40 | dwcallbackdata: usize 41 | ) -> SOCKET 42 | ); 43 | 44 | impl_nio_read!(NioWSAAcceptSyscall, WSAAcceptSyscall, 45 | WSAAccept( 46 | fd: SOCKET, 47 | address: *mut SOCKADDR, 48 | address_len: *mut c_int, 49 | lpfncondition: LPCONDITIONPROC, 50 | dwcallbackdata: usize 51 | ) -> SOCKET 52 | ); 53 | 54 | impl_raw!(RawWSAAcceptSyscall, WSAAcceptSyscall, windows_sys::Win32::Networking::WinSock, 55 | WSAAccept( 56 | fd: SOCKET, 57 | address: *mut SOCKADDR, 58 | address_len: *mut c_int, 59 | lpfncondition: LPCONDITIONPROC, 60 | dwcallbackdata: usize 61 | ) -> SOCKET 62 | ); 63 | -------------------------------------------------------------------------------- /core/src/syscall/windows/WSAPoll.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | use crate::net::EventLoops; 3 | use std::ffi::{c_int, c_uint}; 4 | use std::time::Duration; 5 | use windows_sys::Win32::Networking::WinSock::WSAPOLLFD; 6 | 7 | trait PollSyscall { 8 | extern "system" fn WSAPoll( 9 | &self, 10 | fn_ptr: Option<&extern "system" fn(*mut WSAPOLLFD, c_uint, c_int) -> c_int>, 11 | fds: *mut WSAPOLLFD, 12 | nfds: c_uint, 13 | timeout: c_int, 14 | ) -> c_int; 15 | } 16 | 17 | impl_syscall!(PollSyscallFacade, NioPollSyscall, RawPollSyscall, 18 | WSAPoll(fds: *mut WSAPOLLFD, nfds: c_uint, timeout: c_int) -> c_int 19 | ); 20 | 21 | impl_facade!(PollSyscallFacade, PollSyscall, 22 | WSAPoll(fds: *mut WSAPOLLFD, nfds: c_uint, timeout: c_int) -> c_int 23 | ); 24 | 25 | #[repr(C)] 26 | #[derive(Debug, Default)] 27 | struct NioPollSyscall { 28 | inner: I, 29 | } 30 | 31 | impl PollSyscall for NioPollSyscall { 32 | extern "system" fn WSAPoll( 33 | &self, 34 | fn_ptr: Option<&extern "system" fn(*mut WSAPOLLFD, c_uint, c_int) -> c_int>, 35 | fds: *mut WSAPOLLFD, 36 | nfds: c_uint, 37 | timeout: c_int, 38 | ) -> c_int { 39 | let mut t = if timeout < 0 { c_int::MAX } else { timeout }; 40 | let mut x = 1; 41 | let mut r; 42 | // just check poll every x ms 43 | loop { 44 | r = self.inner.WSAPoll(fn_ptr, fds, nfds, 0); 45 | if r != 0 || t == 0 { 46 | break; 47 | } 48 | _ = EventLoops::wait_event(Some(Duration::from_millis(t.min(x).try_into().expect("overflow")))); 49 | if t != c_int::MAX { 50 | t = if t > x { t - x } else { 0 }; 51 | } 52 | if x < 16 { 53 | x <<= 1; 54 | } 55 | } 56 | r 57 | } 58 | } 59 | 60 | impl_raw!(RawPollSyscall, PollSyscall, windows_sys::Win32::Networking::WinSock, 61 | WSAPoll(fds: *mut WSAPOLLFD, nfds: c_uint, timeout: c_int) -> c_int 62 | ); 63 | -------------------------------------------------------------------------------- /core/src/syscall/windows/WSARecv.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_int, c_uint}; 2 | use windows_sys::Win32::Networking::WinSock::{LPWSAOVERLAPPED_COMPLETION_ROUTINE, SOCKET, WSABUF}; 3 | use windows_sys::Win32::System::IO::OVERLAPPED; 4 | 5 | trait WSARecvSyscall { 6 | extern "system" fn WSARecv( 7 | &self, 8 | fn_ptr: Option< 9 | &extern "system" fn( 10 | SOCKET, 11 | *const WSABUF, 12 | c_uint, 13 | *mut c_uint, 14 | *mut c_uint, 15 | *mut OVERLAPPED, 16 | LPWSAOVERLAPPED_COMPLETION_ROUTINE, 17 | ) -> c_int, 18 | >, 19 | fd: SOCKET, 20 | buf: *const WSABUF, 21 | dwbuffercount: c_uint, 22 | lpnumberofbytesrecvd: *mut c_uint, 23 | lpflags: *mut c_uint, 24 | lpoverlapped: *mut OVERLAPPED, 25 | lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, 26 | ) -> c_int; 27 | } 28 | 29 | impl_syscall!(WSARecvSyscallFacade, NioWSARecvSyscall, RawWSARecvSyscall, 30 | WSARecv( 31 | fd: SOCKET, 32 | buf: *const WSABUF, 33 | dwbuffercount: c_uint, 34 | lpnumberofbytesrecvd: *mut c_uint, 35 | lpflags: *mut c_uint, 36 | lpoverlapped: *mut OVERLAPPED, 37 | lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 38 | ) -> c_int 39 | ); 40 | 41 | impl_facade!(WSARecvSyscallFacade, WSARecvSyscall, 42 | WSARecv( 43 | fd: SOCKET, 44 | buf: *const WSABUF, 45 | dwbuffercount: c_uint, 46 | lpnumberofbytesrecvd: *mut c_uint, 47 | lpflags: *mut c_uint, 48 | lpoverlapped: *mut OVERLAPPED, 49 | lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 50 | ) -> c_int 51 | ); 52 | 53 | impl_nio_read_iovec!(NioWSARecvSyscall, WSARecvSyscall, 54 | WSARecv( 55 | fd: SOCKET, 56 | buf: *const WSABUF, 57 | dwbuffercount: c_uint, 58 | lpnumberofbytesrecvd: *mut c_uint, 59 | lpflags: *mut c_uint, 60 | lpoverlapped: *mut OVERLAPPED, 61 | lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 62 | ) -> c_int 63 | ); 64 | 65 | impl_raw!(RawWSARecvSyscall, WSARecvSyscall, windows_sys::Win32::Networking::WinSock, 66 | WSARecv( 67 | fd: SOCKET, 68 | buf: *const WSABUF, 69 | dwbuffercount: c_uint, 70 | lpnumberofbytesrecvd: *mut c_uint, 71 | lpflags: *mut c_uint, 72 | lpoverlapped: *mut OVERLAPPED, 73 | lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 74 | ) -> c_int 75 | ); 76 | -------------------------------------------------------------------------------- /core/src/syscall/windows/WSASend.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_int, c_uint}; 2 | use windows_sys::Win32::Networking::WinSock::{LPWSAOVERLAPPED_COMPLETION_ROUTINE, SOCKET, WSABUF}; 3 | use windows_sys::Win32::System::IO::OVERLAPPED; 4 | 5 | trait WSASendSyscall { 6 | extern "system" fn WSASend( 7 | &self, 8 | fn_ptr: Option< 9 | &extern "system" fn( 10 | SOCKET, 11 | *const WSABUF, 12 | c_uint, 13 | *mut c_uint, 14 | c_uint, 15 | *mut OVERLAPPED, 16 | LPWSAOVERLAPPED_COMPLETION_ROUTINE, 17 | ) -> c_int, 18 | >, 19 | fd: SOCKET, 20 | buf: *const WSABUF, 21 | dwbuffercount: c_uint, 22 | lpnumberofbytessent: *mut c_uint, 23 | dwflags: c_uint, 24 | lpoverlapped: *mut OVERLAPPED, 25 | lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, 26 | ) -> c_int; 27 | } 28 | 29 | impl_syscall!(WSASendSyscallFacade, NioWSASendSyscall, RawWSASendSyscall, 30 | WSASend( 31 | fd: SOCKET, 32 | buf: *const WSABUF, 33 | dwbuffercount: c_uint, 34 | lpnumberofbytessent: *mut c_uint, 35 | dwflags: c_uint, 36 | lpoverlapped: *mut OVERLAPPED, 37 | lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 38 | ) -> c_int 39 | ); 40 | 41 | impl_facade!(WSASendSyscallFacade, WSASendSyscall, 42 | WSASend( 43 | fd: SOCKET, 44 | buf: *const WSABUF, 45 | dwbuffercount: c_uint, 46 | lpnumberofbytessent: *mut c_uint, 47 | dwflags: c_uint, 48 | lpoverlapped: *mut OVERLAPPED, 49 | lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 50 | ) -> c_int 51 | ); 52 | 53 | impl_nio_write_iovec!(NioWSASendSyscall, WSASendSyscall, 54 | WSASend( 55 | fd: SOCKET, 56 | buf: *const WSABUF, 57 | dwbuffercount: c_uint, 58 | lpnumberofbytessent: *mut c_uint, 59 | dwflags: c_uint, 60 | lpoverlapped: *mut OVERLAPPED, 61 | lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 62 | ) -> c_int 63 | ); 64 | 65 | impl_raw!(RawWSASendSyscall, WSASendSyscall, windows_sys::Win32::Networking::WinSock, 66 | WSASend( 67 | fd: SOCKET, 68 | buf: *const WSABUF, 69 | dwbuffercount: c_uint, 70 | lpnumberofbytessent: *mut c_uint, 71 | dwflags: c_uint, 72 | lpoverlapped: *mut OVERLAPPED, 73 | lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 74 | ) -> c_int 75 | ); 76 | -------------------------------------------------------------------------------- /core/src/syscall/windows/WSASocketW.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_int, c_uint}; 2 | use windows_sys::Win32::Networking::WinSock::{ 3 | IPPROTO, SOCKET, WINSOCK_SOCKET_TYPE, WSAPROTOCOL_INFOW, 4 | }; 5 | 6 | trait WSASocketWSyscall { 7 | extern "system" fn WSASocketW( 8 | &self, 9 | fn_ptr: Option< 10 | &extern "system" fn( 11 | c_int, 12 | WINSOCK_SOCKET_TYPE, 13 | IPPROTO, 14 | *const WSAPROTOCOL_INFOW, 15 | c_uint, 16 | c_uint, 17 | ) -> SOCKET, 18 | >, 19 | domain: c_int, 20 | ty: WINSOCK_SOCKET_TYPE, 21 | protocol: IPPROTO, 22 | lpprotocolinfo: *const WSAPROTOCOL_INFOW, 23 | g: c_uint, 24 | dw_flags: c_uint, 25 | ) -> SOCKET; 26 | } 27 | 28 | impl_syscall!(WSASocketWSyscallFacade, RawWSASocketWSyscall, 29 | WSASocketW( 30 | domain: c_int, 31 | ty: WINSOCK_SOCKET_TYPE, 32 | protocol: IPPROTO, 33 | lpprotocolinfo: *const WSAPROTOCOL_INFOW, 34 | g: c_uint, 35 | dw_flags: c_uint 36 | ) -> SOCKET 37 | ); 38 | 39 | impl_facade!(WSASocketWSyscallFacade, WSASocketWSyscall, 40 | WSASocketW( 41 | domain: c_int, 42 | ty: WINSOCK_SOCKET_TYPE, 43 | protocol: IPPROTO, 44 | lpprotocolinfo: *const WSAPROTOCOL_INFOW, 45 | g: c_uint, 46 | dw_flags: c_uint 47 | ) -> SOCKET 48 | ); 49 | 50 | impl_raw!(RawWSASocketWSyscall, WSASocketWSyscall, windows_sys::Win32::Networking::WinSock, 51 | WSASocketW( 52 | domain: c_int, 53 | ty: WINSOCK_SOCKET_TYPE, 54 | protocol: IPPROTO, 55 | lpprotocolinfo: *const WSAPROTOCOL_INFOW, 56 | g: c_uint, 57 | dw_flags: c_uint 58 | ) -> SOCKET 59 | ); 60 | -------------------------------------------------------------------------------- /core/src/syscall/windows/WaitOnAddress.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{c_uint, c_void}; 2 | use std::time::Duration; 3 | use windows_sys::Win32::Foundation::{BOOL, ERROR_TIMEOUT, FALSE, TRUE}; 4 | use crate::common::{get_timeout_time, now}; 5 | use crate::net::EventLoops; 6 | use crate::syscall::reset_errno; 7 | use crate::syscall::set_errno; 8 | 9 | trait WaitOnAddressSyscall { 10 | extern "system" fn WaitOnAddress( 11 | &self, 12 | fn_ptr: Option<&extern "system" fn(*const c_void, *const c_void, usize, c_uint) -> BOOL>, 13 | address: *const c_void, 14 | compareaddress: *const c_void, 15 | addresssize: usize, 16 | dwmilliseconds: c_uint 17 | ) -> BOOL; 18 | } 19 | 20 | impl_syscall!(WaitOnAddressSyscallFacade, NioWaitOnAddressSyscall, RawWaitOnAddressSyscall, 21 | WaitOnAddress( 22 | address: *const c_void, 23 | compareaddress: *const c_void, 24 | addresssize: usize, 25 | dwmilliseconds: c_uint 26 | ) -> BOOL 27 | ); 28 | 29 | impl_facade!(WaitOnAddressSyscallFacade, WaitOnAddressSyscall, 30 | WaitOnAddress( 31 | address: *const c_void, 32 | compareaddress: *const c_void, 33 | addresssize: usize, 34 | dwmilliseconds: c_uint 35 | ) -> BOOL 36 | ); 37 | 38 | #[repr(C)] 39 | #[derive(Debug, Default)] 40 | struct NioWaitOnAddressSyscall { 41 | inner: I, 42 | } 43 | 44 | impl WaitOnAddressSyscall for NioWaitOnAddressSyscall { 45 | extern "system" fn WaitOnAddress( 46 | &self, 47 | fn_ptr: Option<&extern "system" fn(*const c_void, *const c_void, usize, c_uint) -> BOOL>, 48 | address: *const c_void, 49 | compareaddress: *const c_void, 50 | addresssize: usize, 51 | dwmilliseconds: c_uint 52 | ) -> BOOL { 53 | let timeout = get_timeout_time(Duration::from_millis(dwmilliseconds.into())); 54 | loop { 55 | let mut left_time = timeout.saturating_sub(now()); 56 | if 0 == left_time { 57 | set_errno(ERROR_TIMEOUT); 58 | return FALSE; 59 | } 60 | let r = self.inner.WaitOnAddress( 61 | fn_ptr, 62 | address, 63 | compareaddress, 64 | addresssize, 65 | (left_time / 1_000_000).min(1).try_into().expect("overflow"), 66 | ); 67 | if TRUE == r { 68 | reset_errno(); 69 | return r; 70 | } 71 | left_time = timeout.saturating_sub(now()); 72 | if 0 == left_time { 73 | set_errno(ERROR_TIMEOUT); 74 | return FALSE; 75 | } 76 | let wait_time = if left_time > 10_000_000 { 77 | 10_000_000 78 | } else { 79 | left_time 80 | }; 81 | if EventLoops::wait_event(Some(Duration::new( 82 | wait_time / 1_000_000_000, 83 | (wait_time % 1_000_000_000) as _, 84 | ))) 85 | .is_err() 86 | { 87 | return r; 88 | } 89 | } 90 | } 91 | } 92 | 93 | impl_raw!(RawWaitOnAddressSyscall, WaitOnAddressSyscall, windows_sys::Win32::System::Threading, 94 | WaitOnAddress( 95 | address: *const c_void, 96 | compareaddress: *const c_void, 97 | addresssize: usize, 98 | dwmilliseconds: c_uint 99 | ) -> BOOL 100 | ); -------------------------------------------------------------------------------- /core/src/syscall/windows/connect.rs: -------------------------------------------------------------------------------- 1 | use crate::common::now; 2 | use crate::net::EventLoops; 3 | use crate::syscall::{is_blocking, reset_errno, set_blocking, set_errno, set_non_blocking, send_time_limit}; 4 | use std::ffi::c_int; 5 | use std::io::Error; 6 | use windows_sys::Win32::Networking::WinSock::{getpeername, getsockopt, SO_ERROR, SOCKADDR, SOCKET, SOL_SOCKET, WSAEALREADY, WSAEINPROGRESS, WSAEINTR, WSAETIMEDOUT, WSAEWOULDBLOCK}; 7 | 8 | trait ConnectSyscall { 9 | extern "system" fn connect( 10 | &self, 11 | fn_ptr: Option<&extern "system" fn(SOCKET, *const SOCKADDR, c_int) -> c_int>, 12 | fd: SOCKET, 13 | address: *const SOCKADDR, 14 | len: c_int, 15 | ) -> c_int; 16 | } 17 | 18 | impl_syscall!(ConnectSyscallFacade, NioConnectSyscall, RawConnectSyscall, 19 | connect(fd: SOCKET, address: *const SOCKADDR, len: c_int) -> c_int 20 | ); 21 | 22 | impl_facade!(ConnectSyscallFacade, ConnectSyscall, 23 | connect(fd: SOCKET, address: *const SOCKADDR, len: c_int) -> c_int 24 | ); 25 | 26 | #[repr(C)] 27 | #[derive(Debug, Default)] 28 | struct NioConnectSyscall { 29 | inner: I, 30 | } 31 | 32 | impl ConnectSyscall for NioConnectSyscall { 33 | extern "system" fn connect( 34 | &self, 35 | fn_ptr: Option<&extern "system" fn(SOCKET, *const SOCKADDR, c_int) -> c_int>, 36 | fd: SOCKET, 37 | address: *const SOCKADDR, 38 | len: c_int, 39 | ) -> c_int { 40 | let blocking = is_blocking(fd); 41 | if blocking { 42 | set_non_blocking(fd); 43 | } 44 | let start_time = now(); 45 | let mut left_time = send_time_limit(fd); 46 | let mut r = self.inner.connect(fn_ptr, fd, address, len); 47 | while left_time > 0 { 48 | if r == 0 { 49 | reset_errno(); 50 | break; 51 | } 52 | let errno = Error::last_os_error().raw_os_error(); 53 | if errno == Some(WSAEINPROGRESS) || errno == Some(WSAEALREADY) || errno == Some(WSAEWOULDBLOCK) { 54 | //阻塞,直到写事件发生 55 | left_time = start_time 56 | .saturating_add(send_time_limit(fd)) 57 | .saturating_sub(now()); 58 | let wait_time = std::time::Duration::from_nanos(left_time) 59 | .min(crate::common::constants::SLICE); 60 | if EventLoops::wait_write_event( 61 | fd.try_into().expect("overflow"), 62 | Some(wait_time) 63 | ).is_err() { 64 | break; 65 | } 66 | let mut err = 0; 67 | unsafe { 68 | let mut len = c_int::try_from(size_of_val(&err)).expect("overflow"); 69 | r = getsockopt( 70 | fd, 71 | SOL_SOCKET, 72 | SO_ERROR, 73 | std::ptr::addr_of_mut!(err).cast::(), 74 | &raw mut len, 75 | ); 76 | } 77 | if r != 0 { 78 | r = -1; 79 | break; 80 | } 81 | if err != 0 { 82 | set_errno(err); 83 | r = -1; 84 | break; 85 | } 86 | unsafe { 87 | let mut address = std::mem::zeroed(); 88 | let mut address_len = c_int::try_from(size_of_val(&address)).expect("overflow"); 89 | r = getpeername(fd, &raw mut address, &raw mut address_len); 90 | } 91 | } else if errno != Some(WSAEINTR) { 92 | break; 93 | } 94 | } 95 | if r == -1 && Error::last_os_error().raw_os_error() == Some(WSAETIMEDOUT) { 96 | set_errno(WSAEINPROGRESS.try_into().expect("overflow")); 97 | } 98 | if blocking { 99 | set_blocking(fd); 100 | } 101 | r 102 | } 103 | } 104 | 105 | impl_raw!(RawConnectSyscall, ConnectSyscall, windows_sys::Win32::Networking::WinSock, 106 | connect(fd: SOCKET, address: *const SOCKADDR, len: c_int) -> c_int 107 | ); 108 | -------------------------------------------------------------------------------- /core/src/syscall/windows/ioctlsocket.rs: -------------------------------------------------------------------------------- 1 | use crate::common::constants::{CoroutineState, SyscallName, SyscallState}; 2 | use crate::scheduler::SchedulableCoroutine; 3 | use crate::syscall::windows::NON_BLOCKING; 4 | use crate::{error, info}; 5 | use std::ffi::{c_int, c_uint}; 6 | use windows_sys::Win32::Networking::WinSock::SOCKET; 7 | 8 | trait IoctlsocketSyscall { 9 | extern "system" fn ioctlsocket( 10 | &self, 11 | fn_ptr: Option<&extern "system" fn(SOCKET, c_int, *mut c_uint) -> c_int>, 12 | fd: SOCKET, 13 | cmd: c_int, 14 | argp: *mut c_uint, 15 | ) -> c_int; 16 | } 17 | 18 | impl_syscall!(IoctlsocketSyscallFacade, NioIoctlsocketSyscall, RawIoctlsocketSyscall, 19 | ioctlsocket(fd: SOCKET, cmd: c_int, argp: *mut c_uint) -> c_int 20 | ); 21 | 22 | #[repr(C)] 23 | #[derive(Debug, Default)] 24 | struct IoctlsocketSyscallFacade { 25 | inner: I, 26 | } 27 | 28 | impl IoctlsocketSyscall for IoctlsocketSyscallFacade { 29 | extern "system" fn ioctlsocket( 30 | &self, 31 | fn_ptr: Option<&extern "system" fn(SOCKET, c_int, *mut c_uint) -> c_int>, 32 | fd: SOCKET, 33 | cmd: c_int, 34 | argp: *mut c_uint, 35 | ) -> c_int { 36 | let syscall = SyscallName::ioctlsocket; 37 | info!("enter syscall {}", syscall); 38 | if let Some(co) = SchedulableCoroutine::current() { 39 | _ = co.syscall((), syscall, SyscallState::Executing); 40 | } 41 | let r = self.inner.ioctlsocket(fn_ptr, fd, cmd, argp); 42 | if let Some(co) = SchedulableCoroutine::current() { 43 | if let CoroutineState::Syscall((), SyscallName::ioctlsocket, SyscallState::Executing) = 44 | co.state() 45 | { 46 | if co.running().is_err() { 47 | error!("{} change to running state failed !", co.name()); 48 | } 49 | } 50 | } 51 | info!("exit syscall {}", syscall); 52 | r 53 | } 54 | } 55 | 56 | #[repr(C)] 57 | #[derive(Debug, Default)] 58 | struct NioIoctlsocketSyscall { 59 | inner: I, 60 | } 61 | 62 | impl IoctlsocketSyscall for NioIoctlsocketSyscall { 63 | extern "system" fn ioctlsocket( 64 | &self, 65 | fn_ptr: Option<&extern "system" fn(SOCKET, c_int, *mut c_uint) -> c_int>, 66 | fd: SOCKET, 67 | cmd: c_int, 68 | argp: *mut c_uint, 69 | ) -> c_int { 70 | let r = self.inner.ioctlsocket(fn_ptr, fd, cmd, argp); 71 | if 0 == r { 72 | if 0 == unsafe { *argp } { 73 | _ = NON_BLOCKING.remove(&fd); 74 | } else { 75 | _ = NON_BLOCKING.insert(fd); 76 | } 77 | } 78 | r 79 | } 80 | } 81 | 82 | impl_raw!(RawIoctlsocketSyscall, IoctlsocketSyscall, windows_sys::Win32::Networking::WinSock, 83 | ioctlsocket(fd: SOCKET, cmd: c_int, argp: *mut c_uint) -> c_int 84 | ); 85 | -------------------------------------------------------------------------------- /core/src/syscall/windows/listen.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | use windows_sys::Win32::Networking::WinSock::SOCKET; 3 | 4 | trait ListenSyscall { 5 | extern "system" fn listen( 6 | &self, 7 | fn_ptr: Option<&extern "system" fn(SOCKET, c_int) -> c_int>, 8 | fd: SOCKET, 9 | backlog: c_int, 10 | ) -> c_int; 11 | } 12 | 13 | impl_syscall!(ListenSyscallFacade, RawListenSyscall, listen(fd: SOCKET, backlog: c_int) -> c_int); 14 | 15 | impl_facade!(ListenSyscallFacade, ListenSyscall, listen(fd: SOCKET, backlog: c_int) -> c_int); 16 | 17 | impl_raw!(RawListenSyscall, ListenSyscall, windows_sys::Win32::Networking::WinSock, 18 | listen(fd: SOCKET, backlog: c_int) -> c_int 19 | ); 20 | -------------------------------------------------------------------------------- /core/src/syscall/windows/recv.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | use windows_sys::core::PSTR; 3 | use windows_sys::Win32::Networking::WinSock::{SEND_RECV_FLAGS, SOCKET}; 4 | 5 | trait RecvSyscall { 6 | extern "system" fn recv( 7 | &self, 8 | fn_ptr: Option<&extern "system" fn(SOCKET, PSTR, c_int, SEND_RECV_FLAGS) -> c_int>, 9 | fd: SOCKET, 10 | buf: PSTR, 11 | len: c_int, 12 | flags: SEND_RECV_FLAGS, 13 | ) -> c_int; 14 | } 15 | 16 | impl_syscall!(RecvSyscallFacade, NioRecvSyscall, RawRecvSyscall, 17 | recv(fd: SOCKET, buf: PSTR, len: c_int, flags: SEND_RECV_FLAGS) -> c_int 18 | ); 19 | 20 | impl_facade!(RecvSyscallFacade, RecvSyscall, 21 | recv(fd: SOCKET, buf: PSTR, len: c_int, flags: SEND_RECV_FLAGS) -> c_int 22 | ); 23 | 24 | impl_nio_read_buf!(NioRecvSyscall, RecvSyscall, 25 | recv(fd: SOCKET, buf: PSTR, len: c_int, flags: SEND_RECV_FLAGS) -> c_int 26 | ); 27 | 28 | impl_raw!(RawRecvSyscall, RecvSyscall, windows_sys::Win32::Networking::WinSock, 29 | recv(fd: SOCKET, buf: PSTR, len: c_int, flags: SEND_RECV_FLAGS) -> c_int 30 | ); 31 | -------------------------------------------------------------------------------- /core/src/syscall/windows/select.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use std::ffi::{c_int, c_uint}; 3 | use std::time::Duration; 4 | use windows_sys::Win32::Networking::WinSock::{FD_SET, TIMEVAL}; 5 | 6 | trait SelectSyscall { 7 | extern "system" fn select( 8 | &self, 9 | fn_ptr: Option< 10 | &extern "system" fn(c_int, *mut FD_SET, *mut FD_SET, *mut FD_SET, *mut TIMEVAL) -> c_int, 11 | >, 12 | nfds: c_int, 13 | readfds: *mut FD_SET, 14 | writefds: *mut FD_SET, 15 | errorfds: *mut FD_SET, 16 | timeout: *mut TIMEVAL, 17 | ) -> c_int; 18 | } 19 | 20 | impl_syscall!(SelectSyscallFacade, NioSelectSyscall, RawSelectSyscall, 21 | select( 22 | nfds: c_int, 23 | readfds: *mut FD_SET, 24 | writefds: *mut FD_SET, 25 | errorfds: *mut FD_SET, 26 | timeout: *mut TIMEVAL 27 | ) -> c_int 28 | ); 29 | 30 | impl_facade!(SelectSyscallFacade, SelectSyscall, 31 | select( 32 | nfds: c_int, 33 | readfds: *mut FD_SET, 34 | writefds: *mut FD_SET, 35 | errorfds: *mut FD_SET, 36 | timeout: *mut TIMEVAL 37 | ) -> c_int 38 | ); 39 | 40 | #[repr(C)] 41 | #[derive(Debug, Default)] 42 | struct NioSelectSyscall { 43 | inner: I, 44 | } 45 | 46 | impl SelectSyscall for NioSelectSyscall { 47 | extern "system" fn select( 48 | &self, 49 | fn_ptr: Option< 50 | &extern "system" fn(c_int, *mut FD_SET, *mut FD_SET, *mut FD_SET, *mut TIMEVAL) -> c_int, 51 | >, 52 | nfds: c_int, 53 | readfds: *mut FD_SET, 54 | writefds: *mut FD_SET, 55 | errorfds: *mut FD_SET, 56 | timeout: *mut TIMEVAL, 57 | ) -> c_int { 58 | let mut t = if timeout.is_null() { 59 | c_uint::MAX 60 | } else { 61 | unsafe { 62 | c_uint::try_from((*timeout).tv_sec).expect("overflow") 63 | .saturating_mul(1_000_000) 64 | .saturating_add(c_uint::try_from((*timeout).tv_usec).expect("overflow")) 65 | } 66 | }; 67 | let mut o = TIMEVAL { 68 | tv_sec: 0, 69 | tv_usec: 0, 70 | }; 71 | let mut s: [FD_SET; 3] = unsafe { std::mem::zeroed() }; 72 | if !readfds.is_null() { 73 | s[0] = unsafe { *readfds }; 74 | } 75 | if !writefds.is_null() { 76 | s[1] = unsafe { *writefds }; 77 | } 78 | if !errorfds.is_null() { 79 | s[2] = unsafe { *errorfds }; 80 | } 81 | let mut x = 1; 82 | let mut r; 83 | // just check select every x ms 84 | loop { 85 | r = self 86 | .inner 87 | .select(fn_ptr, nfds, readfds, writefds, errorfds, &raw mut o); 88 | if r != 0 || t == 0 { 89 | break; 90 | } 91 | _ = EventLoops::wait_event(Some(Duration::from_millis(u64::from(t.min(x))))); 92 | if t != c_uint::MAX { 93 | t = t.saturating_sub(x); 94 | } 95 | if x < 16 { 96 | x <<= 1; 97 | } 98 | 99 | if !readfds.is_null() { 100 | unsafe { *readfds = s[0] }; 101 | } 102 | if !writefds.is_null() { 103 | unsafe { *writefds = s[1] }; 104 | } 105 | if !errorfds.is_null() { 106 | unsafe { *errorfds = s[2] }; 107 | } 108 | o.tv_sec = 0; 109 | o.tv_usec = 0; 110 | } 111 | r 112 | } 113 | } 114 | 115 | impl_raw!(RawSelectSyscall, SelectSyscall, windows_sys::Win32::Networking::WinSock, 116 | select( 117 | nfds: c_int, 118 | readfds: *mut FD_SET, 119 | writefds: *mut FD_SET, 120 | errorfds: *mut FD_SET, 121 | timeout: *mut TIMEVAL 122 | ) -> c_int 123 | ); 124 | -------------------------------------------------------------------------------- /core/src/syscall/windows/send.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | use windows_sys::core::PCSTR; 3 | use windows_sys::Win32::Networking::WinSock::{SEND_RECV_FLAGS, SOCKET}; 4 | 5 | trait SendSyscall { 6 | extern "system" fn send( 7 | &self, 8 | fn_ptr: Option<&extern "system" fn(SOCKET, PCSTR, c_int, SEND_RECV_FLAGS) -> c_int>, 9 | fd: SOCKET, 10 | buf: PCSTR, 11 | len: c_int, 12 | flags: SEND_RECV_FLAGS, 13 | ) -> c_int; 14 | } 15 | 16 | impl_syscall!(SendSyscallFacade, NioSendSyscall, RawSendSyscall, 17 | send(fd: SOCKET, buf: PCSTR, len: c_int, flags: SEND_RECV_FLAGS) -> c_int 18 | ); 19 | 20 | impl_facade!(SendSyscallFacade, SendSyscall, 21 | send(fd: SOCKET, buf: PCSTR, len: c_int, flags: SEND_RECV_FLAGS) -> c_int 22 | ); 23 | 24 | impl_nio_write_buf!(NioSendSyscall, SendSyscall, 25 | send(fd: SOCKET, buf: PCSTR, len: c_int, flags: SEND_RECV_FLAGS) -> c_int 26 | ); 27 | 28 | impl_raw!(RawSendSyscall, SendSyscall, windows_sys::Win32::Networking::WinSock, 29 | send(fd: SOCKET, buf: PCSTR, len: c_int, flags: SEND_RECV_FLAGS) -> c_int 30 | ); 31 | -------------------------------------------------------------------------------- /core/src/syscall/windows/setsockopt.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | use windows_sys::core::PSTR; 3 | use crate::syscall::windows::{RECV_TIME_LIMIT, SEND_TIME_LIMIT}; 4 | use windows_sys::Win32::Networking::WinSock::{SO_RCVTIMEO, SO_SNDTIMEO, SOCKET, SOL_SOCKET}; 5 | 6 | trait SetsockoptSyscall { 7 | extern "system" fn setsockopt( 8 | &self, 9 | fn_ptr: Option<&extern "system" fn(SOCKET, c_int, c_int, PSTR, c_int) -> c_int>, 10 | socket: SOCKET, 11 | level: c_int, 12 | name: c_int, 13 | value: PSTR, 14 | option_len: c_int 15 | ) -> c_int; 16 | } 17 | 18 | impl_syscall!(SetsockoptSyscallFacade, NioSetsockoptSyscall, RawSetsockoptSyscall, 19 | setsockopt(socket: SOCKET, level: c_int, name: c_int, value: PSTR, option_len: c_int) -> c_int 20 | ); 21 | 22 | impl_facade!(SetsockoptSyscallFacade, SetsockoptSyscall, 23 | setsockopt(socket: SOCKET, level: c_int, name: c_int, value: PSTR, option_len: c_int) -> c_int 24 | ); 25 | 26 | #[repr(C)] 27 | #[derive(Debug, Default)] 28 | struct NioSetsockoptSyscall { 29 | inner: I, 30 | } 31 | 32 | #[allow(clippy::cast_ptr_alignment)] 33 | impl SetsockoptSyscall for NioSetsockoptSyscall { 34 | extern "system" fn setsockopt( 35 | &self, 36 | fn_ptr: Option<&extern "system" fn(SOCKET, c_int, c_int, PSTR, c_int) -> c_int>, 37 | socket: SOCKET, 38 | level: c_int, 39 | name: c_int, 40 | value: PSTR, 41 | option_len: c_int 42 | ) -> c_int { 43 | let r= self.inner.setsockopt(fn_ptr, socket, level, name, value, option_len); 44 | if 0 == r && SOL_SOCKET == level { 45 | if SO_SNDTIMEO == name { 46 | let ms = unsafe { *value.cast::() }; 47 | let mut time_limit = u64::try_from(ms) 48 | .expect("overflow") 49 | .saturating_mul(1_000_000); 50 | if 0 == time_limit { 51 | // 取消超时 52 | time_limit = u64::MAX; 53 | } 54 | assert!(SEND_TIME_LIMIT.insert(socket, time_limit).is_none()); 55 | } else if SO_RCVTIMEO == name { 56 | let ms = unsafe { *value.cast::() }; 57 | let mut time_limit = u64::try_from(ms) 58 | .expect("overflow") 59 | .saturating_mul(1_000_000); 60 | if 0 == time_limit { 61 | // 取消超时 62 | time_limit = u64::MAX; 63 | } 64 | assert!(RECV_TIME_LIMIT.insert(socket, time_limit).is_none()); 65 | } 66 | } 67 | r 68 | } 69 | } 70 | 71 | impl_raw!(RawSetsockoptSyscall, SetsockoptSyscall, windows_sys::Win32::Networking::WinSock, 72 | setsockopt(socket: SOCKET, level: c_int, name: c_int, value: PSTR, option_len: c_int) -> c_int 73 | ); 74 | -------------------------------------------------------------------------------- /core/src/syscall/windows/shutdown.rs: -------------------------------------------------------------------------------- 1 | use crate::net::EventLoops; 2 | use crate::syscall::set_errno; 3 | use std::ffi::c_int; 4 | use windows_sys::Win32::Networking::WinSock::{SOCKET, WINSOCK_SHUTDOWN_HOW, SD_RECEIVE, SD_SEND, SD_BOTH, WSAEINVAL}; 5 | 6 | trait ShutdownSyscall { 7 | extern "system" fn shutdown( 8 | &self, 9 | fn_ptr: Option<&extern "system" fn(SOCKET, WINSOCK_SHUTDOWN_HOW) -> c_int>, 10 | fd: SOCKET, 11 | how: WINSOCK_SHUTDOWN_HOW, 12 | ) -> c_int; 13 | } 14 | 15 | impl_syscall!(ShutdownSyscallFacade, NioShutdownSyscall, RawShutdownSyscall, 16 | shutdown(fd: SOCKET, how: WINSOCK_SHUTDOWN_HOW) -> c_int 17 | ); 18 | 19 | impl_facade!(ShutdownSyscallFacade, ShutdownSyscall, shutdown(fd: SOCKET, how: WINSOCK_SHUTDOWN_HOW) -> c_int); 20 | 21 | #[repr(C)] 22 | #[derive(Debug, Default)] 23 | struct NioShutdownSyscall { 24 | inner: I, 25 | } 26 | 27 | impl ShutdownSyscall for NioShutdownSyscall { 28 | extern "system" fn shutdown( 29 | &self, 30 | fn_ptr: Option<&extern "system" fn(SOCKET, WINSOCK_SHUTDOWN_HOW) -> c_int>, 31 | fd: SOCKET, 32 | how: WINSOCK_SHUTDOWN_HOW, 33 | ) -> c_int { 34 | { 35 | let fd = fd.try_into().expect("overflow"); 36 | _ = match how { 37 | SD_RECEIVE => EventLoops::del_read_event(fd), 38 | SD_SEND => EventLoops::del_write_event(fd), 39 | SD_BOTH => EventLoops::del_event(fd), 40 | _ => { 41 | set_errno(WSAEINVAL.try_into().expect("overflow")); 42 | return -1; 43 | } 44 | }; 45 | } 46 | self.inner.shutdown(fn_ptr, fd, how) 47 | } 48 | } 49 | 50 | impl_raw!(RawShutdownSyscall, ShutdownSyscall, windows_sys::Win32::Networking::WinSock, 51 | shutdown(fd: SOCKET, how: WINSOCK_SHUTDOWN_HOW) -> c_int 52 | ); 53 | -------------------------------------------------------------------------------- /core/src/syscall/windows/socket.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_int; 2 | use windows_sys::Win32::Networking::WinSock::{IPPROTO, SOCKET, WINSOCK_SOCKET_TYPE}; 3 | 4 | trait SocketSyscall { 5 | extern "system" fn socket( 6 | &self, 7 | fn_ptr: Option<&extern "system" fn(c_int, WINSOCK_SOCKET_TYPE, IPPROTO) -> SOCKET>, 8 | domain: c_int, 9 | ty: WINSOCK_SOCKET_TYPE, 10 | protocol: IPPROTO, 11 | ) -> SOCKET; 12 | } 13 | 14 | impl_syscall!(SocketSyscallFacade, RawSocketSyscall, 15 | socket(domain: c_int, ty: WINSOCK_SOCKET_TYPE, protocol: IPPROTO) -> SOCKET 16 | ); 17 | 18 | impl_facade!(SocketSyscallFacade, SocketSyscall, 19 | socket(domain: c_int, ty: WINSOCK_SOCKET_TYPE, protocol: IPPROTO) -> SOCKET 20 | ); 21 | 22 | impl_raw!(RawSocketSyscall, SocketSyscall, windows_sys::Win32::Networking::WinSock, 23 | socket(domain: c_int, ty: WINSOCK_SOCKET_TYPE, protocol: IPPROTO) -> SOCKET 24 | ); 25 | -------------------------------------------------------------------------------- /core/tests/co_pool.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(all(unix, feature = "preemptive")))] 2 | #[test] 3 | fn co_pool_basic() -> std::io::Result<()> { 4 | let mut pool = open_coroutine_core::co_pool::CoroutinePool::default(); 5 | pool.set_max_size(1); 6 | assert!(pool.is_empty()); 7 | _ = pool.submit_task( 8 | Some(String::from("test_panic")), 9 | |_| panic!("test panic, just ignore it"), 10 | None, 11 | None, 12 | )?; 13 | assert!(!pool.is_empty()); 14 | pool.submit_task( 15 | Some(String::from("test_simple")), 16 | |_| { 17 | println!("2"); 18 | Some(2) 19 | }, 20 | None, 21 | None, 22 | )?; 23 | pool.try_schedule_task() 24 | } 25 | 26 | #[cfg(not(all(unix, feature = "preemptive")))] 27 | #[test] 28 | fn co_pool_suspend() -> std::io::Result<()> { 29 | let mut pool = open_coroutine_core::co_pool::CoroutinePool::default(); 30 | pool.set_max_size(2); 31 | _ = pool.submit_task( 32 | None, 33 | |param| { 34 | println!("[coroutine] delay"); 35 | if let Some(suspender) = open_coroutine_core::scheduler::SchedulableSuspender::current() 36 | { 37 | suspender.delay(std::time::Duration::from_millis(100)); 38 | } 39 | println!("[coroutine] back"); 40 | param 41 | }, 42 | None, 43 | None, 44 | )?; 45 | _ = pool.submit_task( 46 | None, 47 | |_| { 48 | println!("middle"); 49 | Some(1) 50 | }, 51 | None, 52 | None, 53 | )?; 54 | pool.try_schedule_task()?; 55 | std::thread::sleep(std::time::Duration::from_millis(200)); 56 | pool.try_schedule_task() 57 | } 58 | 59 | #[cfg(not(all(unix, feature = "preemptive")))] 60 | #[test] 61 | fn co_pool_stop() -> std::io::Result<()> { 62 | let pool = open_coroutine_core::co_pool::CoroutinePool::default(); 63 | pool.set_max_size(1); 64 | _ = pool.submit_task(None, |_| panic!("test panic, just ignore it"), None, None)?; 65 | pool.submit_task( 66 | None, 67 | |_| { 68 | println!("2"); 69 | Some(2) 70 | }, 71 | None, 72 | None, 73 | ) 74 | .map(|_| ()) 75 | } 76 | 77 | #[cfg(not(all(unix, feature = "preemptive")))] 78 | #[test] 79 | fn co_pool_cancel() -> std::io::Result<()> { 80 | let mut pool = open_coroutine_core::co_pool::CoroutinePool::default(); 81 | pool.set_max_size(1); 82 | assert!(pool.is_empty()); 83 | let task_name = pool.submit_task( 84 | Some(String::from("test_panic")), 85 | |_| panic!("test panic, just ignore it"), 86 | None, 87 | None, 88 | )?; 89 | assert!(!pool.is_empty()); 90 | open_coroutine_core::co_pool::CoroutinePool::try_cancel_task(&task_name); 91 | pool.submit_task( 92 | Some(String::from("test_simple")), 93 | |_| { 94 | println!("2"); 95 | Some(2) 96 | }, 97 | None, 98 | None, 99 | )?; 100 | pool.try_schedule_task() 101 | } 102 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [licenses] 2 | allow = [ 3 | "Apache-2.0", 4 | "Apache-2.0 WITH LLVM-exception", 5 | "Unlicense", 6 | "Unicode-3.0", 7 | "MIT" 8 | ] 9 | confidence-threshold = 0.95 10 | private = {ignore = true} 11 | 12 | [bans] 13 | multiple-versions = "allow" 14 | -------------------------------------------------------------------------------- /docs/cn/background.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 诞生之因 3 | date: 2024-12-29 09:00:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # 诞生之因 8 | 9 | [English](../en/background.md) | 中文 10 | 11 | ## 待调优的线程池 12 | 13 | 在早期程序员为了支持多个用户并发访问服务应用,往往采用多进程方式,即针对每一个TCP网络连接创建一个服务进程。在2000年左右,比较流行使用CGI方式编写Web服务,当时人们用的比较多的Web服务器是基于多进程模式开发的Apache 14 | 1.3.x系列,因为进程占用系统资源较多,而线程占用的资源更少,所以人们开始使用多线程的方式(一般都会用到线程池) 15 | 编写Web服务应用,这使单台服务器支撑的用户并发度提高了,但依然存在资源浪费的问题。 16 | 17 | 2020年我入职V公司,由于内部系统不时出现线程池打满的情况,再加上TL读过[《Java线程池实现原理及其在美团业务中的实践》](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html) 18 | ,我们决定构建自己的动态线程池,从过程来看,效果不错: 19 | 20 |
21 | 22 |
23 | 24 | 但是这没有从根本上解决问题。众所周知,线程上下文切换具有一定开销,线程数越多,线程上下文切换开销越大。对于CPU密集型任务,只需保证线程数等于CPU核心数、并将线程绑定到指定CPU核心( 25 | 以下简称为`thread-per-core`) 26 | ,即可保证最优性能,而对于IO密集型任务,由于任务几乎必定阻塞住线程,线程上下文切换开销一般小于阻塞开销,但当线程数过大时,线程上下文切换开销就会大于阻塞开销了。 27 | 28 | 动态线程池的本质就是通过调整线程数,尽可能地让线程上下文切换开销小于阻塞开销。由于这个是人工的,那么必然保证不了。 29 | 30 |
31 | 32 |
33 | 34 | ## NIO之痛 35 | 36 | 那么有没有一种技术能够在保证thread-per-core的前提下,执行IO密集型任务性能不输多线程呢?答案是`NIO`,但仍存在一些限制或者不友好的地方: 37 | 38 | 1. NIO API使用起来相比BIO API更加复杂; 39 | 2. sleep等系统调用会阻塞线程,如果要发挥最佳性能,相当于禁用所有阻塞调用,这对开发者不友好; 40 | 3. 在线程池模式下,对于单线程来说,只有当前任务执行完了,才能执行下一个任务,无法实现任务间的公平调度; 41 | 42 | PS:假设单线程,CPU时间片为1s,有100个任务,公平调度指每个任务都能公平地占用到10ms的时间片。 43 | 44 | 1还可以克服,2和3是硬伤,其实如果能够实现3,RPC框架们也不用搞太多线程,只要thread-per-core即可。 45 | 46 | 如何在能够保证thread-per-core、执行IO密集型任务性能不输多线程的前提下,开发者使用还十分简单呢?`协程`慢慢进入了我的视野。 47 | 48 | ## Goroutine不香了 49 | 50 | 一开始玩协程,出于学习成本的考虑,首先选择的是`kotlin`,但当我发现kotlin的协程需要更换API( 51 | 比如把Thread.sleep替换为kotlinx.coroutines.delay)才不会阻塞线程后,果断把方向调整为`golang`,大概2周后: 52 | 53 |
54 | 55 |
56 | 57 | 协程技术哪家强,编程语言找golang。然而随着更深入的学习,我发现几个`goroutine`的不足: 58 | 59 | 1. `不是thread-per-core`。goroutine运行时也是由线程池来支撑的,而这个线程池的最大线程为256,这个数字一般比thread-per-core的线程数大得多,且调度线程未绑定到CPU; 60 | 2. `抢占调度会打断正在运行的系统调用`。如果这个系统调用需要很长时间才能完成,显然会被打断多次,整体性能反而降低; 61 | 3. `goroutine离极限性能有明显差距`。对比隔壁c/c++协程库,其性能甚至能到goroutine的1.5倍; 62 | 63 | 带着遗憾,我开始继续研究c/c++的协程库,发现它们要么是只做了`hook`( 64 | 这里解释下hook技术,简单的说,就是代理系统调用,比如调用sleep,没有hook的话会调用操作系统的sleep函数,hook之后会指向我们自己的代码,详细操作步骤可参考`《Linux/Unix系统编程手册》41章和42章`) 65 | ,要么只做了`任务窃取` 66 | ,还有一些库只提供最基础的`协程抽象`,而最令人失望的是:没有一个协程库实现了`抢占调度`。 67 | 68 | 没办法,看样子只能自己干了。 69 | 70 |
71 | 72 |
73 | -------------------------------------------------------------------------------- /docs/cn/why-rust.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 语言选择 3 | date: 2024-12-29 10:00:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # 语言选择 8 | 9 | [English](../en/why-rust.md) | 中文 10 | 11 | 开发open-coroutine用什么语言呢?这是一个很重要的问题,毕竟不同的语言有不同的特性,选择不同的语言会对最终的结果产生很大的影响。 12 | 13 | 之前研究c协程库时,有看到大佬已经尝试过用c写动态链接库、然后java通过jni去调这种方式,最终失败了,具体原因得深入JVM源码才能得知,对鄙人来说太高深,告辞,因此排除java/kotlin等JVM字节码语言。 14 | 15 | 显然,用golang再去实现一个goroutine,且不说其复杂程度完全不亚于深入JVM源码,而且即使真的做出来,也不可能有人愿意在生产环境使用,因此排除golang。 16 | 17 | 到目前为止还剩下c/c++/rust 3位选手。 18 | 19 | 从研究过的好几个用c写的协程库来看,c的表达力差了点,需要编写巨量代码。相较之下,c++表达力就强多了,但开发的效率还是低了些,主要体现在以下几个方面: 20 | 21 | 1. `必须写cmake`。纯粹为了告诉系统怎么编译,有些麻烦,而这其实是不应该操心的部分; 22 | 2. `依赖管理麻烦`。如果要用别人写的类库,需要把代码拉下来,放到自己项目里,然后不得不耗费大量时间来通过编译。如果别人的库没有其他依赖还好,一旦有其他依赖,那么它依赖的依赖,也得按照刚才说的步骤处理,这就十分麻烦了; 23 | 3. `内存不安全`。c++很难写出没有内存泄漏/崩溃的代码。 24 | 25 |
26 | 27 | 28 |
29 | -------------------------------------------------------------------------------- /docs/en/why-rust.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Language Selection 3 | date: 2025-02-24 17:37:10 4 | author: loongs-zhang 5 | --- 6 | 7 | # Language Selection 8 | 9 | English | [中文](../cn/why-rust.md) 10 | 11 | What language is used to develop open routine? This is a very important issue, as different languages have different 12 | features, and choosing different language can have a significant impact on the final outcome. 13 | 14 | When researching the C coroutine library before, I saw that some experts had already tried to write dynamic link 15 | libraries in C and call them in Java through JNI, but finally failed. The specific reason needs to be found in the 16 | JVM source code, which is too hard for me, goodbye. So JVM bytecode languages such as Java/Kotlin are excluded. 17 | 18 | Obviously, using Golang to implement a goroutine is no less complex than delving into JVM source code, and even if it is 19 | actually finished, no one would be willing to use it in a production environment, so Golang is excluded. 20 | 21 | Now, there are still three players left: c/c++/rust. 22 | 23 | From several coroutine libraries written in C that have been studied, it can be seen that the expressiveness of C is a 24 | bit lacking and requires writing a huge amount of code. In comparison, C++ has much stronger expressive power, but its 25 | development efficiency is still low, mainly reflected in the following aspects: 26 | 27 | 1. `Have to write cmake`. Purely to tell the system how to compile, it's a bit troublesome, but this is actually the 28 | part that shouldn't be worried about; 29 | 2. `Difficulty in dependency management`. If you want to use a library written by someone else, you need to pull down 30 | the code and put it into your own project, and then you have to spend a lot of time compiling it. If the library has 31 | no other dependencies, it can barely be handled. Once there are other dependencies, the dependencies it depends on 32 | must also be handled according to the steps just mentioned, which can be very troublesome; 33 | 3. `Memory is unsafe`. It's difficult to write code in C++ without memory leaks/crashes. 34 | 35 |
36 | 37 | 38 |
39 | -------------------------------------------------------------------------------- /docs/img/begin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acl-dev/open-coroutine/50ad55fd49e3ca090a4cb94af167c45130986957/docs/img/begin.jpg -------------------------------------------------------------------------------- /docs/img/good.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acl-dev/open-coroutine/50ad55fd49e3ca090a4cb94af167c45130986957/docs/img/good.jpeg -------------------------------------------------------------------------------- /docs/img/just_do_it.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acl-dev/open-coroutine/50ad55fd49e3ca090a4cb94af167c45130986957/docs/img/just_do_it.jpg -------------------------------------------------------------------------------- /docs/img/run.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acl-dev/open-coroutine/50ad55fd49e3ca090a4cb94af167c45130986957/docs/img/run.jpg -------------------------------------------------------------------------------- /docs/img/rust.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acl-dev/open-coroutine/50ad55fd49e3ca090a4cb94af167c45130986957/docs/img/rust.jpeg -------------------------------------------------------------------------------- /docs/img/watching.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acl-dev/open-coroutine/50ad55fd49e3ca090a4cb94af167c45130986957/docs/img/watching.png -------------------------------------------------------------------------------- /docs/img/what_else_can_I_say.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acl-dev/open-coroutine/50ad55fd49e3ca090a4cb94af167c45130986957/docs/img/what_else_can_I_say.jpg -------------------------------------------------------------------------------- /hook/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "open-coroutine-hook" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | description = "The syscall hook for open-coroutine" 7 | repository.workspace = true 8 | keywords = ["open-coroutine", "hook", "syscall"] 9 | categories = ["os", "concurrency", "asynchronous"] 10 | license.workspace = true 11 | readme.workspace = true 12 | exclude.workspace = true 13 | 14 | [dependencies] 15 | once_cell.workspace = true 16 | open-coroutine-core.workspace = true 17 | 18 | [target.'cfg(unix)'.dependencies] 19 | libc.workspace = true 20 | 21 | [target.'cfg(windows)'.dependencies] 22 | windows-sys = { workspace = true, features = [ 23 | "Win32_Security", 24 | "Win32_Foundation", 25 | "Win32_System_Threading", 26 | "Win32_Storage_FileSystem", 27 | "Win32_System_LibraryLoader", 28 | "Win32_System_SystemServices", 29 | "Win32_System_Diagnostics_Debug" 30 | ] } 31 | minhook.workspace = true 32 | 33 | [features] 34 | default = ["open-coroutine-core/default"] 35 | 36 | # Print some help log. 37 | # Enable for default. 38 | log = ["open-coroutine-core/log"] 39 | 40 | # This feature only used in open-coroutine inner, don't use it in your project. 41 | ci = ["open-coroutine-core/ci"] 42 | 43 | # Provide preemptive scheduling implementation. 44 | # Enable for default. 45 | preemptive = ["open-coroutine-core/preemptive"] 46 | 47 | # Provide net API abstraction and implementation. 48 | net = ["open-coroutine-core/net"] 49 | 50 | # Provide io_uring adaptation, this feature only works in linux. 51 | io_uring = ["open-coroutine-core/io_uring"] 52 | 53 | # Provide IOCP adaptation, this feature only works in windows. 54 | iocp = ["open-coroutine-core/iocp"] 55 | 56 | # Provide completion IO adaptation 57 | completion_io = ["open-coroutine-core/completion_io"] 58 | 59 | # Provide syscall implementation. 60 | syscall = ["open-coroutine-core/syscall"] 61 | 62 | [lib] 63 | crate-type = ["cdylib"] 64 | -------------------------------------------------------------------------------- /hook/docs/cn/hook.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hook总览 3 | date: 2025-01-20 10:00:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # Hook总览 8 | 9 | [English](../en/hook.md) | 中文 10 | 11 | ## 为什么hook? 12 | 13 | 在`Coroutine::resume_with`之后,一个协程可能会长时间占用调度线程(例如,陷入重度计算或系统调用),从而拖慢被该线程调度的其他协程。为了解决陷入系统调用的问题,我们引入hook机制,这样当协程进入系统调用时,它会被自动挂起,从而让其他协程执行。 14 | 15 | 这带来了一个新问题,`preemptive`特性会`发送大量信号`,而`信号会中断正在执行的系统调用`。此外,由于大多数用户在代码中未处理信号,因此如果他们直接使用`open-routine-core`并且启用`preemptive`特性将导致`灾难性后果`。 16 | 17 | ## 什么是hook? 18 | 19 | Hook可以通过在运行时插入自定义代码来修改或扩展现有代码的行为,甚至可以监控、拦截、修改和重定向系统调用。现在,让我们用一个[例子](https://github.com/loongs-zhang/link-example)来直观地体验它。 20 | 21 | 假设我们有以下测试代码: 22 | 23 | ```rust 24 | use std::time::{Duration, Instant}; 25 | 26 | #[test] 27 | fn test_hook() { 28 | let start = Instant::now(); 29 | std::thread::sleep(Duration::MAX); 30 | let cost = Instant::now().duration_since(start); 31 | println!("cost: {:?}", cost); 32 | } 33 | ``` 34 | 35 | 如果我们不hook,因为`std::thread::sleep(Duration::MAX)`,这个测试几乎永远不会结束,但有了hook,我们可以在`不更改测试代码`的情况下将`nanosleep`系统调用重定向到[我们的自定义代码](https://github.com/loongs-zhang/link-example/blob/master/dep/src/lib.rs),然后测试就会[很快结束](https://github.com/loongs-zhang/link-example/actions/runs/12862762378/job/35858206179)。 36 | 37 |
38 | 39 |
40 | 41 | ## 工作原理 42 | 43 | ```mermaid 44 | sequenceDiagram 45 | Actor Your Project 46 | participant open-coroutine 47 | participant open-coroutine-hook 48 | participant open-coroutine-core 49 | 50 | Your Project ->> open-coroutine: depends on 51 | open-coroutine ->> open-coroutine-hook: depends on 52 | alt at compile time 53 | open-coroutine ->> open-coroutine: build open-coroutine-hook into dylib 54 | open-coroutine ->> open-coroutine: link open-coroutine-hook's dylib 55 | else runtime 56 | Your Project -->> Operation System: logic execute syscall 57 | alt what actually happened 58 | Your Project ->> open-coroutine-hook: redirect syscall to open-coroutine-hook's syscall mod 59 | open-coroutine-hook ->> open-coroutine-core: call open-coroutine-core's syscall mod 60 | open-coroutine-core ->> Operation System: execute fast syscall actually 61 | Operation System ->> open-coroutine-core: return syscall result and errno 62 | open-coroutine-core -->> Operation System: maybe execute fast syscall many times 63 | open-coroutine-core -->> open-coroutine-core: maybe modify syscall result or errno 64 | open-coroutine-core ->> open-coroutine-hook: return syscall result and errno 65 | open-coroutine-hook ->> Your Project: return syscall result and errno 66 | end 67 | Operation System ->> Your Project: return syscall result and errno 68 | end 69 | ``` 70 | -------------------------------------------------------------------------------- /hook/docs/en/hook.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hook Overview 3 | date: 2025-01-20 10:00:00 4 | author: loongs-zhang 5 | --- 6 | 7 | # Hook Overview 8 | 9 | English | [中文](../cn/hook.md) 10 | 11 | ## Why hook? 12 | 13 | After a `Coroutine::resume_with`, a coroutine may occupy the scheduling thread for a long time (e.g. getting stuck in 14 | heavy computing or syscall), thereby slowing down other coroutines scheduled by that scheduling thread. To solve the 15 | problem of getting stuck in syscall, we introduce hook, which automatically suspends coroutines that enter syscall and 16 | allow other coroutines to execute. 17 | 18 | This brings a new problem, the `preemptive` feature will send a large number of signals `which can interrupt the running 19 | syscall`. In addition, most user code does not handle signals, if they directly use `open-routine-core` and enabling the 20 | preemptive feature will lead to `catastrophic consequences`. 21 | 22 | ## What is hook? 23 | 24 | Hook can modify or extend the behavior of existing code by inserting custom code at runtime, and even monitor, 25 | intercept, modify, and redirect system calls. Now, let's use an [example](https://github.com/loongs-zhang/link-example) 26 | to visually experience it. 27 | 28 | Assuming we have the following test code: 29 | 30 | ```rust 31 | use std::time::{Duration, Instant}; 32 | 33 | #[test] 34 | fn test_hook() { 35 | let start = Instant::now(); 36 | std::thread::sleep(Duration::MAX); 37 | let cost = Instant::now().duration_since(start); 38 | println!("cost: {:?}", cost); 39 | } 40 | ``` 41 | 42 | If we don't hook, because `std::thread::sleep(Duration::MAX)`, this test almost never ends, but with hook, we redirect 43 | the `nanosleep` syscall 44 | to [our custom code](https://github.com/loongs-zhang/link-example/blob/master/dep/src/lib.rs) `without change the test 45 | code`, and then the test 46 | will [end soon](https://github.com/loongs-zhang/link-example/actions/runs/12862762378/job/35858206179). 47 | 48 |
49 | 50 |
51 | 52 | ## How it works 53 | 54 | ```mermaid 55 | sequenceDiagram 56 | Actor Your Project 57 | participant open-coroutine 58 | participant open-coroutine-hook 59 | participant open-coroutine-core 60 | 61 | Your Project ->> open-coroutine: depends on 62 | open-coroutine ->> open-coroutine-hook: depends on 63 | alt at compile time 64 | open-coroutine ->> open-coroutine: build open-coroutine-hook into dylib 65 | open-coroutine ->> open-coroutine: link open-coroutine-hook's dylib 66 | else runtime 67 | Your Project -->> Operation System: logic execute syscall 68 | alt what actually happened 69 | Your Project ->> open-coroutine-hook: redirect syscall to open-coroutine-hook's syscall mod 70 | open-coroutine-hook ->> open-coroutine-core: call open-coroutine-core's syscall mod 71 | open-coroutine-core ->> Operation System: execute fast syscall actually 72 | Operation System ->> open-coroutine-core: return syscall result and errno 73 | open-coroutine-core -->> Operation System: maybe execute fast syscall many times 74 | open-coroutine-core -->> open-coroutine-core: maybe modify syscall result or errno 75 | open-coroutine-core ->> open-coroutine-hook: return syscall result and errno 76 | open-coroutine-hook ->> Your Project: return syscall result and errno 77 | end 78 | Operation System ->> Your Project: return syscall result and errno 79 | end 80 | ``` 81 | -------------------------------------------------------------------------------- /hook/docs/img/result-on-macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acl-dev/open-coroutine/50ad55fd49e3ca090a4cb94af167c45130986957/hook/docs/img/result-on-macos.png -------------------------------------------------------------------------------- /hook/src/syscall/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(unix)] 2 | pub mod unix; 3 | 4 | #[allow(non_snake_case)] 5 | #[cfg(windows)] 6 | pub mod windows; 7 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "open-coroutine-macros" 3 | version.workspace = true 4 | edition.workspace = true 5 | description = "The proc macros for open-coroutine" 6 | repository.workspace = true 7 | keywords = ["open-coroutine", "macro"] 8 | categories = ["concurrency", "asynchronous", "os", "network-programming", "wasm"] 9 | license.workspace = true 10 | readme.workspace = true 11 | exclude.workspace = true 12 | 13 | [dependencies] 14 | syn = { workspace = true, features = ["full"] } 15 | quote.workspace = true 16 | 17 | [lib] 18 | proc-macro = true -------------------------------------------------------------------------------- /open-coroutine/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "open-coroutine" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors = ["zhangzicheng@apache.org"] 6 | description = "The open-coroutine is a simple, efficient and generic stackfull-coroutine library." 7 | repository = "https://github.com/acl-dev/open-coroutine" 8 | keywords = ["coroutine", "fiber", "stackfull", "hook"] 9 | categories = ["data-structures", "concurrency", "asynchronous", "web-programming", "wasm"] 10 | license.workspace = true 11 | readme.workspace = true 12 | exclude.workspace = true 13 | 14 | [dependencies] 15 | libc.workspace = true 16 | open-coroutine-core.workspace = true 17 | open-coroutine-hook.workspace = true 18 | open-coroutine-macros.workspace = true 19 | 20 | [target.'cfg(windows)'.dependencies] 21 | windows-sys = { workspace = true, features = [ 22 | "Win32_Foundation", 23 | "Win32_System_Kernel", 24 | "Win32_System_Threading", 25 | "Win32_System_SystemInformation", 26 | "Win32_System_Diagnostics_Debug", 27 | ] } 28 | 29 | [build-dependencies] 30 | tracing = { workspace = true, default-features = false } 31 | tracing-subscriber = { workspace = true, features = [ 32 | "fmt", 33 | "local-time" 34 | ], default-features = false } 35 | tracing-appender.workspace = true 36 | time.workspace = true 37 | cargo_metadata.workspace = true 38 | 39 | [dev-dependencies] 40 | tempfile.workspace = true 41 | cfg-if.workspace = true 42 | 43 | [features] 44 | default = ["open-coroutine-hook/default", "open-coroutine-core/default"] 45 | 46 | # Print some help log. 47 | # Enable for default. 48 | log = ["open-coroutine-hook/log", "open-coroutine-core/log"] 49 | 50 | # This feature only used in open-coroutine inner, don't use it in your project. 51 | ci = ["open-coroutine-hook/ci", "open-coroutine-core/ci"] 52 | 53 | # Provide preemptive scheduling implementation. 54 | # Enable for default. 55 | preemptive = ["open-coroutine-hook/preemptive", "open-coroutine-core/preemptive"] 56 | 57 | # Provide net API abstraction and implementation. 58 | net = ["open-coroutine-hook/net", "open-coroutine-core/net"] 59 | 60 | # Provide io_uring abstraction and implementation. 61 | # This feature only works in linux. 62 | io_uring = ["open-coroutine-hook/io_uring", "open-coroutine-core/io_uring"] 63 | 64 | # Provide IOCP adaptation, this feature only works in windows. 65 | iocp = ["open-coroutine-hook/iocp", "open-coroutine-core/iocp"] 66 | 67 | # Provide completion IO adaptation 68 | completion_io = ["open-coroutine-hook/completion_io", "open-coroutine-core/completion_io"] 69 | 70 | # Provide syscall implementation. 71 | syscall = ["open-coroutine-hook/syscall", "open-coroutine-core/syscall"] 72 | -------------------------------------------------------------------------------- /open-coroutine/examples/file_co.rs: -------------------------------------------------------------------------------- 1 | use open_coroutine::{task, JoinHandle}; 2 | use std::fs::File; 3 | use std::io::{Error, IoSlice, IoSliceMut, Read, Result, Seek, SeekFrom, Write}; 4 | use std::time::Duration; 5 | 6 | #[open_coroutine::main(event_loop_size = 1, max_size = 1)] 7 | pub fn main() -> Result<()> { 8 | let join_handle: JoinHandle> = task!( 9 | |_| { 10 | const HELLO: &str = "Hello World!"; 11 | 12 | // Write 13 | let mut tmpfile: File = tempfile::tempfile()?; 14 | assert_eq!(HELLO.len(), tmpfile.write(HELLO.as_ref())?); 15 | // Seek to start 16 | tmpfile.seek(SeekFrom::Start(0))?; 17 | // Read 18 | let mut buf = String::new(); 19 | assert_eq!(HELLO.len(), tmpfile.read_to_string(&mut buf)?); 20 | assert_eq!(HELLO, buf); 21 | 22 | // Seek to start 23 | tmpfile.seek(SeekFrom::Start(0))?; 24 | 25 | // Write multiple 26 | let ioslices = [IoSlice::new(HELLO.as_ref()), IoSlice::new(HELLO.as_ref())]; 27 | assert_eq!(HELLO.len() * 2, tmpfile.write_vectored(&ioslices)?); 28 | // Seek to start 29 | tmpfile.seek(SeekFrom::Start(0))?; 30 | // Read multiple 31 | let mut buf1 = [0; HELLO.len()]; 32 | let mut buf2 = [0; HELLO.len()]; 33 | let mut ioslicemuts = [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)]; 34 | assert_eq!(HELLO.len() * 2, tmpfile.read_vectored(&mut ioslicemuts)?); 35 | assert_eq!(HELLO, unsafe { std::str::from_utf8_unchecked(&mut buf1) }); 36 | assert_eq!(HELLO, unsafe { std::str::from_utf8_unchecked(&mut buf2) }); 37 | 38 | Ok(()) 39 | }, 40 | () 41 | ); 42 | if let Some(r) = join_handle.timeout_join(Duration::from_secs(30))? { 43 | return r; 44 | } 45 | Err(Error::other("Failed to join the task")) 46 | } 47 | -------------------------------------------------------------------------------- /open-coroutine/examples/file_not_co.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; 3 | 4 | #[open_coroutine::main(event_loop_size = 1, max_size = 1)] 5 | pub fn main() -> std::io::Result<()> { 6 | const HELLO: &str = "Hello World!"; 7 | 8 | // Write 9 | let mut tmpfile: File = tempfile::tempfile()?; 10 | assert_eq!(HELLO.len(), tmpfile.write(HELLO.as_ref())?); 11 | // Seek to start 12 | tmpfile.seek(SeekFrom::Start(0))?; 13 | // Read 14 | let mut buf = String::new(); 15 | assert_eq!(HELLO.len(), tmpfile.read_to_string(&mut buf)?); 16 | assert_eq!(HELLO, buf); 17 | 18 | // Seek to start 19 | tmpfile.seek(SeekFrom::Start(0))?; 20 | 21 | // Write multiple 22 | let ioslices = [IoSlice::new(HELLO.as_ref()), IoSlice::new(HELLO.as_ref())]; 23 | assert_eq!(HELLO.len() * 2, tmpfile.write_vectored(&ioslices)?); 24 | // Seek to start 25 | tmpfile.seek(SeekFrom::Start(0))?; 26 | // Read multiple 27 | let mut buf1 = [0; HELLO.len()]; 28 | let mut buf2 = [0; HELLO.len()]; 29 | let mut ioslicemuts = [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)]; 30 | assert_eq!(HELLO.len() * 2, tmpfile.read_vectored(&mut ioslicemuts)?); 31 | assert_eq!(HELLO, unsafe { std::str::from_utf8_unchecked(&mut buf1) }); 32 | assert_eq!(HELLO, unsafe { std::str::from_utf8_unchecked(&mut buf2) }); 33 | 34 | Ok(()) 35 | } 36 | -------------------------------------------------------------------------------- /open-coroutine/examples/preemptive.rs: -------------------------------------------------------------------------------- 1 | /// outputs: 2 | /// ``` 3 | /// coroutine1 launched 4 | /// loop1 5 | /// coroutine2 launched 6 | /// loop2 7 | /// coroutine3 launched 8 | /// loop1 9 | /// loop2 end 10 | /// loop1 end 11 | /// ``` 12 | #[open_coroutine::main(event_loop_size = 1, max_size = 3)] 13 | pub fn main() -> std::io::Result<()> { 14 | cfg_if::cfg_if! { 15 | if #[cfg(all(unix, feature = "preemptive"))] { 16 | use open_coroutine::task; 17 | use std::sync::{Arc, Condvar, Mutex}; 18 | use std::time::Duration; 19 | 20 | static mut TEST_FLAG1: bool = true; 21 | static mut TEST_FLAG2: bool = true; 22 | let pair = Arc::new((Mutex::new(true), Condvar::new())); 23 | let pair2 = Arc::clone(&pair); 24 | let handler = std::thread::Builder::new() 25 | .name("preemptive".to_string()) 26 | .spawn(move || { 27 | let join = task!( 28 | |_| { 29 | println!("coroutine1 launched"); 30 | while unsafe { TEST_FLAG1 } { 31 | println!("loop1"); 32 | _ = unsafe { libc::usleep(10_000) }; 33 | } 34 | println!("loop1 end"); 35 | }, 36 | (), 37 | ); 38 | _ = task!( 39 | |_| { 40 | println!("coroutine2 launched"); 41 | while unsafe { TEST_FLAG2 } { 42 | println!("loop2"); 43 | _ = unsafe { libc::usleep(10_000) }; 44 | } 45 | println!("loop2 end"); 46 | unsafe { TEST_FLAG1 = false }; 47 | }, 48 | (), 49 | ); 50 | _ = task!( 51 | |_| { 52 | println!("coroutine3 launched"); 53 | unsafe { TEST_FLAG2 = false }; 54 | }, 55 | (), 56 | ); 57 | join.timeout_join(Duration::from_millis(3000)).expect("join failed"); 58 | 59 | let (lock, cvar) = &*pair2; 60 | let mut pending = lock.lock().unwrap(); 61 | *pending = false; 62 | // notify the condvar that the value has changed. 63 | cvar.notify_one(); 64 | }) 65 | .expect("failed to spawn thread"); 66 | 67 | // wait for the thread to start up 68 | let (lock, cvar) = &*pair; 69 | let result = cvar 70 | .wait_timeout_while( 71 | lock.lock().unwrap(), 72 | Duration::from_millis(3000), 73 | |&mut pending| pending, 74 | ) 75 | .unwrap(); 76 | if result.1.timed_out() { 77 | Err(std::io::Error::other( 78 | "preemptive schedule failed", 79 | )) 80 | } else { 81 | unsafe { 82 | handler.join().unwrap(); 83 | assert!(!TEST_FLAG1); 84 | } 85 | Ok(()) 86 | } 87 | } else { 88 | println!("please enable preemptive feature"); 89 | Ok(()) 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /open-coroutine/examples/scalable_stack.rs: -------------------------------------------------------------------------------- 1 | use open_coroutine::{maybe_grow, task}; 2 | 3 | #[open_coroutine::main(event_loop_size = 1, max_size = 1)] 4 | pub fn main() { 5 | let join = task!( 6 | |_| { 7 | fn recurse(i: u32, p: &mut [u8; 10240]) { 8 | maybe_grow!(|| { 9 | // Ensure the stack allocation isn't optimized away. 10 | unsafe { _ = std::ptr::read_volatile(&p) }; 11 | if i > 0 { 12 | recurse(i - 1, &mut [0; 10240]); 13 | } 14 | }) 15 | .expect("allocate stack failed") 16 | } 17 | println!("[coroutine] launched"); 18 | // Use ~500KB of stack. 19 | recurse(50, &mut [0; 10240]); 20 | // Use ~500KB of stack. 21 | recurse(50, &mut [0; 10240]); 22 | println!("[coroutine] exited"); 23 | }, 24 | (), 25 | ); 26 | assert_eq!(Some(()), join.join().expect("join failed")); 27 | } 28 | -------------------------------------------------------------------------------- /open-coroutine/examples/sleep_co.rs: -------------------------------------------------------------------------------- 1 | use open_coroutine::task; 2 | use open_coroutine_core::common::now; 3 | 4 | pub fn sleep_test_co(millis: u64) { 5 | _ = task!( 6 | move |_| { 7 | let start = now(); 8 | #[cfg(unix)] 9 | std::thread::sleep(std::time::Duration::from_millis(millis)); 10 | #[cfg(windows)] 11 | unsafe { 12 | windows_sys::Win32::System::Threading::Sleep(millis as u32); 13 | } 14 | let end = now(); 15 | assert!(end - start >= millis, "Time consumption less than expected"); 16 | println!("[coroutine1] {millis} launched"); 17 | }, 18 | (), 19 | ); 20 | _ = task!( 21 | move |_| { 22 | #[cfg(unix)] 23 | std::thread::sleep(std::time::Duration::from_millis(500)); 24 | #[cfg(windows)] 25 | unsafe { 26 | windows_sys::Win32::System::Threading::Sleep(500); 27 | } 28 | println!("[coroutine2] {millis} launched"); 29 | }, 30 | (), 31 | ); 32 | #[cfg(unix)] 33 | std::thread::sleep(std::time::Duration::from_millis(millis + 500)); 34 | #[cfg(windows)] 35 | unsafe { 36 | windows_sys::Win32::System::Threading::Sleep((millis + 500) as u32); 37 | } 38 | } 39 | 40 | #[open_coroutine::main(event_loop_size = 1, max_size = 2)] 41 | pub fn main() { 42 | sleep_test_co(1); 43 | sleep_test_co(1000); 44 | } 45 | -------------------------------------------------------------------------------- /open-coroutine/examples/sleep_not_co.rs: -------------------------------------------------------------------------------- 1 | use open_coroutine::task; 2 | use open_coroutine_core::common::now; 3 | 4 | fn sleep_test(millis: u64) { 5 | _ = task!( 6 | move |_| { 7 | println!("[coroutine1] {millis} launched"); 8 | }, 9 | (), 10 | ); 11 | _ = task!( 12 | move |_| { 13 | println!("[coroutine2] {millis} launched"); 14 | }, 15 | (), 16 | ); 17 | let start = now(); 18 | #[cfg(unix)] 19 | std::thread::sleep(std::time::Duration::from_millis(millis)); 20 | #[cfg(windows)] 21 | unsafe { 22 | windows_sys::Win32::System::Threading::Sleep(millis as u32); 23 | } 24 | let end = now(); 25 | assert!(end - start >= millis, "Time consumption less than expected"); 26 | } 27 | 28 | #[open_coroutine::main(event_loop_size = 1, max_size = 2)] 29 | pub fn main() { 30 | sleep_test(1); 31 | sleep_test(1000); 32 | } 33 | -------------------------------------------------------------------------------- /open-coroutine/tests/file_co.rs: -------------------------------------------------------------------------------- 1 | include!("../examples/file_co.rs"); 2 | 3 | // The implementation of rust std is inconsistent between unix and windows. 4 | #[cfg(not(windows))] 5 | #[test] 6 | fn file_co() -> Result<()> { 7 | main() 8 | } 9 | -------------------------------------------------------------------------------- /open-coroutine/tests/file_not_co.rs: -------------------------------------------------------------------------------- 1 | include!("../examples/file_not_co.rs"); 2 | 3 | // The implementation of rust std is inconsistent between unix and windows. 4 | #[cfg(not(windows))] 5 | #[test] 6 | fn file_not_co() -> std::io::Result<()> { 7 | main() 8 | } 9 | -------------------------------------------------------------------------------- /open-coroutine/tests/preemptive.rs: -------------------------------------------------------------------------------- 1 | include!("../examples/preemptive.rs"); 2 | 3 | #[cfg(not(windows))] 4 | #[test] 5 | fn preemptive() -> std::io::Result<()> { 6 | main() 7 | } 8 | -------------------------------------------------------------------------------- /open-coroutine/tests/scalable_stack.rs: -------------------------------------------------------------------------------- 1 | include!("../examples/scalable_stack.rs"); 2 | 3 | #[test] 4 | fn scalable_stack() { 5 | main(); 6 | } 7 | -------------------------------------------------------------------------------- /open-coroutine/tests/sleep_co.rs: -------------------------------------------------------------------------------- 1 | include!("../examples/sleep_co.rs"); 2 | 3 | #[test] 4 | fn sleep_co() { 5 | main(); 6 | } 7 | -------------------------------------------------------------------------------- /open-coroutine/tests/sleep_not_co.rs: -------------------------------------------------------------------------------- 1 | include!("../examples/sleep_not_co.rs"); 2 | 3 | #[test] 4 | fn sleep_not_co() { 5 | main(); 6 | } 7 | -------------------------------------------------------------------------------- /open-coroutine/tests/socket_co.rs: -------------------------------------------------------------------------------- 1 | include!("../examples/socket_co.rs"); 2 | 3 | #[test] 4 | fn socket_co() -> std::io::Result<()> { 5 | main() 6 | } 7 | -------------------------------------------------------------------------------- /open-coroutine/tests/socket_co_client.rs: -------------------------------------------------------------------------------- 1 | include!("../examples/socket_co_client.rs"); 2 | 3 | #[test] 4 | fn socket_co_client() -> std::io::Result<()> { 5 | main() 6 | } 7 | -------------------------------------------------------------------------------- /open-coroutine/tests/socket_co_server.rs: -------------------------------------------------------------------------------- 1 | include!("../examples/socket_co_server.rs"); 2 | 3 | #[test] 4 | fn socket_co_server() -> std::io::Result<()> { 5 | main() 6 | } 7 | -------------------------------------------------------------------------------- /open-coroutine/tests/socket_not_co.rs: -------------------------------------------------------------------------------- 1 | include!("../examples/socket_not_co.rs"); 2 | 3 | #[test] 4 | fn socket_not_co() -> std::io::Result<()> { 5 | main() 6 | } 7 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd core 4 | cargo publish --registry crates-io 5 | 6 | cd ../hook 7 | cargo publish --registry crates-io 8 | 9 | cd ../macros 10 | cargo publish --registry crates-io 11 | 12 | cd ../open-coroutine 13 | cargo publish --registry crates-io 14 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | reorder_imports = true 3 | --------------------------------------------------------------------------------