musique/lib/link/include/ableton/platforms/asio/AsioTimer.hpp
2023-01-06 16:53:58 +01:00

133 lines
3.5 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
* If you would like to incorporate Link into a proprietary software application,
* please contact <link-devs@ableton.com>.
*/
#pragma once
#include <ableton/platforms/asio/AsioWrapper.hpp>
#include <ableton/util/SafeAsyncHandler.hpp>
#include <functional>
namespace ableton
{
namespace platforms
{
namespace asio
{
// 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 = ::asio::error_code;
using TimePoint = std::chrono::system_clock::time_point;
AsioTimer(::asio::io_service& io)
: mpTimer(new ::asio::system_timer(io))
, mpAsyncHandler(std::make_shared<AsyncHandler>())
{
}
~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 <typename T>
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 <typename Handler>
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 <typename Handler>
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<void(const ErrorCode)> mpHandler;
};
std::unique_ptr<::asio::system_timer> mpTimer;
std::shared_ptr<AsyncHandler> mpAsyncHandler;
};
} // namespace asio
} // namespace platforms
} // namespace ableton