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";
Cancellation
cancel() cancels all pending waits. cancel_one() cancels only the
oldest pending wait ( FIFO order ). Both return the number of operations
cancelled:
std::size_t n = t.cancel(); // Cancel all pending waits
std::size_t m = t.cancel_one(); // Cancel oldest pending wait (0 or 1)
The cancelled wait completes with an error:
auto [ec] = co_await t.wait();
if (ec == capy::error::canceled)
std::cout << "Timer was cancelled\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 waits and returns the number cancelled:
t.expires_after(10s);
// Later, before 10s elapses:
std::size_t n = t.expires_after(5s); // Resets to 5s, cancels previous waits
Multiple Waiters
Multiple coroutines can wait on the same timer concurrently. When the
timer expires, all waiters complete with success. When cancelled, all
waiters complete with capy::error::canceled:
capy::task<void> waiter(corosio::timer& t, int id)
{
auto [ec] = co_await t.wait();
if (!ec)
std::cout << "Waiter " << id << " expired\n";
}
capy::task<void> multi_wait(corosio::io_context& ioc)
{
corosio::timer t(ioc);
t.expires_after(1s);
// All three coroutines wait on the same timer
co_await capy::when_all(
waiter(t, 1),
waiter(t, 2),
waiter(t, 3));
}
Each waiter has independent stop token cancellation. Cancelling one
waiter’s stop token does not affect the others. cancel_one() cancels
the oldest waiter only.
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::tcp_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::tcp_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