Skip to content

Conversation

@sgerbino
Copy link
Collaborator

@sgerbino sgerbino commented Jan 21, 2026

Replace signal() with sigaction() and add signal flags support

Replace the POSIX signal() implementation with sigaction() and add
support for signal flags (SA_RESTART, SA_NOCLDSTOP, etc.) while
keeping OS-specific headers out of the public API.

Public API changes:

  • Add flags_t enum directly to signal_set with abstract bit values
  • Add signal_set::add(int, flags_t) overload
  • Existing add(int) becomes inline convenience calling add(signal, none)
  • Add bitwise operators for flag manipulation (|, &, |=, &=, ~)

Available flags: none, restart, no_child_stop, no_child_wait, no_defer,
reset_handler, dont_care

POSIX implementation (posix/signals.cpp):

  • Replace signal() with sigaction() for robust signal handling
  • Add flags_supported() to validate flags available on this platform
  • Add to_sigaction_flags() to map abstract flags to SA_* constants
  • Add flag conflict detection for multiple signal_sets on same signal
  • Remove handler re-registration (not needed with sigaction)
  • Track registered flags in global signal_state for cross-set validation
  • Return operation_not_supported if SA_NOCLDWAIT unavailable
  • Document async-signal-safety limitation in signal handler

Windows implementation (win/signals.cpp):

  • Only none and dont_care flags are supported
  • Other flags return operation_not_supported

Testing: Cross-platform tests for none/dont_care, POSIX-only tests for
actual flag behavior, Windows-only test for operation_not_supported.

Resolves #36.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added signal flags support enabling fine-grained control over signal behavior including restart semantics and child process handling options.
    • Enhanced documentation with practical examples for signal registration and cross-platform compatibility guidance.
  • Tests

    • Expanded test suite with comprehensive cross-platform coverage for signal flags functionality.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 21, 2026

📝 Walkthrough

Walkthrough

This PR implements POSIX signal flags support for corosio::signal_set via sigaction(), introducing a new flags_t enum with options like restart and no_child_stop, adding an overloaded add(int, flags_t) method, and enforcing cross-instance flag compatibility. Windows support accepts but ignores flags. Documentation and comprehensive tests are included.

Changes

Cohort / File(s) Summary
Public API
include/boost/corosio/signal_set.hpp
Added flags_t enum with 7 flag values and bitwise operators (|, &, |=, &=, ~). Extended signal_set::add() with flags overload; existing add(int) delegates to add(int, none).
POSIX Implementation
src/corosio/src/detail/posix/signals.hpp, src/corosio/src/detail/posix/signals.cpp
Replaced signal() with sigaction() for handler installation. Added per-signal flags tracking (registered_flags[]), flag validation (flags_supported()), POSIX constant mapping (to_sigaction_flags()), and cross-set compatibility checks (flags_compatible()). Updated add_signal() and related signatures to propagate flags.
Windows Implementation
src/corosio/src/detail/win/signals.hpp, src/corosio/src/detail/win/signals.cpp
Extended add_signal() and win_signal_impl::add() to accept flags; validates only none and dont_care are supported, returning operation_not_supported for others.
Documentation
doc/modules/ROOT/pages/guide/signals.adoc
Added comprehensive flag semantics, examples for registering signals with flags, cross-set compatibility rules, Windows limitations, and new "Child Process Management (POSIX)" section with no_child_stop/no_child_wait example.
Tests
test/unit/signal_set.cpp
Added 13 new test methods covering flag bitwise operations, adding signals with various flag combinations, same-signal re-registration with compatible/incompatible flags, cross-set interactions, async_wait with flags, and platform-specific Windows error handling.

Sequence Diagram

sequenceDiagram
    participant Caller as Application Code
    participant API as signal_set::add<br/>(int, flags_t)
    participant Validate as flags_supported<br/>flags_compatible
    participant State as signal_state<br/>registered_flags
    participant Register as posix_signals<br/>::add_signal
    participant SA as sigaction()<br/>kernel
    
    Caller->>API: add(SIGTERM, restart)
    API->>Validate: flags_supported(restart)
    alt Flags invalid
        Validate-->>API: false
        API-->>Caller: error
    end
    
    API->>State: Check registered_flags[SIGTERM]
    alt First registration
        State-->>API: (empty/none)
    else Re-registration
        State->>Validate: flags_compatible(existing, restart)
        alt Incompatible flags
            Validate-->>API: false
            API-->>Caller: error
        end
    end
    
    API->>Register: add_signal(impl, SIGTERM, restart)
    Register->>Validate: to_sigaction_flags(restart)
    Validate-->>Register: SA_RESTART
    Register->>SA: sigaction(SIGTERM, &sa{flags=SA_RESTART})
    SA-->>Register: success
    Register->>State: registered_flags[SIGTERM] = restart
    Register-->>API: success
    API-->>Caller: result<void>()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • vinniefalco

Poem

🐰 Flags now flutter on signals grand,
POSIX flows through sigaction's hand,
Restart, defer, and child care too—
Cross-platform harmony shines through! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly describes the primary changes: replacing signal() with sigaction() and adding signal flags support, matching the main objectives.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #36: flags enum, sigaction() replacement on POSIX, flags parameter added to add() overload, flag validation and storage per-signal, Windows support (accepting flags but limiting to none/dont_care), and comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue objectives: public API extensions (flags enum and add overload), POSIX implementation refactoring to sigaction(), Windows flag validation, documentation enhancements, and comprehensive test coverage. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@include/boost/corosio/signal_set.hpp`:
- Around line 92-94: Update the doc comment in signal_set.hpp (the note near the
signal_set/flags description) to reflect actual Windows behavior: do not say
flags are "silently ignored"—state that on Windows the backend returns
operation_not_supported (error) for any flag other than none/dont_care; mention
that flags are only meaningful on POSIX and that callers should expect an
operation_not_supported error on Windows when passing unsupported flags. Refer
to signal_set and its flags in the comment so readers can locate the behavior.

In `@src/corosio/src/detail/posix/signals.cpp`:
- Around line 135-138: The code currently ignores requests for
signal_set::no_child_wait when SA_NOCLDWAIT is not defined; modify the handling
in src/corosio/src/detail/posix/signals.cpp so the caller is not misled: either
(A) add a compile-time guard that fails fast when signal_set::no_child_wait is
used but SA_NOCLDWAIT is unavailable (e.g. detect flags &
signal_set::no_child_wait and return a sentinel/error), or (B) emit a clear
runtime warning/error and return a failure code when flags contains
signal_set::no_child_wait but SA_NOCLDWAIT is undefined; update the caller
contract (function that consumes flags/returns sa_flags) to propagate this error
so callers can detect unsupported no_child_wait.
- Around line 75-77: deliver_signal() runs in a signal handler but currently
acquires non-async-signal-safe locks (e.g., std::mutex), which is undefined
behavior; change the implementation so the handler does not use mutexes: make
deliver_signal() perform only async-signal-safe actions (set a sig_atomic_t flag
or write a byte to a self-pipe/eventfd) and move the heavy work and any locking
into the main/event loop where handle_pending_signals() (or similar consumer
code) reads the flag/pipe and then acquires mutexes; update sigaction() setup
comments and/or document the new restriction if you choose a minimal change.

In `@src/corosio/src/detail/win/signals.hpp`:
- Around line 42-45: Update the comment in signals.hpp so it accurately
describes runtime behavior: replace "Signal flags are accepted but silently
ignored" with a note that unsupported signal flags are rejected at runtime with
operation_not_supported (or equivalent error), and mention that callers should
expect EOPNOTSUPP/operation_not_supported rather than silent no-op; ensure the
revised comment references the Windows-specific behavior already noted (use of C
runtime signal() and re-registration requirement) so maintainers see the
complete platform limitations.
🧹 Nitpick comments (1)
src/corosio/src/detail/posix/signals.cpp (1)

403-407: Use errno for accurate error reporting from sigaction().

When sigaction() fails, the code returns a generic invalid_argument error. The actual failure reason is in errno and could be more informative (e.g., EINVAL for invalid signal number, EFAULT for bad pointer).

Suggested improvement
         if (::sigaction(signal_number, &sa, nullptr) < 0)
         {
             delete new_reg;
-            return make_error_code(system::errc::invalid_argument);
+            return make_error_code(static_cast<system::errc>(errno));
         }

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@doc/asio-feature-parity.md`:
- Around line 152-160: Update the Signal Handling table to reflect the new
corosio::signal_set implementation: change the Corosio column entries for
`signal_set`, `async_wait` for signals, and `Add/remove signals` from :x: to
:white_check_mark: (or a partial/conditional mark if you prefer), and update the
Notes cell to mention that corosio::signal_set is flag-aware and implemented via
sigaction() on POSIX, with Windows limitations (e.g., limited/partial support on
Windows). Specifically reference `corosio::signal_set` and mention the use of
`sigaction()` and the platform caveat in the Notes column.

In `@doc/epoll-for-dummies.md`:
- Around line 20-35: The fenced code/diagram blocks in the doc (for example the
ASCII diagram shown inside the triple backticks in the posix_scheduler diagram)
are missing language identifiers and trigger MD040; update each triple-backtick
fence to include an appropriate language tag (use text for ASCII diagrams and
cpp for C++ code examples) so markdownlint passes—apply this change to the
blocks around the shown snippet and the other ranges called out (71-110,
196-205, 240-262, 315-353, 481-507, 609-614, 625-637, 646-670, 762-798).
- Around line 371-378: The doc snippet shows corosio_posix_signal_handler
calling posix_signals::deliver_signal and then re-registering via
::signal(signal_number, corosio_posix_signal_handler); update it to reflect the
sigaction-based implementation by removing the re-registration line and noting
that sigaction persists the handler automatically (or briefly mention using
sigaction instead of signal), keeping corosio_posix_signal_handler and
posix_signals::deliver_signal references intact.

In `@doc/issues/sigaction-support.md`:
- Around line 148-149: Replace the two bare URLs by turning them into Markdown
inline links to satisfy MD034: change the lines containing "Boost.Asio
`signal_set`" and the Boost URL to a linked form like [Boost.Asio
`signal_set`](https://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/signal_set.html)
and change the line containing "POSIX sigaction" and the POSIX URL to [POSIX
sigaction](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html);
ensure the visible text remains the descriptive labels (`signal_set` and
`sigaction`) so the markdownlint no-bare-urls rule is satisfied.
♻️ Duplicate comments (1)
src/corosio/src/detail/win/signals.hpp (1)

180-185: Align add_signal param docs with the runtime flag validation.

The param note says flags are ignored, but the header block says unsupported flags return operation_not_supported. Aligning these avoids mixed guidance.

📝 Suggested doc tweak
-        `@param` flags The flags to apply (ignored on Windows).
+        `@param` flags Only `none` and `dont_care` are supported; other flags
+        return `operation_not_supported`.
🧹 Nitpick comments (2)
doc/issues/cpp-modules-support.md (1)

45-55: Add a language tag to the fenced block.

MD040: the fence at Line 45 has no language; add one (e.g., text) to satisfy markdownlint.

✅ Proposed fix
-```
+```text
 boost.corosio              - Primary module interface
 boost.corosio:io_context   - io_context and scheduler
 boost.corosio:socket       - Socket types
 boost.corosio:acceptor     - TCP acceptor
 boost.corosio:resolver     - DNS resolver
 boost.corosio:read         - Read operations
 boost.corosio:write        - Write operations
 boost.corosio:tls          - TLS stream (WolfSSL)
 boost.corosio:buffers      - Buffer types (any_bufref, consuming_buffers)
doc/issues/epollexclusive-thundering-herd.md (1)

50-50: Replace hardcoded line numbers with function/section references.

The document references specific line numbers (scheduler.cpp:94, sockets.hpp:462, doc/epoll-for-dummies.md lines 621-643) which will become stale as the codebase evolves. Consider referencing by function name or section header instead.

For example:

  • Line 50: "registered in scheduler::initialize_epoll()" instead of "scheduler.cpp:94"
  • Line 110: "in socket::async_accept()" instead of "sockets.hpp:462"
♻️ Proposed changes
-The eventfd used for wakeup signaling is registered at `scheduler.cpp:94`:
+The eventfd used for wakeup signaling is registered in the scheduler initialization:
-`EPOLLEXCLUSIVE` is also relevant for accept operations on listening sockets (`sockets.hpp:462`). When multiple threads wait to accept on the same socket, `EPOLLEXCLUSIVE` prevents all threads from waking on each incoming connection.
+`EPOLLEXCLUSIVE` is also relevant for accept operations on listening sockets. When multiple threads wait to accept on the same socket, `EPOLLEXCLUSIVE` prevents all threads from waking on each incoming connection.
-- Corosio documentation: `doc/epoll-for-dummies.md` (lines 621-643)
+- Corosio documentation: `doc/epoll-for-dummies.md` (section on multi-threaded epoll usage)

Also applies to: 110-110, 184-184

Replace the POSIX signal() implementation with sigaction() and add
support for signal flags (SA_RESTART, SA_NOCLDSTOP, etc.) while
keeping OS-specific headers out of the public API.

Public API changes:

  - Add flags_t enum directly to signal_set with abstract bit values
  - Add signal_set::add(int, flags_t) overload
  - Existing add(int) becomes inline convenience calling add(signal, none)
  - Add bitwise operators for flag manipulation (|, &, |=, &=, ~)

Available flags: none, restart, no_child_stop, no_child_wait, no_defer,
reset_handler, dont_care

POSIX implementation (posix/signals.cpp):

  - Replace signal() with sigaction() for robust signal handling
  - Add flags_supported() to validate flags available on this platform
  - Add to_sigaction_flags() to map abstract flags to SA_* constants
  - Add flag conflict detection for multiple signal_sets on same signal
  - Remove handler re-registration (not needed with sigaction)
  - Track registered flags in global signal_state for cross-set validation
  - Return operation_not_supported if SA_NOCLDWAIT unavailable
  - Document async-signal-safety limitation in signal handler

Windows implementation (win/signals.cpp):

  - Only none and dont_care flags are supported
  - Other flags return operation_not_supported

Testing: Cross-platform tests for none/dont_care, POSIX-only tests for
actual flag behavior, Windows-only test for operation_not_supported.
@sgerbino sgerbino merged commit d75b02c into cppalliance:develop Jan 21, 2026
13 checks passed
@sgerbino sgerbino deleted the feature/sigaction branch January 21, 2026 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add signal flags support via sigaction() on POSIX

1 participant