// This file Copyright © Mnemosyne LLC. // It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. #include #include #include #include #ifdef _WIN32 #include #else #include #endif #include #include "libtransmission/timer.h" #include "libtransmission/timer-ev.h" #include "libtransmission/tr-assert.h" #include "libtransmission/utils-ev.h" using namespace std::literals; namespace libtransmission { class EvTimer final : public Timer { public: explicit EvTimer(struct event_base* base) : base_{ base } { } EvTimer(EvTimer&&) = delete; EvTimer(EvTimer const&) = delete; EvTimer& operator=(EvTimer&&) = delete; EvTimer& operator=(EvTimer const&) = delete; ~EvTimer() override = default; void stop() override { if (!is_running_) { return; } event_del(evtimer_.get()); is_running_ = false; } void start() override { if (is_running_) { return; } using namespace std::chrono; auto const secs = duration_cast(interval_); auto tv = timeval{}; tv.tv_sec = secs.count(); tv.tv_usec = static_cast(duration_cast(interval_ - secs).count()); evtimer_add(evtimer_.get(), &tv); is_running_ = true; } void set_callback(std::function callback) override { callback_ = std::move(callback); } [[nodiscard]] std::chrono::milliseconds interval() const noexcept override { return interval_; } void set_interval(std::chrono::milliseconds interval) override { TR_ASSERT_MSG(interval.count() > 0 || !is_repeating(), "repeating timers must have a positive interval"); if (interval_ == interval) { return; } interval_ = interval; applyChanges(); } [[nodiscard]] bool is_repeating() const noexcept override { return is_repeating_; } void set_repeating(bool repeating) override { if (is_repeating_ == repeating) { return; } is_repeating_ = repeating; applyChanges(); } private: [[nodiscard]] static constexpr short events(bool is_repeating) noexcept { return static_cast(EV_TIMEOUT | (is_repeating ? EV_PERSIST : 0)); } void applyChanges() { auto const old_events = event_get_events(evtimer_.get()); auto const new_events = events(is_repeating()); auto const was_running = isRunning(); if (was_running) { stop(); } if (new_events != old_events) { [[maybe_unused]] auto const val = event_assign(evtimer_.get(), base_, -1, new_events, &EvTimer::onTimer, this); TR_ASSERT(val == 0); } if (was_running) { start(); } } static void onTimer(evutil_socket_t /*unused*/, short /*unused*/, void* vself) { static_cast(vself)->handleTimer(); } void handleTimer() { is_running_ = is_repeating_; TR_ASSERT(callback_); callback_(); } [[nodiscard]] constexpr bool isRunning() const noexcept { return is_running_; } std::chrono::milliseconds interval_ = 100ms; bool is_repeating_ = false; bool is_running_ = false; std::function callback_; struct event_base* const base_; evhelpers::event_unique_ptr const evtimer_{ event_new(base_, -1, events(is_repeating_), &EvTimer::onTimer, this) }; }; std::unique_ptr EvTimerMaker::create() { return std::make_unique(event_base_); } } // namespace libtransmission