Skip to content

Signal handling: CRuby-compatible SignalException conversion + SIG_DFL re-raise #3623

Signal handling: CRuby-compatible SignalException conversion + SIG_DFL re-raise

Signal handling: CRuby-compatible SignalException conversion + SIG_DFL re-raise #3623

Workflow file for this run

name: Rust
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
amd64:
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "4.0.2"
- uses: dtolnay/rust-toolchain@nightly
with:
toolchain: nightly
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: taiki-e/install-action@nextest
- name: Clone optcarrot
run: git clone https://github.com/mame/optcarrot.git ../optcarrot
- name: Clone ruby/spec and mspec (pinned)
# Pinned to fixed commits so upstream ruby/spec changes can't break CI.
# `bin/test` runs these specs under `set -e`, so a single newly added
# upstream spec that monoruby doesn't pass yet would fail the whole run.
# Bump these SHAs deliberately when adopting newer specs.
env:
SPEC_REV: baf5738ada1709afdbcf222c7609d3379b9d211d
MSPEC_REV: dffcdf7d00612c13465983224203e457a30cf124
run: |
git init ../spec
git -C ../spec fetch --depth 1 https://github.com/ruby/spec.git "$SPEC_REV"
git -C ../spec checkout FETCH_HEAD
git init ../mspec
git -C ../mspec fetch --depth 1 https://github.com/ruby/mspec.git "$MSPEC_REV"
git -C ../mspec checkout FETCH_HEAD
- name: Clone ruby-bench
run: git clone --depth 1 https://github.com/ruby/ruby-bench.git ../ruby-bench
- name: Install ruby-bench gem dependencies
# bin/ruby-bench-diff requires `erubi`. The other benchmarks
# only use stdlib gems, so a single explicit install is enough.
run: gem install erubi --no-document
- name: Test
run: bin/test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
# Coverage upload is a non-essential, externally-dependent step: the
# Codecov CLI download/GPG-signature verification fails intermittently
# on Codecov's side (e.g. "Could not verify signature. No public key"),
# which must not fail the whole CI run when the tests themselves pass.
continue-on-error: true
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
files: lcov.info
fail_ci_if_error: false
darwin:
# Native Apple Silicon (arm64-apple-darwin) runner. build.rs sets the `jit`
# cfg on arm64 (only `jit_x86` is x86-only), so this builds and runs the
# AArch64 JIT (AsmIR→A64 backend, asmir/compile_stub.rs) — instructions not
# yet ported bail to the VM. It runs the same `bin/test` as the Linux
# `amd64` job (portable head/yjit handling makes it BSD/macOS-safe), so
# the arm64 VM + JIT get the full check scope (unit tests, benchmark diffs,
# optcarrot, ruby-bench, ruby/spec).
#
# SKIP_COV=1: this job does NOT collect coverage. On the GitHub M1 runner,
# llvm-cov coverage instrumentation itself trips a latent VM-layer memory
# bug (a value is freed mid-flight -> String INVALID(255) -> SIGABRT). It is
# independent of the JIT (`--no-jit` crashes the same way) and does not
# reproduce on x86-64, qemu-aarch64, or a local Apple Silicon Mac — so it is
# specific to native-M1 + coverage instrumentation and is tracked
# separately. The Linux `amd64` job owns the Codecov upload.
runs-on: macos-latest
# Backstop: if a hung attempt somehow escapes the per-attempt 25-min cap on
# the Test step below, GitHub cancels the job cleanly here (2 attempts ×
# 25 min + setup/clone overhead) rather than letting the runner die with
# "lost communication".
timeout-minutes: 70
env:
CARGO_TERM_COLOR: always
SKIP_COV: "1"
steps:
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "4.0.2"
- name: Install bigdecimal (no longer a default gem since Ruby 3.4)
run: gem install bigdecimal --no-document
- name: Install ruby-bench gem dependencies
# bin/ruby-bench-diff requires `erubi`; matches the `coverage` job.
run: gem install erubi --no-document
- name: Install libffi + pkg-config
# The aarch64-macOS dep block in monoruby/Cargo.toml builds libffi-sys
# with the `system` feature; libffi is keg-only so pkg-config needs its
# prefix on PKG_CONFIG_PATH to find it.
run: |
brew install libffi pkg-config
echo "PKG_CONFIG_PATH=$(brew --prefix libffi)/lib/pkgconfig" >> "$GITHUB_ENV"
- uses: dtolnay/rust-toolchain@nightly
with:
toolchain: nightly
- uses: taiki-e/install-action@nextest
- name: Clone optcarrot
run: git clone https://github.com/mame/optcarrot.git ../optcarrot
- name: Clone ruby/spec and mspec (pinned)
# Pinned to fixed commits so upstream ruby/spec changes can't break CI.
# Keep these SHAs in sync with the `amd64` job above; bump deliberately.
env:
SPEC_REV: baf5738ada1709afdbcf222c7609d3379b9d211d
MSPEC_REV: dffcdf7d00612c13465983224203e457a30cf124
run: |
git init ../spec
git -C ../spec fetch --depth 1 https://github.com/ruby/spec.git "$SPEC_REV"
git -C ../spec checkout FETCH_HEAD
git init ../mspec
git -C ../mspec fetch --depth 1 https://github.com/ruby/mspec.git "$MSPEC_REV"
git -C ../mspec checkout FETCH_HEAD
- name: Clone ruby-bench
run: git clone --depth 1 https://github.com/ruby/ruby-bench.git ../ruby-bench
- name: Test (VM + AArch64 JIT, arm64-apple-darwin)
# The arm64 JIT/runtime intermittently (~a few % of runs) hangs by
# exhausting memory: the macOS runner OOM-dies ("loses communication")
# before any stack can be captured, and on-demand reproduction under
# instrumentation has so far failed (nextest ×5 and full bin/test ×8
# both ran clean). Two guards:
# * retry — cap each attempt at 25 min and retry once, so a hang fails
# fast (normal pass ~11 min) and the rerun recovers automatically; and
# * a background memory watchdog that NAMES the culprit the next time
# the hang strikes a real run: it polls every monoruby process's RSS
# and, when one exceeds ~4 GB (or the parallel sum exceeds ~5 GB),
# logs that process's command line (the nextest test / benchmark /
# spec it is running), `sample`s its stack, and kills it before the
# runner dies. It only acts above 4 GB, so normal runs are
# unaffected and the retry recovers the killed attempt.
# The manual .github/workflows/darwin-hang-debug.yml drives the same
# watchdog in a loop for active reproduction.
uses: nick-fields/retry@v3
with:
timeout_minutes: 25
max_attempts: 2
command: |
RSS_LIMIT_KB=4000000
SUM_LIMIT_KB=5000000
( while true; do
snap=$(ps -Ao pid,rss,comm | awk 'NR>1 { n=split($3,a,"/"); b=a[n]; if (b ~ /^monoruby/) print $1, $2 }')
if [ -n "$snap" ]; then
sum=$(echo "$snap" | awk '{s+=$2} END{print s+0}')
set -- $(echo "$snap" | awk '{if($2>m){m=$2;p=$1}} END{print p+0, m+0}')
if [ "${2:-0}" -gt "$RSS_LIMIT_KB" ] || [ "${sum:-0}" -gt "$SUM_LIMIT_KB" ]; then
echo "==================== MEMORY WATCHDOG fired @ $(date -u +%H:%M:%S) ===================="
echo "sum_rss_kb=$sum top_pid=$1 top_rss_kb=$2 (caps: per-proc=$RSS_LIMIT_KB sum=$SUM_LIMIT_KB)"
ps -Ao pid,rss,command | awk 'NR>1 { n=split($3,a,"/"); b=a[n]; if (b ~ /^monoruby/) print }'
echo "---- CULPRIT command line (pid $1) ----"; ps -p "$1" -o command= 2>/dev/null || true
echo "---- sample pid $1 (2s) ----"; /usr/bin/sample "$1" 2 2>&1 | sed -n '1,160p' || true
echo "---- killing pid $1 ----"; kill -9 "$1" 2>/dev/null || true
fi
fi
sleep 4
done ) &
WATCHDOG_PID=$!
trap 'kill "$WATCHDOG_PID" 2>/dev/null || true' EXIT
bin/test