/* 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 #include #include #include #include #include #include #include namespace ableton { namespace link { template class MeasurementService { public: using IoType = util::Injected; using MeasurementInstance = Measurement; MeasurementService(discovery::IpAddress address, SessionId sessionId, GhostXForm ghostXForm, Clock clock, IoType io) : mClock(std::move(clock)) , mIo(std::move(io)) , mPingResponder(std::move(address), std::move(sessionId), std::move(ghostXForm), mClock, util::injectRef(*mIo)) { } MeasurementService(const MeasurementService&) = delete; MeasurementService(MeasurementService&&) = delete; void updateNodeState(const SessionId& sessionId, const GhostXForm& xform) { mPingResponder.updateNodeState(sessionId, xform); } discovery::UdpEndpoint endpoint() const { return mPingResponder.endpoint(); } // Measure the peer and invoke the handler with a GhostXForm template void measurePeer(const PeerState& state, const Handler handler) { using namespace std; const auto nodeId = state.nodeState.nodeId; auto addr = mPingResponder.endpoint().address(); auto callback = CompletionCallback{*this, nodeId, handler}; try { mMeasurementMap[nodeId] = std::unique_ptr(new MeasurementInstance{ state, std::move(callback), std::move(addr), mClock, mIo}); } catch (const runtime_error& err) { info(mIo->log()) << "gateway@" + addr.to_string() << " Failed to measure. Reason: " << err.what(); handler(GhostXForm{}); } } private: template struct CompletionCallback { void operator()(std::vector& data) { using namespace std; using std::chrono::microseconds; // Post this to the measurement service's IoContext so that we // don't delete the measurement object in its stack. Capture all // needed data separately from this, since this object may be // gone by the time the block gets executed. auto nodeId = mNodeId; auto handler = mHandler; auto& measurementMap = mMeasurementService.mMeasurementMap; const auto it = measurementMap.find(nodeId); if (it != measurementMap.end()) { if (data.empty()) { handler(GhostXForm{}); } else { handler(GhostXForm{1, microseconds(llround(median(data.begin(), data.end())))}); } measurementMap.erase(it); } } MeasurementService& mMeasurementService; NodeId mNodeId; Handler mHandler; }; // Make sure the measurement map outlives the IoContext so that the rest of // the members are guaranteed to be valid when any final handlers // are begin run. using MeasurementMap = std::map>; MeasurementMap mMeasurementMap; Clock mClock; IoType mIo; PingResponder mPingResponder; }; } // namespace link } // namespace ableton