1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# SPDX-License-Identifier: Apache-2.0
name: CI
on:
push:
branches:
pull_request:
branches:
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
# ── Standard build + test ──────────────────────────────────────────────────
test:
name: Build & Test
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # always run all platforms; don't cancel Windows when macOS fails
matrix:
os:
toolchain:
continue-on-error: false
steps:
- uses: actions/checkout@v4
- name: Update tool chain
run: |
rustup update ${{ matrix.toolchain }}
rustup default ${{ matrix.toolchain }}
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: cargo test
# --nocapture: print test output immediately so crash context is
# visible in the job log even when the process exits abnormally
# (e.g. STATUS_ACCESS_VIOLATION on Windows).
# timeout-minutes: guard against a run_impl park() hang on slow runners.
timeout-minutes: 10
run: cargo test --release -- --nocapture
- name: Print Windows VEH crash file (if present)
# always(): the crash file matters most when `cargo test` just failed,
# which would otherwise skip this step entirely.
if: always() && runner.os == 'Windows'
shell: pwsh
# The VEH writes .\go-lib-crash-veh.txt on crash. Print it so the
# diagnostics appear in the CI log even when the test binary exits via
# TerminateProcess (which may race with pipe drain).
run: |
if (Test-Path "go-lib-crash-veh.txt") {
Write-Host "=== go-lib-crash-veh.txt ==="
Get-Content "go-lib-crash-veh.txt"
} else {
Write-Host "(no crash file — process either did not crash or VEH was not reached)"
}
- name: cargo run --examples
timeout-minutes: 10
# bash on all three platforms so the inverted exit-code check below
# behaves identically (the default Windows shell is pwsh).
shell: bash
run: |
cargo run --release --example attr_run
cargo run --release --example cond
cargo run --release --example hello
# main_exitcode exits 1 BY DESIGN (it demonstrates returning
# ExitCode::FAILURE from main). Assert the non-zero exit instead
# of failing the job on it.
if cargo run --release --example main_exitcode; then
echo "main_exitcode: expected a non-zero exit code"; exit 1
fi
cargo run --release --example main_result
cargo run --release --example pipeline
cargo run --release --example scope
cargo run --release --example scope_channel
cargo run --release --example select_fanin
- name: stress canary (drain/wake races)
# Regression canary for the cross-Rt drain/wake races (timer-heap
# TOCTOU, netpoll harvest, nested RcuGuard deadlock, stale timer
# entries — see examples/stress_drain_timer.rs). On the buggy
# runtime this crashes (malloc abort / NULL deref in chanrecv) or
# hangs within seconds; the job timeout converts a hang into a
# failure. With the singleton scheduler each run_impl leaks only a
# 16-byte InvState, so the canary can run tens of thousands of
# iterations in its 15-second window; the per-worker cap just bounds
# runtime on very fast runners.
timeout-minutes: 3
shell: bash
run: cargo run --release --example stress_drain_timer 15 100000
- name: cargo doc (no deps, no-run)
run: cargo doc --no-deps
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Update tool chain
run: |
rustup update
rustup default
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: cargo build
run: cargo build --all-targets
# ── Loom concurrency model checker ────────────────────────────────────────
loom:
name: Loom model check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Update tool chain
run: |
rustup update
rustup default
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-loom-${{ hashFiles('**/Cargo.lock') }}
- name: cargo test (loom)
# --cfg loom: activate loom::sync shims in crate::loom_shim.
# -- --test-threads 1: loom's model explorer must run serially.
# LOOM_MAX_PERMUTATIONS: cap the search so CI stays bounded;
# increase locally with LOOM_MAX_PERMUTATIONS=0 (unlimited) for
# deeper exploration.
env:
RUSTFLAGS: "--cfg loom"
LOOM_MAX_PERMUTATIONS: "10000"
# --nocapture: surface the async-signal-safe SIGSEGV handler dump
# in the job log if a test crashes (otherwise libtest swallows it).
run: cargo test -- --test-threads 1 --nocapture
coverage:
name: Coverage
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
toolchain:
steps:
- uses: actions/checkout@v4
- name: Update tool chain
run: |
rustup update ${{ matrix.toolchain }}
rustup default ${{ matrix.toolchain }}
rustup component add llvm-tools-preview --toolchain ${{ matrix.toolchain }}
- name: Install coverage tool
run: cargo +stable install cargo-llvm-cov --locked
- name: Test project and generate coverage report
run: cargo llvm-cov --remap-path-prefix --show-missing-lines --branch --doctests --lcov --output-path lcov.info
- name: Publish coverage summary to job summary
uses: livewing/lcov-job-summary@v1.1.0
with:
lcov: lcov.info
- name: Fail build if coverage below 80%
uses: bigmeech/gha-simple-coverage@master
with:
lcov-file-path: lcov.info
fail-if-below: 80