Timers
The timer class provides asynchronous delays and timeouts. It integrates
with the I/O context to schedule operations at specific times or after
durations.
| Code snippets assume: |
#include <boost/corosio/timer.hpp>
namespace corosio = boost::corosio;
using namespace std::chrono_literals;
Overview
Timers let you pause execution for a duration:
corosio::timer t(ioc);
t.expires_after(5s);
co_await t.wait(); // Suspends for 5 seconds
Setting Expiry Time
Relative Time (Duration)
t.expires_after(100ms); // 100 milliseconds from now
t.expires_after(5s); // 5 seconds from now
t.expires_after(2min); // 2 minutes from now
Any std::chrono::duration type works.
Waiting
The wait() operation suspends until the timer expires:
t.expires_after(1s);
auto [ec] = co_await t.wait();
if (!ec)
std::cout << "Timer expired normally\n";
Type Aliases
using clock_type = std::chrono::steady_clock;
using time_point = clock_type::time_point;
using duration = clock_type::duration;
The timer uses steady_clock for monotonic timing unaffected by system
clock adjustments.
Resetting Timers
Setting a new expiry cancels any pending wait:
t.expires_after(10s);
// Later, before 10s elapses:
t.expires_after(5s); // Resets to 5s, cancels previous wait
Use Cases
Simple Delay
capy::task<void> delayed_action(corosio::io_context& ioc)
{
corosio::timer t(ioc);
t.expires_after(2s);
co_await t.wait();
std::cout << "2 seconds have passed\n";
}
Periodic Timer
capy::task<void> periodic_task(corosio::io_context& ioc)
{
corosio::timer t(ioc);
for (int i = 0; i < 10; ++i)
{
t.expires_after(1s);
co_await t.wait();
std::cout << "Tick " << i << "\n";
}
}
Operation Timeout
capy::task<void> with_timeout(
corosio::io_context& ioc,
corosio::socket& sock)
{
corosio::timer timeout(ioc);
timeout.expires_after(30s);
// Start both operations
auto read_task = sock.read_some(buffer);
auto timeout_task = timeout.wait();
// In practice, use parallel composition utilities
// This is simplified for illustration
}
For proper timeout handling, use Capy’s parallel composition utilities
like when_any or cancellation tokens.
|
Rate Limiting
capy::task<void> rate_limited_work(corosio::io_context& ioc)
{
corosio::timer t(ioc);
auto next_time = std::chrono::steady_clock::now();
for (int i = 0; i < 100; ++i)
{
// Do work
process_item(i);
// Wait until next interval
next_time += 100ms;
t.expires_at(next_time);
auto [ec] = co_await t.wait();
if (ec)
break;
}
}
Using absolute time points prevents drift in periodic operations.
Stop Token Cancellation
Timer waits support stop token cancellation through the affine protocol:
// Inside a cancellable task:
auto [ec] = co_await t.wait();
// Completes with capy::error::canceled if stop requested
Move Semantics
Timers are move-only:
corosio::timer t1(ioc);
corosio::timer t2 = std::move(t1); // OK
corosio::timer t3 = t2; // Error: deleted copy constructor
Move assignment cancels any pending wait on the destination timer.
| Source and destination must share the same execution context. |
Thread Safety
| Operation | Thread Safety |
|---|---|
Distinct timers |
Safe from different threads |
Same timer |
NOT safe for concurrent operations |
Don’t call wait(), expires_after(), or cancel() concurrently on the
same timer.
Example: Heartbeat
capy::task<void> heartbeat(
corosio::io_context& ioc,
corosio::socket& sock,
std::atomic<bool>& running)
{
corosio::timer t(ioc);
while (running)
{
t.expires_after(30s);
auto [ec] = co_await t.wait();
if (ec)
break;
// Send heartbeat
std::string ping = "PING\r\n";
auto [wec, n] = co_await corosio::write(
sock, capy::const_buffer(ping.data(), ping.size()));
if (wec)
break;
}
}
Next Steps
-
Signal Handling — Respond to OS signals
-
I/O Context — The event loop
-
Error Handling — Cancellation patterns