Skip to content

Commit 34a9b3a

Browse files
authored
Add zmq_timers support (#657)
* Add zmq_timers support * Add zmq_timers unit tests
1 parent 7cb78a8 commit 34a9b3a

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
cmake_minimum_required(VERSION 3.11)
22

33
list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
4+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
45

56
include (DetectCPPZMQVersion)
67

tests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ add_executable(
3030
codec_multipart.cpp
3131
monitor.cpp
3232
utilities.cpp
33+
timers.cpp
3334
)
3435

3536
target_include_directories(unit_tests PUBLIC ${CATCH_MODULE_PATH})

tests/timers.cpp

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include <catch2/catch_all.hpp>
2+
#include <zmq.hpp>
3+
4+
#include <type_traits>
5+
#include <thread>
6+
#include <chrono>
7+
8+
#if defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS)
9+
10+
static_assert(std::is_default_constructible<zmq::timers>::value);
11+
static_assert(!std::is_copy_constructible<zmq::timers>::value);
12+
static_assert(!std::is_copy_assignable<zmq::timers>::value);
13+
14+
TEST_CASE("timers constructor", "[timers]")
15+
{
16+
zmq::timers timers;
17+
CHECK(!timers.timeout().has_value());
18+
}
19+
20+
TEST_CASE("timers add/execute", "[timers]")
21+
{
22+
using namespace std::chrono_literals;
23+
zmq::timers timers;
24+
bool handler_ran = false;
25+
timers.add(4ms, [](auto, void *arg) { *(bool *) arg = true; }, &handler_ran);
26+
CHECK(timers.timeout().has_value());
27+
CHECK(!handler_ran);
28+
std::this_thread::sleep_for(10ms);
29+
timers.execute();
30+
CHECK(handler_ran);
31+
}
32+
33+
TEST_CASE("timers add/cancel", "[timers]")
34+
{
35+
using namespace std::chrono_literals;
36+
zmq::timers timers;
37+
bool handler_ran = false;
38+
auto id =
39+
timers.add(4ms, [](auto, void *arg) { *(bool *) arg = true; }, &handler_ran);
40+
CHECK(timers.timeout().has_value());
41+
CHECK(!handler_ran);
42+
timers.cancel(id);
43+
CHECK(!timers.timeout().has_value());
44+
CHECK(!handler_ran);
45+
}
46+
47+
TEST_CASE("timers set_interval", "[timers]")
48+
{
49+
using namespace std::chrono_literals;
50+
zmq::timers timers;
51+
bool handler_ran = false;
52+
// Interval of 4 hours should never run like this
53+
auto id =
54+
timers.add(4h, [](auto, void *arg) { *(bool *) arg = true; }, &handler_ran);
55+
CHECK(timers.timeout().has_value());
56+
CHECK(!handler_ran);
57+
// Change the interval to 4ms and wait for it to timeout
58+
timers.set_interval(id, 4ms);
59+
std::this_thread::sleep_for(10ms);
60+
timers.execute();
61+
CHECK(handler_ran);
62+
}
63+
64+
TEST_CASE("timers reset", "[timers]")
65+
{
66+
using namespace std::chrono_literals;
67+
zmq::timers timers;
68+
bool handler_ran = false;
69+
auto id =
70+
timers.add(4ms, [](auto, void *arg) { *(bool *) arg = true; }, &handler_ran);
71+
CHECK(timers.timeout().has_value());
72+
std::this_thread::sleep_for(10ms);
73+
// Available to be executed but we reset it
74+
timers.reset(id);
75+
CHECK(timers.timeout().has_value());
76+
CHECK(!handler_ran);
77+
78+
}
79+
80+
#endif // defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS)

zmq.hpp

+79
Original file line numberDiff line numberDiff line change
@@ -2794,6 +2794,85 @@ inline std::ostream &operator<<(std::ostream &os, const message_t &msg)
27942794
return os << msg.str();
27952795
}
27962796

2797+
#if defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS)
2798+
2799+
class timers
2800+
{
2801+
public:
2802+
using id_t = int;
2803+
using fn_t = zmq_timer_fn;
2804+
2805+
#if CPPZMQ_HAS_OPTIONAL
2806+
using timeout_result_t = std::optional<std::chrono::milliseconds>;
2807+
#else
2808+
using timeout_result_t = detail::trivial_optional<std::chrono::milliseconds>;
2809+
#endif
2810+
2811+
timers() : _timers(zmq_timers_new())
2812+
{
2813+
if (_timers == nullptr)
2814+
throw error_t();
2815+
}
2816+
2817+
timers(const timers &other) = delete;
2818+
timers &operator=(const timers &other) = delete;
2819+
2820+
~timers()
2821+
{
2822+
int rc = zmq_timers_destroy(&_timers);
2823+
ZMQ_ASSERT(rc == 0);
2824+
}
2825+
2826+
id_t add(std::chrono::milliseconds interval, zmq_timer_fn handler, void *arg)
2827+
{
2828+
id_t timer_id = zmq_timers_add(_timers, interval.count(), handler, arg);
2829+
if (timer_id == -1)
2830+
throw zmq::error_t();
2831+
return timer_id;
2832+
}
2833+
2834+
void cancel(id_t timer_id)
2835+
{
2836+
int rc = zmq_timers_cancel(_timers, timer_id);
2837+
if (rc == -1)
2838+
throw zmq::error_t();
2839+
}
2840+
2841+
void set_interval(id_t timer_id, std::chrono::milliseconds interval)
2842+
{
2843+
int rc = zmq_timers_set_interval(_timers, timer_id, interval.count());
2844+
if (rc == -1)
2845+
throw zmq::error_t();
2846+
}
2847+
2848+
void reset(id_t timer_id)
2849+
{
2850+
int rc = zmq_timers_reset(_timers, timer_id);
2851+
if (rc == -1)
2852+
throw zmq::error_t();
2853+
}
2854+
2855+
timeout_result_t timeout() const
2856+
{
2857+
int timeout = zmq_timers_timeout(_timers);
2858+
if (timeout == -1)
2859+
return timeout_result_t{};
2860+
return std::chrono::milliseconds{timeout};
2861+
}
2862+
2863+
void execute()
2864+
{
2865+
int rc = zmq_timers_execute(_timers);
2866+
if (rc == -1)
2867+
throw zmq::error_t();
2868+
}
2869+
2870+
private:
2871+
void *_timers;
2872+
};
2873+
2874+
#endif // defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS)
2875+
27972876
} // namespace zmq
27982877

27992878
#endif // __ZMQ_HPP_INCLUDED__

0 commit comments

Comments
 (0)