Skip to content

Add and document examples#195

Merged
sgerbino merged 1 commit intocppalliance:developfrom
sgerbino:pr/examples
Mar 3, 2026
Merged

Add and document examples#195
sgerbino merged 1 commit intocppalliance:developfrom
sgerbino:pr/examples

Conversation

@sgerbino
Copy link
Collaborator

@sgerbino sgerbino commented Mar 3, 2026

Summary by CodeRabbit

  • New Features

    • Added several new example programs, including async-mutex, custom-executor, parallel-tasks, and strand-serialization, demonstrating common concurrency patterns.
  • Chores

    • Reorganized the examples build configuration and expanded conditional inclusion of optional components.
    • Refactored the echo-server example to use concrete I/O APIs and simplified connection handling and logging.

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

Caution

Review failed

Failed to post review comments

📝 Walkthrough

Walkthrough

This PR adds multiple example projects (async-mutex, custom-executor, parallel-tasks, strand-serialization), refactors the corosio echo-server to use corosio::tcp_socket/io_context, and reorganizes example/CMakeLists.txt with new subdirectory ordering and conditional asio inclusion. (50 words)

Changes

Cohort / File(s) Summary
Top-level CMake
example/CMakeLists.txt
Reorders and expands example subdirectories; adds conditional inclusion for Boost::asio target and adjusts placement of allocation/asio entries.
Async Mutex Example
example/async-mutex/CMakeLists.txt, example/async-mutex/async_mutex.cpp
New example demonstrating fair FIFO async_mutex usage, worker coroutines on a strand, cancellation handling, and acquisition ordering.
Custom Executor Example
example/custom-executor/CMakeLists.txt, example/custom-executor/custom_executor.cpp
Adds run_loop execution_context and nested executor_type that satisfy Executor concept; includes event-loop enqueue/run, dispatch/post semantics, and example usage.
Parallel Tasks Example
example/parallel-tasks/CMakeLists.txt, example/parallel-tasks/parallel_tasks.cpp
New example showing parallel partial-sum coroutines, run_async submission, aggregation via when_all, and result validation.
Strand Serialization Example
example/strand-serialization/CMakeLists.txt, example/strand-serialization/strand_serialization.cpp
New example demonstrating strand-based serialization protecting shared counter across concurrent coroutines.
Echo Server (corosio) refactor
example/echo-server-corosio/echo_server.cpp
Refactors from generic any_stream to concrete corosio::tcp_socket; updates accept loop to use corosio::tcp_acceptor and corosio::io_context, removes thread-pool wrapper and connection_id/client_info handling.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client as "Client"
participant Acceptor as "corosio::tcp_acceptor"
participant IO as "corosio::io_context"
participant Session as "echo_session (task)"
Note over Acceptor,IO: Accept loop on IO context
Client->>Acceptor: TCP connect
Acceptor->>IO: accept() -> socket
IO->>Session: run_async(echo_session(socket))
Session->>Session: read into buffer
Session->>Client: write echo
Session->>Session: close socket
IO->>Acceptor: loop continues

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐇 A bunny hops through Capy's trees,

spinning loops and posting keys,
Mutexes fair, strands in line,
Executors hum, tasks combine—
Examples bloom, small joys to seize.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add and document examples' is directly related to the changeset, which adds multiple new example files (async_mutex, custom_executor, parallel_tasks, strand_serialization) and corresponding CMakeLists.txt configurations.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@cppalliance-bot
Copy link

cppalliance-bot commented Mar 3, 2026

An automated preview of the documentation is available at https://195.capy.prtest.cppalliance.org/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-03-03 18:56:47 UTC

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: 7

🧹 Nitpick comments (2)
example/echo-server-corosio/echo_server.cpp (1)

10-15: Use a post-include /* ... */ implementation overview block.

This file has non-trivial coroutine/network flow, so prefer a high-level /* */ overview immediately after includes instead of banner-style // header text.

As per coding guidelines: "Files containing non-trivial implementation logic should include a /* */ block comment after the includes that provides a high-level overview of how the implementation works."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/echo-server-corosio/echo_server.cpp` around lines 10 - 15, Replace
the leading banner-style '//' header in echo_server.cpp with a post-include
block comment that gives a high-level implementation overview of the coroutine
and network flow; remove or convert the current top-of-file // comments and
instead insert a /* ... */ block immediately after the `#include` statements that
explains key components (e.g., how Corosio-driven coroutines accept connections,
spawn handler coroutines, echo data back, and any important lifecycle/error
handling) so readers get a concise architecture summary before diving into
functions like the accept loop and per-connection handler.
example/custom-executor/custom_executor.cpp (1)

1-27: Consolidate banner comments into one concise post-include overview block.

The decorative header blocks are verbose for an example file. Prefer a short /* ... */ implementation overview placed immediately after includes.

As per coding guidelines "Files containing non-trivial implementation logic should include a /* */ block comment after the includes..." and "Keep the ascii-art to a minimum, only add dividers if the class declaration is very long."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/custom-executor/custom_executor.cpp` around lines 1 - 27, Keep the
license/copyright header at the top but remove the multi-line decorative banner
comments; replace the verbose ASCII headers and scattered commentary with one
concise C-style block comment placed immediately after the includes that
succinctly describes the file purpose (e.g., "Custom single-threaded Executor
example demonstrating the Executor concept and run loop integration with Capy").
Ensure the new comment references the key intent shown in the original text
(single-threaded execution context / GUI-like run loop) and remove duplicate
explanatory lines elsewhere (such as the scattered "Custom Executor Example" and
"A minimal single-threaded execution context" comments) so only the single
post-include overview block remains before the implementation (after the
includes and before using namespace / class/function definitions).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@example/async-mutex/async_mutex.cpp`:
- Around line 34-40: The on_error lambda only catches std::exception, so
non-standard exceptions escape and can prevent done.count_down() from executing;
update on_error (the lambda capturing std::exception_ptr ep and calling
done.count_down()) to also catch all other exceptions (catch(...)) after the
current catch(std::exception const& e) and log a generic message (e.g., "Unknown
exception") so that any exception type is handled and done.count_down() always
runs regardless of the exception type.

In `@example/custom-executor/custom_executor.cpp`:
- Around line 30-31: The queue_ is accessed concurrently (run(), enqueue(),
dispatch(), post()) causing data races and executor_type's public default ctor
leaves loop_ null; fix by adding a std::mutex (e.g. queue_mutex_) and guard all
accesses to queue_ (lock in enqueue(), lock when popping in run()), ensure
dispatch()/post() call the thread-safe enqueue() only, and update
enqueue()/run() to minimize lock hold time; also remove or delete the public
default constructor of executor_type (or initialize loop_ to a valid pointer)
and add a runtime check/assert at the start of public methods (enqueue(),
dispatch(), post(), run()) to fail fast if loop_ is null so you don't
dereference it.
- Around line 86-108: The methods context(), dispatch(), and post() dereference
loop_ which can be null for a default-constructed executor_type; add explicit
precondition guards (e.g., assert(loop_ != nullptr) or a DEBUG-only check) at
the start of execution_context& context() const noexcept,
std::coroutine_handle<> dispatch(...), and void post(...) to document and
enforce the invariant before dereferencing loop_, and ensure the header includes
<cassert> (or your project’s assert utility) so calls to context(), dispatch(),
and post() fail fast with a clear message when loop_ is uninitialized.

In `@example/echo-server-corosio/echo_server.cpp`:
- Around line 79-82: The port parsing currently casts std::atoi result into
unsigned short (port) with no validation; replace this by parsing argv[1] safely
(e.g., use strtol or std::stoi with error checking), verify the parsed value is
numeric and in the valid TCP port range (1–65535) before assigning to unsigned
short port, and handle invalid input by printing an error and exiting or falling
back to the default 8080; update the code paths around the port variable and the
argc/argv handling so negative, non-numeric, or out-of-range values are rejected
rather than silently wrapped.
- Around line 24-25: The coroutine echo_session currently takes
corosio::tcp_socket& and is launched detached via run_async from accept_loop
with a stack-local peer, causing a dangling reference; change echo_session to
take corosio::tcp_socket by value (or by rvalue) so the socket is owned by the
coroutine and when you call echo_session from accept_loop pass std::move(peer)
(or otherwise move the peer into the task) to transfer ownership; update any
callsites and signatures (echo_session, accept_loop, run_async invocation)
accordingly so the coroutine no longer references a stack-local socket.

In `@example/parallel-tasks/parallel_tasks.cpp`:
- Around line 50-56: The on_error lambda can let non-std exceptions escape and
skip the completion signal; update on_error (the lambda capturing done and
taking std::exception_ptr ep) so that after rethrowing you handle both
std::exception (catch(std::exception const& e)) and a final catch(...) branch
that logs an unknown/non-std error, and ensure done.count_down() is executed
unconditionally (i.e., keep the count_down() call after the catch blocks or use
a scope guard) so completion is always signaled even for non-std exceptions.

In `@example/strand-serialization/strand_serialization.cpp`:
- Around line 34-40: The on_error lambda currently only catches std::exception
from std::rethrow_exception and will let non-std::exception payloads escape
(potentially skipping done.count_down()); update the handler (on_error) to add a
catch(...) branch after catching std::exception to handle non-standard
exceptions, log a generic error message (or rethrow info if available), and
ensure done.count_down() is executed in all paths so the latch is always
signaled even for non-std::exception errors.

---

Nitpick comments:
In `@example/custom-executor/custom_executor.cpp`:
- Around line 1-27: Keep the license/copyright header at the top but remove the
multi-line decorative banner comments; replace the verbose ASCII headers and
scattered commentary with one concise C-style block comment placed immediately
after the includes that succinctly describes the file purpose (e.g., "Custom
single-threaded Executor example demonstrating the Executor concept and run loop
integration with Capy"). Ensure the new comment references the key intent shown
in the original text (single-threaded execution context / GUI-like run loop) and
remove duplicate explanatory lines elsewhere (such as the scattered "Custom
Executor Example" and "A minimal single-threaded execution context" comments) so
only the single post-include overview block remains before the implementation
(after the includes and before using namespace / class/function definitions).

In `@example/echo-server-corosio/echo_server.cpp`:
- Around line 10-15: Replace the leading banner-style '//' header in
echo_server.cpp with a post-include block comment that gives a high-level
implementation overview of the coroutine and network flow; remove or convert the
current top-of-file // comments and instead insert a /* ... */ block immediately
after the `#include` statements that explains key components (e.g., how
Corosio-driven coroutines accept connections, spawn handler coroutines, echo
data back, and any important lifecycle/error handling) so readers get a concise
architecture summary before diving into functions like the accept loop and
per-connection handler.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4b27fb and c0fc363.

⛔ Files ignored due to path filters (7)
  • doc/modules/ROOT/nav.adoc is excluded by !**/doc/**
  • doc/modules/ROOT/pages/7.examples/7i.echo-server-corosio.adoc is excluded by !**/doc/**
  • doc/modules/ROOT/pages/7.examples/7j.stream-pipeline.adoc is excluded by !**/doc/**
  • doc/modules/ROOT/pages/7.examples/7k.strand-serialization.adoc is excluded by !**/doc/**
  • doc/modules/ROOT/pages/7.examples/7l.async-mutex.adoc is excluded by !**/doc/**
  • doc/modules/ROOT/pages/7.examples/7m.parallel-tasks.adoc is excluded by !**/doc/**
  • doc/modules/ROOT/pages/7.examples/7n.custom-executor.adoc is excluded by !**/doc/**
📒 Files selected for processing (10)
  • example/CMakeLists.txt
  • example/async-mutex/CMakeLists.txt
  • example/async-mutex/async_mutex.cpp
  • example/custom-executor/CMakeLists.txt
  • example/custom-executor/custom_executor.cpp
  • example/echo-server-corosio/echo_server.cpp
  • example/parallel-tasks/CMakeLists.txt
  • example/parallel-tasks/parallel_tasks.cpp
  • example/strand-serialization/CMakeLists.txt
  • example/strand-serialization/strand_serialization.cpp

Comment on lines +30 to +31
std::queue<std::coroutine_handle<>> queue_;
std::thread::id owner_;
Copy link

@coderabbitai coderabbitai bot Mar 3, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n example/custom-executor/custom_executor.cpp

Repository: cppalliance/capy

Length of output: 4568


Protect queue_ access; concurrent scheduling creates data race.

The queue_ member is accessed without synchronization. run() (lines 54–59) and enqueue() (lines 62–65, 107) mutate the queue without locks. However, dispatch() explicitly allows cross-thread enqueuing (line 99–102: enqueue is called when NOT on the owner thread), and post() unconditionally calls enqueue() from any thread. This creates a data race if post() or dispatch() is called from a thread other than the one running loop.run().

Additionally, executor_type has a public default constructor (line 86) that leaves loop_ as nullptr, causing a null-pointer dereference if any method is called on a default-constructed executor.

Suggested fix
 `#include` <boost/capy.hpp>
 `#include` <iostream>
+#include <mutex>
 `#include` <queue>
 `#include` <thread>
@@
 class run_loop : public execution_context
 {
     std::queue<std::coroutine_handle<>> queue_;
+    mutable std::mutex queue_mtx_;
     std::thread::id owner_;
@@
     void run()
     {
         owner_ = std::this_thread::get_id();
-        while (!queue_.empty())
+        while (true)
         {
-            auto h = queue_.front();
-            queue_.pop();
+            std::coroutine_handle<> h;
+            {
+                std::lock_guard<std::mutex> lk(queue_mtx_);
+                if (queue_.empty())
+                    break;
+                h = queue_.front();
+                queue_.pop();
+            }
             h.resume();
         }
     }
@@
     void enqueue(std::coroutine_handle<> h)
     {
+        std::lock_guard<std::mutex> lk(queue_mtx_);
         queue_.push(h);
     }

Also applies to: lines 51–65, 96–108

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/custom-executor/custom_executor.cpp` around lines 30 - 31, The queue_
is accessed concurrently (run(), enqueue(), dispatch(), post()) causing data
races and executor_type's public default ctor leaves loop_ null; fix by adding a
std::mutex (e.g. queue_mutex_) and guard all accesses to queue_ (lock in
enqueue(), lock when popping in run()), ensure dispatch()/post() call the
thread-safe enqueue() only, and update enqueue()/run() to minimize lock hold
time; also remove or delete the public default constructor of executor_type (or
initialize loop_ to a valid pointer) and add a runtime check/assert at the start
of public methods (enqueue(), dispatch(), post(), run()) to fail fast if loop_
is null so you don't dereference it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not addressing. This is intentionally a single-threaded run loop — the class comment and example title say so explicitly. In main(), run_async enqueues onto the loop, then loop.run() drains it, all on the same thread. Adding a mutex would add complexity that contradicts the purpose of the example (showing the minimal Executor concept implementation). The dispatch() cross-thread path exists to satisfy the concept but is not exercised here.

Copy link

Choose a reason for hiding this comment

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

@sgerbino, thank you for the clarification! You're absolutely right—the class comment and example explicitly state this is a single-threaded run loop demonstrating the minimal Executor concept. Since the example runs entirely on one thread (enqueue via run_async, then drain via loop.run()), the data race concern doesn't apply to the intended usage. I appreciate you explaining the design goal of keeping it simple for educational purposes.


✏️ Learnings added
Learnt from: sgerbino
Repo: cppalliance/capy PR: 195
File: example/custom-executor/custom_executor.cpp:30-31
Timestamp: 2026-03-03T18:46:48.319Z
Learning: The custom executor example (example/custom-executor/custom_executor.cpp) is intentionally a single-threaded run loop demonstrating minimal Executor concept implementation without thread safety mechanisms. The cross-thread path in dispatch() exists to satisfy the concept but is not exercised in the example.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: cppalliance/capy PR: 0
File: .cursor/rules/doc-concept.mdc:0-0
Timestamp: 2026-02-01T22:20:18.267Z
Learning: Applies to **/*.{hpp,h} : Concept javadoc should include par Example with a code block showing a generic algorithm templated on the concept, demonstrating typical usage with destructuring, error handling, and interaction with related concepts

Learnt from: CR
Repo: cppalliance/capy PR: 0
File: .cursor/rules/doc-general.mdc:0-0
Timestamp: 2026-02-01T22:20:39.250Z
Learning: Applies to **/*.{h,hpp,hxx} : Include par Example in function javadocs with one or more code blocks; provide more examples for functions with multiple usage patterns or overloads

Comment on lines +86 to +108
executor_type() = default;

execution_context& context() const noexcept
{
return *loop_;
}

void on_work_started() const noexcept {}
void on_work_finished() const noexcept {}

std::coroutine_handle<> dispatch(
std::coroutine_handle<> h) const
{
if (loop_->is_running_on_this_thread())
return h;
loop_->enqueue(h);
return std::noop_coroutine();
}

void post(std::coroutine_handle<> h) const
{
loop_->enqueue(h);
}
Copy link

@coderabbitai coderabbitai bot Mar 3, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the complete executor_type class definition
cd example/custom-executor && wc -l custom_executor.cpp

Repository: cppalliance/capy

Length of output: 83


🏁 Script executed:

# Get the full context of the executor_type class
cd example/custom-executor && sed -n '70,120p' custom_executor.cpp

Repository: cppalliance/capy

Length of output: 1038


🏁 Script executed:

# Search for the executor_type class declaration and all its members
rg -n 'class executor_type|executor_type\s*\(' example/custom-executor/custom_executor.cpp -A 10 -B 2

Repository: cppalliance/capy

Length of output: 690


🏁 Script executed:

# Check if loop_ is initialized or what its default value is
rg -n 'loop_\s*=|loop_\s*\(' example/custom-executor/custom_executor.cpp -B 2 -A 2

Repository: cppalliance/capy

Length of output: 369


🏁 Script executed:

# Search for executor concept requirements in the codebase
rg -n 'concept.*Executor|default_initializable|semiregular' --type=cpp --type=hpp | head -30

Repository: cppalliance/capy

Length of output: 87


🏁 Script executed:

# Check how executor_type is actually used (instantiated)
rg -n 'executor_type\s*\(\)|\brun_loop::executor_type\b' --type=cpp --type=hpp

Repository: cppalliance/capy

Length of output: 87


🏁 Script executed:

# Search for executor concept in the codebase
find . -name "*.hpp" -o -name "*.h" -o -name "*.cpp" | head -20

Repository: cppalliance/capy

Length of output: 903


🏁 Script executed:

# Look for executor concept or requirements
rg -l 'concept.*Executor|executor_type' --type=cpp | head -20

Repository: cppalliance/capy

Length of output: 620


🏁 Script executed:

# Search for capy executor concept
rg -n 'concept.*Executor' . -A 5 -B 2

Repository: cppalliance/capy

Length of output: 4501


🏁 Script executed:

# Check tests for executor_type usage
rg -n 'executor_type\s*\(\)' . --type=cpp --type=hpp

Repository: cppalliance/capy

Length of output: 87


🏁 Script executed:

# Look for any documentation on executor semiregular/regular requirement
rg -n 'semiregular|regular|default.construct' . --type=cpp --type=hpp | head -20

Repository: cppalliance/capy

Length of output: 87


🏁 Script executed:

# Check the include files to understand executor interface requirements
cat example/custom-executor/custom_executor.cpp | head -50

Repository: cppalliance/capy

Length of output: 1216


🏁 Script executed:

# Look at the full executor concept from the capy library
cat include/boost/capy/concept/executor.hpp | grep -A 30 'concept Executor'

Repository: cppalliance/capy

Length of output: 764


🏁 Script executed:

# Search for any usage of executor_type default construction
rg 'executor_type\s*\(\)|\bexecutor_type\s*\{\}' . --type=cpp

Repository: cppalliance/capy

Length of output: 207


🏁 Script executed:

# Check if there are any tests that default-construct executor_type
rg 'executor_type' test/ --type=cpp -B 2 -A 2

Repository: cppalliance/capy

Length of output: 3299


🏁 Script executed:

# Check examples directory for executor_type usage
rg 'executor_type' example/ --type=cpp -B 2 -A 2

Repository: cppalliance/capy

Length of output: 4235


🏁 Script executed:

# Get the full context of the thread_pool test that mentions default construction
sed -n '1,50p' test/unit/ex/thread_pool.cpp | cat -n

Repository: cppalliance/capy

Length of output: 1689


🏁 Script executed:

# Look for the test with "valid but unassociated" comment
rg -n 'valid but unassociated|Default-constructed executor' test/unit/ex/thread_pool.cpp -B 5 -A 10

Repository: cppalliance/capy

Length of output: 569


🏁 Script executed:

# Check if there's any documentation about executor null state in include/boost/capy/concept/executor.hpp
cat include/boost/capy/concept/executor.hpp | head -100

Repository: cppalliance/capy

Length of output: 3681


Add precondition guards to prevent undefined behavior on default-constructed executors.

The default constructor leaves loop_ == nullptr, and context(), dispatch(), and post() dereference it unconditionally. This creates undefined behavior when these methods are called on default-constructed executors. While this appears to be intentional design (allowing default construction for standard library compatibility while accepting a "valid but unassociated state"), the example should defend against accidental misuse by explicitly guarding the dereferences with assertions that document the precondition.

Suggested defensive guards
+#include <cassert>
@@
     execution_context& context() const noexcept
     {
+        assert(loop_ != nullptr);
         return *loop_;
     }
@@
     std::coroutine_handle<> dispatch(
         std::coroutine_handle<> h) const
     {
+        assert(loop_ != nullptr);
         if (loop_->is_running_on_this_thread())
             return h;
         loop_->enqueue(h);
         return std::noop_coroutine();
     }
@@
     void post(std::coroutine_handle<> h) const
     {
+        assert(loop_ != nullptr);
         loop_->enqueue(h);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/custom-executor/custom_executor.cpp` around lines 86 - 108, The
methods context(), dispatch(), and post() dereference loop_ which can be null
for a default-constructed executor_type; add explicit precondition guards (e.g.,
assert(loop_ != nullptr) or a DEBUG-only check) at the start of
execution_context& context() const noexcept, std::coroutine_handle<>
dispatch(...), and void post(...) to document and enforce the invariant before
dereferencing loop_, and ensure the header includes <cassert> (or your project’s
assert utility) so calls to context(), dispatch(), and post() fail fast with a
clear message when loop_ is uninitialized.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not addressing. The default constructor exists to satisfy semiregular requirements of the Executor concept. Adding asserts would add noise to what is meant to be a minimal teaching example. The library's own thread_pool::executor_type follows the same pattern.

Copy link

Choose a reason for hiding this comment

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

@sgerbino, understood—that makes sense for a teaching example. I appreciate the clarification about the semiregular requirement and the consistency with thread_pool::executor_type. Keeping the example minimal is the right call here.


✏️ Learnings added
Learnt from: sgerbino
Repo: cppalliance/capy PR: 195
File: example/custom-executor/custom_executor.cpp:86-108
Timestamp: 2026-03-03T18:46:58.770Z
Learning: In the capy library, executor types (like `run_loop::executor_type` and `thread_pool::executor_type`) can be default-constructed with a null internal pointer to satisfy the `semiregular` requirement of the Executor concept, creating a "valid but unassociated" state. Methods like `context()`, `dispatch()`, and `post()` may assume the executor is properly initialized and not guard against the null state.

Learnt from: sgerbino
Repo: cppalliance/capy PR: 195
File: example/custom-executor/custom_executor.cpp:86-108
Timestamp: 2026-03-03T18:46:58.770Z
Learning: Teaching examples in the capy codebase should be kept minimal without defensive assertions or precondition guards, even when those guards would prevent undefined behavior in edge cases. The focus is on demonstrating the primary usage pattern clearly.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: cppalliance/capy PR: 0
File: .cursor/rules/doc-concept.mdc:0-0
Timestamp: 2026-02-01T22:20:18.267Z
Learning: Applies to **/*.{hpp,h} : Concept javadoc should include par Example with a code block showing a generic algorithm templated on the concept, demonstrating typical usage with destructuring, error handling, and interaction with related concepts

Learnt from: CR
Repo: cppalliance/capy PR: 0
File: .cursor/rules/doc-general.mdc:0-0
Timestamp: 2026-02-01T22:20:39.250Z
Learning: Applies to **/*.{h,hpp,hxx} : Include par Example in function javadocs with one or more code blocks; provide more examples for functions with multiple usage patterns or overloads

Comment on lines +79 to +82
unsigned short port = 8080;
if (argc > 1)
port = static_cast<unsigned short>(std::atoi(argv[1]));

Copy link

@coderabbitai coderabbitai bot Mar 3, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the file
fd -t f "echo_server.cpp" --exec wc -l {} \;

Repository: cppalliance/capy

Length of output: 108


🏁 Script executed:

# Read the specific lines mentioned in the review
sed -n '75,85p' example/echo-server-corosio/echo_server.cpp

Repository: cppalliance/capy

Length of output: 303


🏁 Script executed:

# Get broader context around lines 79-82 to understand the full function
sed -n '1,100p' example/echo-server-corosio/echo_server.cpp | head -100

Repository: cppalliance/capy

Length of output: 2104


Harden CLI port parsing and range validation.

Line 81 uses std::atoi and then narrows to unsigned short without validation; invalid, negative, or out-of-range input can silently produce unintended ports.

💡 Proposed fix
+#include <charconv>
+#include <cstring>
+#include <limits>
@@
     unsigned short port = 8080;
     if (argc > 1)
-        port = static_cast<unsigned short>(std::atoi(argv[1]));
+    {
+        unsigned int parsed = 0;
+        const char* first = argv[1];
+        const char* last = first + std::strlen(first);
+        auto [ptr, ec] = std::from_chars(first, last, parsed);
+        if (ec != std::errc{} || ptr != last ||
+            parsed > std::numeric_limits<unsigned short>::max())
+        {
+            std::cerr << "Invalid port: " << argv[1] << "\n";
+            return 1;
+        }
+        port = static_cast<unsigned short>(parsed);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
unsigned short port = 8080;
if (argc > 1)
port = static_cast<unsigned short>(std::atoi(argv[1]));
unsigned short port = 8080;
if (argc > 1)
{
unsigned int parsed = 0;
const char* first = argv[1];
const char* last = first + std::strlen(first);
auto [ptr, ec] = std::from_chars(first, last, parsed);
if (ec != std::errc{} || ptr != last ||
parsed > std::numeric_limits<unsigned short>::max())
{
std::cerr << "Invalid port: " << argv[1] << "\n";
return 1;
}
port = static_cast<unsigned short>(parsed);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example/echo-server-corosio/echo_server.cpp` around lines 79 - 82, The port
parsing currently casts std::atoi result into unsigned short (port) with no
validation; replace this by parsing argv[1] safely (e.g., use strtol or
std::stoi with error checking), verify the parsed value is numeric and in the
valid TCP port range (1–65535) before assigning to unsigned short port, and
handle invalid input by printing an error and exiting or falling back to the
default 8080; update the code paths around the port variable and the argc/argv
handling so negative, non-numeric, or out-of-range values are rejected rather
than silently wrapped.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not addressing. This is example code — the focus is on the Capy/Corosio integration, not CLI parsing. Replacing atoi with from_chars + range validation would triple the size of main() for no pedagogical benefit.

Copy link

Choose a reason for hiding this comment

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

@sgerbino, understood. You're right that example code should stay focused on demonstrating the core library features—Capy/Corosio integration in this case—rather than adding boilerplate for edge cases like CLI validation. I'll keep that in mind for future reviews of example files.


✏️ Learnings added
Learnt from: sgerbino
Repo: cppalliance/capy PR: 195
File: example/echo-server-corosio/echo_server.cpp:79-82
Timestamp: 2026-03-03T18:47:00.898Z
Learning: In example code files (e.g., files in `example/` directories), avoid flagging robustness issues in peripheral code (such as CLI argument parsing, error handling for non-library operations) when the example's purpose is to demonstrate library integration or specific library features. Example code should remain concise and focused on its pedagogical goal, not production-grade hardening of ancillary concerns.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: cppalliance/capy PR: 0
File: .cursor/rules/doc-concept.mdc:0-0
Timestamp: 2026-02-01T22:20:18.267Z
Learning: Applies to **/*.{hpp,h} : Concept javadoc should include par Example with a code block showing a generic algorithm templated on the concept, demonstrating typical usage with destructuring, error handling, and interaction with related concepts

Learnt from: CR
Repo: cppalliance/capy PR: 0
File: .cursor/rules/doc-general.mdc:0-0
Timestamp: 2026-02-01T22:20:39.250Z
Learning: Applies to **/*.{h,hpp,hxx} : Include par Example in function javadocs with one or more code blocks; provide more examples for functions with multiple usage patterns or overloads

@cppalliance-bot
Copy link

GCOVR code coverage report https://195.capy.prtest3.cppalliance.org/gcovr/index.html
LCOV code coverage report https://195.capy.prtest3.cppalliance.org/genhtml/index.html
Coverage Diff Report https://195.capy.prtest3.cppalliance.org/diff-report/index.html

Build time: 2026-03-03 18:07:11 UTC

Add four new examples (async-mutex, custom-executor, parallel-tasks,
strand-serialization) with CMake build files and Antora documentation
pages. Rewrite echo-server-corosio to use the real Corosio API.

Fix echo_session to take tcp_socket by value to prevent a dangling
reference from the detached coroutine. Add catch(...) to on_error
handlers to ensure latch signaling for non-std exceptions.
@sgerbino sgerbino merged commit b43334e into cppalliance:develop Mar 3, 2026
87 of 123 checks passed
@sgerbino sgerbino deleted the pr/examples branch March 3, 2026 22:06
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.

2 participants