/* Copyright 2016, Ableton AG, Berlin. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * If you would like to incorporate Link into a proprietary software application, * please contact . */ #pragma once #include #include #include namespace ableton { namespace platforms { namespace LINK_ASIO_NAMESPACE { // This implementation is based on the boost::asio::system_timer concept. // Since boost::system_timer doesn't support move semantics, we create a wrapper // with a unique_ptr to get a movable type. It also handles an inconvenient // aspect of asio timers, which is that you must explicitly guard against the // handler firing after cancellation. We handle this by use of the SafeAsyncHandler // utility. AsioTimer therefore guarantees that a handler will not be called after // the destruction of the timer, or after the timer has been canceled. class AsioTimer { public: using ErrorCode = ::LINK_ASIO_NAMESPACE::error_code; using TimePoint = std::chrono::system_clock::time_point; using IoService = ::LINK_ASIO_NAMESPACE::io_service; using SystemTimer = ::LINK_ASIO_NAMESPACE::system_timer; AsioTimer(IoService& io) : mpTimer(new SystemTimer(io)) , mpAsyncHandler(std::make_shared()) { } ~AsioTimer() { // The timer may not be valid anymore if this instance was moved from if (mpTimer != nullptr) { // Ignore errors during cancellation cancel(); } } AsioTimer(const AsioTimer&) = delete; AsioTimer& operator=(const AsioTimer&) = delete; // Enable move construction but not move assignment. Move assignment // would get weird - would have to handle outstanding handlers AsioTimer(AsioTimer&& rhs) : mpTimer(std::move(rhs.mpTimer)) , mpAsyncHandler(std::move(rhs.mpAsyncHandler)) { } void expires_at(std::chrono::system_clock::time_point tp) { mpTimer->expires_at(std::move(tp)); } template void expires_from_now(T duration) { mpTimer->expires_from_now(std::move(duration)); } ErrorCode cancel() { ErrorCode ec; mpTimer->cancel(ec); mpAsyncHandler->mpHandler = nullptr; return ec; } template void async_wait(Handler handler) { *mpAsyncHandler = std::move(handler); mpTimer->async_wait(util::makeAsyncSafe(mpAsyncHandler)); } TimePoint now() const { return std::chrono::system_clock::now(); } private: struct AsyncHandler { template AsyncHandler& operator=(Handler handler) { mpHandler = [handler](ErrorCode ec) { handler(std::move(ec)); }; return *this; } void operator()(ErrorCode ec) { if (mpHandler) { mpHandler(std::move(ec)); } } std::function mpHandler; }; std::unique_ptr mpTimer; std::shared_ptr mpAsyncHandler; }; } // namespace LINK_ASIO_NAMESPACE } // namespace platforms } // namespace ableton