├── .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 | [](https://crates.io/crates/open-coroutine)
12 | [](https://docs.rs/open-coroutine)
13 | [](https://github.com/acl-dev/open-coroutine/blob/master/LICENSE-APACHE)
14 | [](https://github.com/acl-dev/open-coroutine/actions)
15 | [](https://codecov.io/github/acl-dev/open-coroutine)
16 | [](http://isitmaintained.com/project/acl-dev/open-coroutine "Average time to resolve an issue")
17 | [](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 | [](https://crates.io/crates/open-coroutine)
12 | [](https://docs.rs/open-coroutine)
13 | [](https://github.com/acl-dev/open-coroutine/blob/master/LICENSE-APACHE)
14 | [](https://github.com/acl-dev/open-coroutine/actions)
15 | [](https://codecov.io/github/acl-dev/open-coroutine)
16 | [](http://isitmaintained.com/project/acl-dev/open-coroutine "Average time to resolve an issue")
17 | [](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 |
--------------------------------------------------------------------------------