The timer class provides asynchronous delays and timeouts. It integrates
with the I/O context to schedule operations at specific times or after
durations.
|
Note
|
Code snippets assume: #include <boost/corosio/timer.hpp>
namespace corosio = boost::corosio;
using namespace std::chrono_literals; |
Timers let you pause execution for a duration:
corosio::timer t(ioc);
t.expires_after(5s);
co_await t.wait(); // Suspends for 5 secondst.expires_after(100ms); // 100 milliseconds from now
t.expires_after(5s); // 5 seconds from now
t.expires_after(2min); // 2 minutes from nowAny std::chrono::duration type works.
auto deadline = std::chrono::steady_clock::now() + 10s;
t.expires_at(deadline);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";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";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.
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 waitsMultiple 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.
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";
}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";
}
}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
}|
Note
|
For proper timeout handling, use Capy’s parallel composition utilities
like when_any or cancellation tokens.
|
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.
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 requestedTimers are move-only:
corosio::timer t1(ioc);
corosio::timer t2 = std::move(t1); // OK
corosio::timer t3 = t2; // Error: deleted copy constructorMove assignment cancels any pending wait on the destination timer.
|
Note
|
After a move, the destination uses the source’s execution context. |
| 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.
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;
}
}-
Signal Handling — Respond to OS signals
-
I/O Context — The event loop
-
Error Handling — Cancellation patterns