Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PeerGateways.hpp 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. * If you would like to incorporate Link into a proprietary software application,
  17. * please contact <link-devs@ableton.com>.
  18. */
  19. #pragma once
  20. #include <ableton/discovery/InterfaceScanner.hpp>
  21. #include <ableton/platforms/asio/AsioWrapper.hpp>
  22. #include <map>
  23. namespace ableton
  24. {
  25. namespace discovery
  26. {
  27. // GatewayFactory must have an operator()(NodeState, IoRef, asio::ip::address)
  28. // that constructs a new PeerGateway on a given interface address.
  29. template <typename NodeState, typename GatewayFactory, typename IoContext>
  30. class PeerGateways
  31. {
  32. public:
  33. using IoType = typename util::Injected<IoContext>::type;
  34. using Gateway = typename std::result_of<GatewayFactory(
  35. NodeState, util::Injected<IoType&>, asio::ip::address)>::type;
  36. using GatewayMap = std::map<asio::ip::address, Gateway>;
  37. PeerGateways(const std::chrono::seconds rescanPeriod,
  38. NodeState state,
  39. GatewayFactory factory,
  40. util::Injected<IoContext> io)
  41. : mIo(std::move(io))
  42. {
  43. mpScannerCallback =
  44. std::make_shared<Callback>(std::move(state), std::move(factory), *mIo);
  45. mpScanner = std::make_shared<Scanner>(
  46. rescanPeriod, util::injectShared(mpScannerCallback), util::injectRef(*mIo));
  47. }
  48. ~PeerGateways()
  49. {
  50. // Release the callback in the io thread so that gateway cleanup
  51. // doesn't happen in the client thread
  52. mIo->async(Deleter{*this});
  53. }
  54. PeerGateways(const PeerGateways&) = delete;
  55. PeerGateways& operator=(const PeerGateways&) = delete;
  56. PeerGateways(PeerGateways&&) = delete;
  57. PeerGateways& operator=(PeerGateways&&) = delete;
  58. void enable(const bool bEnable)
  59. {
  60. auto pCallback = mpScannerCallback;
  61. auto pScanner = mpScanner;
  62. if (pCallback && pScanner)
  63. {
  64. mIo->async([pCallback, pScanner, bEnable] {
  65. pCallback->mGateways.clear();
  66. pScanner->enable(bEnable);
  67. });
  68. }
  69. }
  70. template <typename Handler>
  71. void withGatewaysAsync(Handler handler)
  72. {
  73. auto pCallback = mpScannerCallback;
  74. if (pCallback)
  75. {
  76. mIo->async([pCallback, handler] {
  77. handler(pCallback->mGateways.begin(), pCallback->mGateways.end());
  78. });
  79. }
  80. }
  81. void updateNodeState(const NodeState& state)
  82. {
  83. auto pCallback = mpScannerCallback;
  84. if (pCallback)
  85. {
  86. mIo->async([pCallback, state] {
  87. pCallback->mState = state;
  88. for (const auto& entry : pCallback->mGateways)
  89. {
  90. entry.second->updateNodeState(state);
  91. }
  92. });
  93. }
  94. }
  95. // If a gateway has become non-responsive or is throwing exceptions,
  96. // this method can be invoked to either fix it or discard it.
  97. void repairGateway(const asio::ip::address& gatewayAddr)
  98. {
  99. auto pCallback = mpScannerCallback;
  100. auto pScanner = mpScanner;
  101. if (pCallback && pScanner)
  102. {
  103. mIo->async([pCallback, pScanner, gatewayAddr] {
  104. if (pCallback->mGateways.erase(gatewayAddr))
  105. {
  106. // If we erased a gateway, rescan again immediately so that
  107. // we will re-initialize it if it's still present
  108. pScanner->scan();
  109. }
  110. });
  111. }
  112. }
  113. private:
  114. struct Callback
  115. {
  116. Callback(NodeState state, GatewayFactory factory, IoType& io)
  117. : mState(std::move(state))
  118. , mFactory(std::move(factory))
  119. , mIo(io)
  120. {
  121. }
  122. template <typename AddrRange>
  123. void operator()(const AddrRange& range)
  124. {
  125. using namespace std;
  126. // Get the set of current addresses.
  127. vector<asio::ip::address> curAddrs;
  128. curAddrs.reserve(mGateways.size());
  129. transform(std::begin(mGateways), std::end(mGateways), back_inserter(curAddrs),
  130. [](const typename GatewayMap::value_type& vt) { return vt.first; });
  131. // Now use set_difference to determine the set of addresses that
  132. // are new and the set of cur addresses that are no longer there
  133. vector<asio::ip::address> newAddrs;
  134. set_difference(std::begin(range), std::end(range), std::begin(curAddrs),
  135. std::end(curAddrs), back_inserter(newAddrs));
  136. vector<asio::ip::address> staleAddrs;
  137. set_difference(std::begin(curAddrs), std::end(curAddrs), std::begin(range),
  138. std::end(range), back_inserter(staleAddrs));
  139. // Remove the stale addresses
  140. for (const auto& addr : staleAddrs)
  141. {
  142. mGateways.erase(addr);
  143. }
  144. // Add the new addresses
  145. for (const auto& addr : newAddrs)
  146. {
  147. try
  148. {
  149. // Only handle v4 for now
  150. if (addr.is_v4())
  151. {
  152. info(mIo.log()) << "initializing peer gateway on interface " << addr;
  153. mGateways.emplace(addr, mFactory(mState, util::injectRef(mIo), addr.to_v4()));
  154. }
  155. }
  156. catch (const runtime_error& e)
  157. {
  158. warning(mIo.log()) << "failed to init gateway on interface " << addr
  159. << " reason: " << e.what();
  160. }
  161. }
  162. }
  163. NodeState mState;
  164. GatewayFactory mFactory;
  165. IoType& mIo;
  166. GatewayMap mGateways;
  167. };
  168. using Scanner = InterfaceScanner<std::shared_ptr<Callback>, IoType&>;
  169. struct Deleter
  170. {
  171. Deleter(PeerGateways& gateways)
  172. : mpScannerCallback(std::move(gateways.mpScannerCallback))
  173. , mpScanner(std::move(gateways.mpScanner))
  174. {
  175. }
  176. void operator()()
  177. {
  178. mpScanner.reset();
  179. mpScannerCallback.reset();
  180. }
  181. std::shared_ptr<Callback> mpScannerCallback;
  182. std::shared_ptr<Scanner> mpScanner;
  183. };
  184. std::shared_ptr<Callback> mpScannerCallback;
  185. std::shared_ptr<Scanner> mpScanner;
  186. util::Injected<IoContext> mIo;
  187. };
  188. // Factory function
  189. template <typename NodeState, typename GatewayFactory, typename IoContext>
  190. std::unique_ptr<PeerGateways<NodeState, GatewayFactory, IoContext>> makePeerGateways(
  191. const std::chrono::seconds rescanPeriod,
  192. NodeState state,
  193. GatewayFactory factory,
  194. util::Injected<IoContext> io)
  195. {
  196. using namespace std;
  197. using Gateways = PeerGateways<NodeState, GatewayFactory, IoContext>;
  198. return unique_ptr<Gateways>{
  199. new Gateways{rescanPeriod, move(state), move(factory), move(io)}};
  200. }
  201. } // namespace discovery
  202. } // namespace ableton