diff --git a/source/Makefile.mk b/source/Makefile.mk index 07d0e7695..5cb079bf5 100644 --- a/source/Makefile.mk +++ b/source/Makefile.mk @@ -119,7 +119,7 @@ endif ifeq ($(NOOPT),true) # No CPU-specific optimization flags -BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections +BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections -DBUILDING_CARLA_NOOPT endif ifeq ($(WIN32),true) @@ -218,10 +218,9 @@ HAVE_HYLIA = true endif endif -# FIXME make mingw compatible -# ifeq ($(WIN32),true) -# HAVE_HYLIA = true -# endif +ifeq ($(WIN32),true) +HAVE_HYLIA = true +endif ifeq ($(MACOS_OR_WIN32),true) HAVE_DGL = true diff --git a/source/modules/hylia/hylia.cpp b/source/modules/hylia/hylia.cpp index bd5800470..f9d130f34 100644 --- a/source/modules/hylia/hylia.cpp +++ b/source/modules/hylia/hylia.cpp @@ -64,6 +64,21 @@ public: outputLatency = latency; } + void setStartStopSyncEnabled(const bool enabled) + { + engine.setStartStopSyncEnabled(enabled); + } + + void startPlaying() + { + engine.startPlaying(); + } + + void stopPlaying() + { + engine.stopPlaying(); + } + void process(const uint32_t frames, LinkTimeInfo* const info) { const std::chrono::microseconds hostTime = hostTimeFilter.sampleTimeToHostTime(sampleTime) @@ -114,6 +129,21 @@ void hylia_set_output_latency(hylia_t* link, uint32_t latency) ((HyliaTransport*)link)->setOutputLatency(latency); } +void hylia_set_start_stop_sync_enabled(hylia_t* link, bool enabled) +{ + ((HyliaTransport*)link)->setStartStopSyncEnabled(enabled); +} + +void hylia_start_playing(hylia_t* link) +{ + ((HyliaTransport*)link)->startPlaying(); +} + +void hylia_stop_playing(hylia_t* link) +{ + ((HyliaTransport*)link)->stopPlaying(); +} + void hylia_process(hylia_t* link, uint32_t frames, hylia_time_info_t* info) { ((HyliaTransport*)link)->process(frames, (LinkTimeInfo*)info); diff --git a/source/modules/hylia/hylia.h b/source/modules/hylia/hylia.h index dcb833dfb..4c239523d 100644 --- a/source/modules/hylia/hylia.h +++ b/source/modules/hylia/hylia.h @@ -30,6 +30,7 @@ typedef struct _hylia_t hylia_t; typedef struct _hylia_time_info_t { double beatsPerBar, beatsPerMinute, beat, phase; + bool playing; } hylia_time_info_t; hylia_t* hylia_create(void); @@ -38,6 +39,9 @@ void hylia_process(hylia_t* link, uint32_t frames, hylia_time_info_t* info); void hylia_set_beats_per_bar(hylia_t* link, double beatsPerBar); void hylia_set_beats_per_minute(hylia_t* link, double beatsPerMinute); void hylia_set_output_latency(hylia_t* link, uint32_t latency); +void hylia_set_start_stop_sync_enabled(hylia_t* link, bool enabled); +void hylia_start_playing(hylia_t* link); +void hylia_stop_playing(hylia_t* link); void hylia_cleanup(hylia_t* link); #ifdef __cplusplus diff --git a/source/modules/hylia/link/AudioEngine.cpp b/source/modules/hylia/link/AudioEngine.cpp index ffcbd59d8..57e9fd6fa 100644 --- a/source/modules/hylia/link/AudioEngine.cpp +++ b/source/modules/hylia/link/AudioEngine.cpp @@ -19,10 +19,6 @@ #include "AudioEngine.hpp" -// Make sure to define this before is included for Windows -#define _USE_MATH_DEFINES -#include - namespace ableton { namespace link @@ -30,19 +26,38 @@ namespace link AudioEngine::AudioEngine(Link& link) : mLink(link) - , mSharedEngineData({0., false, 4.}) + , mSharedEngineData({0., false, false, 4., false}) + , mLockfreeEngineData(mSharedEngineData) + , mIsPlaying(false) +{ +} + +void AudioEngine::startPlaying() +{ + std::lock_guard lock(mEngineDataGuard); + mSharedEngineData.requestStart = true; +} + +void AudioEngine::stopPlaying() { + std::lock_guard lock(mEngineDataGuard); + mSharedEngineData.requestStop = true; +} + +bool AudioEngine::isPlaying() const +{ + return mLink.captureAppSessionState().isPlaying(); } double AudioEngine::beatTime() const { - const auto timeline = mLink.captureAppTimeline(); - return timeline.beatAtTime(mLink.clock().micros(), mSharedEngineData.quantum); + const auto sessionState = mLink.captureAppSessionState(); + return sessionState.beatAtTime(mLink.clock().micros(), mSharedEngineData.quantum); } void AudioEngine::setTempo(double tempo) { - const std::lock_guard lock(mEngineDataGuard); + std::lock_guard lock(mEngineDataGuard); mSharedEngineData.requestedTempo = tempo; } @@ -53,26 +68,38 @@ double AudioEngine::quantum() const void AudioEngine::setQuantum(double quantum) { - const std::lock_guard lock(mEngineDataGuard); + std::lock_guard lock(mEngineDataGuard); mSharedEngineData.quantum = quantum; - mSharedEngineData.resetBeatTime = true; +} + +bool AudioEngine::isStartStopSyncEnabled() const +{ + return mLink.isStartStopSyncEnabled(); +} + +void AudioEngine::setStartStopSyncEnabled(const bool enabled) +{ + mLink.enableStartStopSync(enabled); } AudioEngine::EngineData AudioEngine::pullEngineData() { auto engineData = EngineData{}; - if (mEngineDataGuard.try_lock()) { engineData.requestedTempo = mSharedEngineData.requestedTempo; mSharedEngineData.requestedTempo = 0; + engineData.requestStart = mSharedEngineData.requestStart; + mSharedEngineData.requestStart = false; + engineData.requestStop = mSharedEngineData.requestStop; + mSharedEngineData.requestStop = false; - engineData.resetBeatTime = mSharedEngineData.resetBeatTime; - engineData.quantum = mSharedEngineData.quantum; - mSharedEngineData.resetBeatTime = false; + mLockfreeEngineData.quantum = mSharedEngineData.quantum; + mLockfreeEngineData.startStopSyncOn = mSharedEngineData.startStopSyncOn; mEngineDataGuard.unlock(); } + engineData.quantum = mLockfreeEngineData.quantum; return engineData; } @@ -81,29 +108,44 @@ void AudioEngine::timelineCallback(const std::chrono::microseconds hostTime, Lin { const auto engineData = pullEngineData(); - auto timeline = mLink.captureAudioTimeline(); + auto sessionState = mLink.captureAudioSessionState(); + + if (engineData.requestStart) + { + sessionState.setIsPlaying(true, hostTime); + } + + if (engineData.requestStop) + { + sessionState.setIsPlaying(false, hostTime); + } - if (engineData.resetBeatTime) + if (!mIsPlaying && sessionState.isPlaying()) + { + // Reset the timeline so that beat 0 corresponds to the time when transport starts + sessionState.requestBeatAtStartPlayingTime(0, engineData.quantum); + mIsPlaying = true; + } + else if (mIsPlaying && !sessionState.isPlaying()) { - // Reset the timeline so that beat 0 lands at the beginning of - // this buffer and clear the flag. - timeline.requestBeatAtTime(0, hostTime, engineData.quantum); + mIsPlaying = false; } if (engineData.requestedTempo > 0) { // Set the newly requested tempo from the beginning of this buffer - timeline.setTempo(engineData.requestedTempo, hostTime); + sessionState.setTempo(engineData.requestedTempo, hostTime); } // Timeline modifications are complete, commit the results - mLink.commitAudioTimeline(timeline); + mLink.commitAudioSessionState(sessionState); - // Save timeline info + // Save session state info->beatsPerBar = engineData.quantum; - info->beatsPerMinute = timeline.tempo(); - info->beat = timeline.beatAtTime(hostTime, engineData.quantum); - info->phase = timeline.phaseAtTime(hostTime, engineData.quantum); + info->beatsPerMinute = sessionState.tempo(); + info->beat = sessionState.beatAtTime(hostTime, engineData.quantum); + info->phase = sessionState.phaseAtTime(hostTime, engineData.quantum); + info->playing = mIsPlaying; } } // namespace link diff --git a/source/modules/hylia/link/AudioEngine.hpp b/source/modules/hylia/link/AudioEngine.hpp index 4e970e1b4..f7374e63e 100644 --- a/source/modules/hylia/link/AudioEngine.hpp +++ b/source/modules/hylia/link/AudioEngine.hpp @@ -19,13 +19,27 @@ #pragma once -// Make sure to define this before is included for Windows +#ifdef LINK_PLATFORM_WINDOWS #define _USE_MATH_DEFINES +#include "mingw-std-threads/mingw.condition_variable.h" +#include "mingw-std-threads/mingw.mutex.h" +#include "mingw-std-threads/mingw.thread.h" +#if __BIG_ENDIAN__ +# define htonll(x) (x) +# define ntohll(x) (x) +#else +# define htonll(x) ((uint64_t)htonl(((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) +# define ntohll(x) ((uint64_t)ntohl(((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) +#endif +#endif + +#include + #include "ableton/Link.hpp" -#include struct LinkTimeInfo { double beatsPerBar, beatsPerMinute, beat, phase; + bool playing; }; namespace ableton @@ -37,10 +51,15 @@ class AudioEngine { public: AudioEngine(Link& link); + void startPlaying(); + void stopPlaying(); + bool isPlaying() const; double beatTime() const; void setTempo(double tempo); double quantum() const; void setQuantum(double quantum); + bool isStartStopSyncEnabled() const; + void setStartStopSyncEnabled(bool enabled); void timelineCallback(const std::chrono::microseconds hostTime, LinkTimeInfo* const info); @@ -48,14 +67,18 @@ private: struct EngineData { double requestedTempo; - bool resetBeatTime; + bool requestStart; + bool requestStop; double quantum; + bool startStopSyncOn; }; EngineData pullEngineData(); Link& mLink; EngineData mSharedEngineData; + EngineData mLockfreeEngineData; + bool mIsPlaying; std::mutex mEngineDataGuard; }; diff --git a/source/modules/hylia/link/ableton/Link.hpp b/source/modules/hylia/link/ableton/Link.hpp index f60266d05..a5b76c5e7 100644 --- a/source/modules/hylia/link/ableton/Link.hpp +++ b/source/modules/hylia/link/ableton/Link.hpp @@ -32,13 +32,20 @@ namespace ableton /*! @class Link * @brief Class that represents a participant in a Link session. * - * @discussion Each Link instance has its own beat timeline that - * starts running from beat 0 at the initial tempo when - * constructed. A Link instance is initially disabled after - * construction, which means that it will not communicate on the - * network. Once enabled, a Link instance initiates network - * communication in an effort to discover other peers. When peers are - * discovered, they immediately become part of a shared Link session. + * @discussion Each Link instance has its own session state which + * represents a beat timeline and a transport start/stop state. The + * timeline starts running from beat 0 at the initial tempo when + * constructed. The timeline always advances at a speed defined by + * its current tempo, even if transport is stopped. Synchronizing to the + * transport start/stop state of Link is optional for every peer. + * The transport start/stop state is only shared with other peers when + * start/stop synchronization is enabled. + * + * A Link instance is initially disabled after construction, which + * means that it will not communicate on the network. Once enabled, + * a Link instance initiates network communication in an effort to + * discover other peers. When peers are discovered, they immediately + * become part of a shared Link session. * * Each method of the Link type documents its thread-safety and * realtime-safety properties. When a method is marked thread-safe, @@ -47,31 +54,31 @@ namespace ableton * it does not block and is appropriate for use in the thread that * performs audio IO. * - * Link provides one Timeline capture/commit method pair for use in the - * audio thread and one for all other application contexts. In - * general, modifying the Link timeline should be done in the audio + * Link provides one session state capture/commit method pair for use + * in the audio thread and one for all other application contexts. In + * general, modifying the session state should be done in the audio * thread for the most accurate timing results. The ability to modify - * the Link timeline from application threads should only be used in + * the session state from application threads should only be used in * cases where an application's audio thread is not actively running - * or if it doesn't generate audio at all. Modifying the Link - * timeline from both the audio thread and an application thread - * concurrently is not advised and will potentially lead to - * unexpected behavior. + * or if it doesn't generate audio at all. Modifying the Link session + * state from both the audio thread and an application thread + * concurrently is not advised and will potentially lead to unexpected + * behavior. */ -template -class BasicLink +class Link { public: - class Timeline; + using Clock = link::platform::Clock; + class SessionState; /*! @brief Construct with an initial tempo. */ - BasicLink(double bpm); + Link(double bpm); /*! @brief Link instances cannot be copied or moved */ - BasicLink(const BasicLink&) = delete; - BasicLink& operator=(const BasicLink&) = delete; - BasicLink(BasicLink&&) = delete; - BasicLink& operator=(BasicLink&&) = delete; + Link(const Link&) = delete; + Link& operator=(const Link&) = delete; + Link(Link&&) = delete; + Link& operator=(Link&&) = delete; /*! @brief Is Link currently enabled? * Thread-safe: yes @@ -85,6 +92,18 @@ public: */ void enable(bool bEnable); + /*! @brief: Is start/stop synchronization enabled? + * Thread-safe: yes + * Realtime-safe: no + */ + bool isStartStopSyncEnabled() const; + + /*! @brief: Enable start/stop synchronization. + * Thread-safe: yes + * Realtime-safe: no + */ + void enableStartStopSync(bool bEnable); + /*! @brief How many peers are currently connected in a Link session? * Thread-safe: yes * Realtime-safe: yes @@ -116,6 +135,19 @@ public: template void setTempoCallback(Callback callback); + /*! brief: Register a callback to be notified when the state of + * start/stop isPlaying changes. + * Thread-safe: yes + * Realtime-safe: no + * + * @discussion The callback is invoked on a Link-managed thread. + * + * @param callback The callback signature is: + * void (bool isPlaying) + */ + template + void setStartStopCallback(Callback callback); + /*! @brief The clock used by Link. * Thread-safe: yes * Realtime-safe: yes @@ -130,69 +162,81 @@ public: */ Clock clock() const; - /*! @brief Capture the current Link timeline from the audio thread. + /*! @brief Capture the current Link Session State from the audio thread. * Thread-safe: no * Realtime-safe: yes * * @discussion This method should ONLY be called in the audio thread * and must not be accessed from any other threads. The returned - * Timeline stores a snapshot of the current Link state, so it + * object stores a snapshot of the current Link Session State, so it * should be captured and used in a local scope. Storing the - * Timeline for later use in a different context is not advised - * because it will provide an outdated view on the Link state. + * Session State for later use in a different context is not advised + * because it will provide an outdated view. */ - Timeline captureAudioTimeline() const; + SessionState captureAudioSessionState() const; - /*! @brief Commit the given timeline to the Link session from the + /*! @brief Commit the given Session State to the Link session from the * audio thread. * Thread-safe: no * Realtime-safe: yes * * @discussion This method should ONLY be called in the audio - * thread. The given timeline will replace the current Link - * timeline. Modifications to the session based on the new timeline - * will be communicated to other peers in the session. + * thread. The given Session State will replace the current Link + * state. Modifications will be communicated to other peers in the + * session. */ - void commitAudioTimeline(Timeline timeline); + void commitAudioSessionState(SessionState state); - /*! @brief Capture the current Link timeline from an application + /*! @brief Capture the current Link Session State from an application * thread. * Thread-safe: yes * Realtime-safe: no * - * @discussion Provides a mechanism for capturing the Link timeline - * from an application thread (other than the audio thread). The - * returned Timeline stores a snapshot of the current Link state, - * so it should be captured and used in a local scope. Storing the - * Timeline for later use in a different context is not advised - * because it will provide an outdated view on the Link state. + * @discussion Provides a mechanism for capturing the Link Session + * State from an application thread (other than the audio thread). + * The returned Session State stores a snapshot of the current Link + * state, so it should be captured and used in a local scope. + * Storing the it for later use in a different context is not + * advised because it will provide an outdated view. */ - Timeline captureAppTimeline() const; + SessionState captureAppSessionState() const; - /*! @brief Commit the given timeline to the Link session from an + /*! @brief Commit the given Session State to the Link session from an * application thread. * Thread-safe: yes * Realtime-safe: no * - * @discussion The given timeline will replace the current Link - * timeline. Modifications to the session based on the new timeline - * will be communicated to other peers in the session. + * @discussion The given Session State will replace the current Link + * Session State. Modifications of the Session State will be + * communicated to other peers in the session. */ - void commitAppTimeline(Timeline timeline); + void commitAppSessionState(SessionState state); - /*! @class Timeline - * @brief Representation of a mapping between time and beats for - * varying quanta. + /*! @class SessionState + * @brief Representation of a timeline and the start/stop state + * + * @discussion A SessionState object is intended for use in a local scope within + * a single thread - none of its methods are thread-safe. All of its methods are + * non-blocking, so it is safe to use from a realtime thread. + * It provides functions to observe and manipulate the timeline and start/stop + * state. * - * @discussion A Timeline object is intended for use in a local - * scope within a single thread - none of its methods are - * thread-safe. All of its methods are non-blocking, so it is safe - * to use from a realtime thread. + * The timeline is a representation of a mapping between time and beats for varying + * quanta. + * The start/stop state represents the user intention to start or stop transport at + * a specific time. Start stop synchronization is an optional feature that allows to + * share the user request to start or stop transport between a subgroup of peers in + * a Link session. When observing a change of start/stop state, audio playback of a + * peer should be started or stopped the same way it would have happened if the user + * had requested that change at the according time locally. The start/stop state can + * only be changed by the user. This means that the current local start/stop state + * persists when joining or leaving a Link session. After joining a Link session + * start/stop change requests will be communicated to all connected peers. */ - class Timeline + class SessionState { public: - Timeline(const link::Timeline timeline, const bool bRespectQuantum); + SessionState(const link::ApiState state, const bool bRespectQuantum); /*! @brief: The tempo of the timeline, in bpm */ double tempo() const; @@ -286,29 +330,45 @@ public: */ void forceBeatAtTime(double beat, std::chrono::microseconds time, double quantum); - private: - friend BasicLink; + /*! @brief: Set if transport should be playing or stopped, taking effect + * at the given time. + */ + void setIsPlaying(bool isPlaying, std::chrono::microseconds time); + + /*! @brief: Is transport playing? */ + bool isPlaying() const; + + /*! @brief: Get the time at which a transport start/stop occurs */ + std::chrono::microseconds timeForIsPlaying() const; - link::Timeline mOriginalTimeline; + /*! @brief: Convenience function to attempt to map the given beat to the time + * when transport is starting to play in context of the given quantum. + * This function evaluates to a no-op if isPlaying() equals false. + */ + void requestBeatAtStartPlayingTime(double beat, double quantum); + + /*! @brief: Convenience function to start or stop transport at a given time and + * attempt to map the given beat to this time in context of the given quantum. + */ + void setIsPlayingAndRequestBeatAtTime( + bool isPlaying, std::chrono::microseconds time, double beat, double quantum); + + private: + friend Link; + link::ApiState mOriginalState; + link::ApiState mState; bool mbRespectQuantum; - link::Timeline mTimeline; }; private: - using Controller = ableton::link::Controller; - std::mutex mCallbackMutex; link::PeerCountCallback mPeerCountCallback; link::TempoCallback mTempoCallback; + link::StartStopStateCallback mStartStopCallback; Clock mClock; - Controller mController; + link::platform::Controller mController; }; -using Link = BasicLink; - -} // ableton +} // namespace ableton #include diff --git a/source/modules/hylia/link/ableton/Link.ipp b/source/modules/hylia/link/ableton/Link.ipp index 6943b7f71..381bcd7a9 100644 --- a/source/modules/hylia/link/ableton/Link.ipp +++ b/source/modules/hylia/link/ableton/Link.ipp @@ -23,11 +23,40 @@ namespace ableton { +namespace detail +{ + +inline Link::SessionState toSessionState( + const link::ClientState& state, const bool isConnected) +{ + const auto time = state.timeline.fromBeats(state.startStopState.beats); + const auto startStopState = + link::ApiStartStopState{state.startStopState.isPlaying, time}; + return {{state.timeline, startStopState}, isConnected}; +} -template -inline BasicLink::BasicLink(const double bpm) +inline link::IncomingClientState toIncomingClientState(const link::ApiState& state, + const link::ApiState& originalState, + const std::chrono::microseconds timestamp) +{ + const auto timeline = originalState.timeline != state.timeline + ? link::OptionalTimeline{state.timeline} + : link::OptionalTimeline{}; + const auto startStopState = + originalState.startStopState != state.startStopState + ? link::OptionalStartStopState{{state.startStopState.isPlaying, + state.timeline.toBeats(state.startStopState.time), timestamp}} + : link::OptionalStartStopState{}; + return {timeline, startStopState, timestamp}; +} + +} // namespace detail + +inline Link::Link(const double bpm) : mPeerCountCallback([](std::size_t) {}) , mTempoCallback([](link::Tempo) {}) + , mStartStopCallback([](bool) {}) + , mClock{} , mController(link::Tempo(bpm), [this](const std::size_t peers) { std::lock_guard lock(mCallbackMutex); @@ -37,134 +66,134 @@ inline BasicLink::BasicLink(const double bpm) std::lock_guard lock(mCallbackMutex); mTempoCallback(tempo); }, + [this](const bool isPlaying) { + std::lock_guard lock(mCallbackMutex); + mStartStopCallback(isPlaying); + }, mClock, util::injectVal(link::platform::IoContext{})) { } -template -inline bool BasicLink::isEnabled() const +inline bool Link::isEnabled() const { return mController.isEnabled(); } -template -inline void BasicLink::enable(const bool bEnable) +inline void Link::enable(const bool bEnable) { mController.enable(bEnable); } -template -inline std::size_t BasicLink::numPeers() const +inline bool Link::isStartStopSyncEnabled() const +{ + return mController.isStartStopSyncEnabled(); +} + +inline void Link::enableStartStopSync(bool bEnable) +{ + mController.enableStartStopSync(bEnable); +} + +inline std::size_t Link::numPeers() const { return mController.numPeers(); } -template template -void BasicLink::setNumPeersCallback(Callback callback) +void Link::setNumPeersCallback(Callback callback) { std::lock_guard lock(mCallbackMutex); mPeerCountCallback = [callback](const std::size_t numPeers) { callback(numPeers); }; } -template template -void BasicLink::setTempoCallback(Callback callback) +void Link::setTempoCallback(Callback callback) { std::lock_guard lock(mCallbackMutex); mTempoCallback = [callback](const link::Tempo tempo) { callback(tempo.bpm()); }; } -template -inline Clock BasicLink::clock() const +template +void Link::setStartStopCallback(Callback callback) +{ + std::lock_guard lock(mCallbackMutex); + mStartStopCallback = callback; +} + +inline Link::Clock Link::clock() const { return mClock; } -template -inline typename BasicLink::Timeline BasicLink::captureAudioTimeline() const +inline Link::SessionState Link::captureAudioSessionState() const { - return BasicLink::Timeline{mController.timelineRtSafe(), numPeers() > 0}; + return detail::toSessionState(mController.clientStateRtSafe(), numPeers() > 0); } -template -inline void BasicLink::commitAudioTimeline(const Timeline timeline) +inline void Link::commitAudioSessionState(const Link::SessionState state) { - if (timeline.mOriginalTimeline != timeline.mTimeline) - { - mController.setTimelineRtSafe(timeline.mTimeline, mClock.micros()); - } + mController.setClientStateRtSafe( + detail::toIncomingClientState(state.mState, state.mOriginalState, mClock.micros())); } -template -inline typename BasicLink::Timeline BasicLink::captureAppTimeline() const +inline Link::SessionState Link::captureAppSessionState() const { - return Timeline{mController.timeline(), numPeers() > 0}; + return detail::toSessionState(mController.clientState(), numPeers() > 0); } -template -inline void BasicLink::commitAppTimeline(const Timeline timeline) +inline void Link::commitAppSessionState(const Link::SessionState state) { - if (timeline.mOriginalTimeline != timeline.mTimeline) - { - mController.setTimeline(timeline.mTimeline, mClock.micros()); - } + mController.setClientState( + detail::toIncomingClientState(state.mState, state.mOriginalState, mClock.micros())); } -//////////////////// -// Link::Timeline // -//////////////////// +// Link::SessionState -template -inline BasicLink::Timeline::Timeline( - const link::Timeline timeline, const bool bRespectQuantum) - : mOriginalTimeline(timeline) +inline Link::SessionState::SessionState( + const link::ApiState state, const bool bRespectQuantum) + : mOriginalState(state) + , mState(state) , mbRespectQuantum(bRespectQuantum) - , mTimeline(timeline) { } -template -inline double BasicLink::Timeline::tempo() const +inline double Link::SessionState::tempo() const { - return mTimeline.tempo.bpm(); + return mState.timeline.tempo.bpm(); } -template -inline void BasicLink::Timeline::setTempo( +inline void Link::SessionState::setTempo( const double bpm, const std::chrono::microseconds atTime) { - const auto desiredTl = - link::clampTempo(link::Timeline{link::Tempo(bpm), mTimeline.toBeats(atTime), atTime}); - mTimeline.tempo = desiredTl.tempo; - mTimeline.timeOrigin = desiredTl.fromBeats(mTimeline.beatOrigin); + const auto desiredTl = link::clampTempo( + link::Timeline{link::Tempo(bpm), mState.timeline.toBeats(atTime), atTime}); + mState.timeline.tempo = desiredTl.tempo; + mState.timeline.timeOrigin = desiredTl.fromBeats(mState.timeline.beatOrigin); } -template -inline double BasicLink::Timeline::beatAtTime( +inline double Link::SessionState::beatAtTime( const std::chrono::microseconds time, const double quantum) const { - return link::toPhaseEncodedBeats(mTimeline, time, link::Beats{quantum}).floating(); + return link::toPhaseEncodedBeats(mState.timeline, time, link::Beats{quantum}) + .floating(); } -template -inline double BasicLink::Timeline::phaseAtTime( +inline double Link::SessionState::phaseAtTime( const std::chrono::microseconds time, const double quantum) const { return link::phase(link::Beats{beatAtTime(time, quantum)}, link::Beats{quantum}) .floating(); } -template -inline std::chrono::microseconds BasicLink::Timeline::timeAtBeat( +inline std::chrono::microseconds Link::SessionState::timeAtBeat( const double beat, const double quantum) const { - return link::fromPhaseEncodedBeats(mTimeline, link::Beats{beat}, link::Beats{quantum}); + return link::fromPhaseEncodedBeats( + mState.timeline, link::Beats{beat}, link::Beats{quantum}); } -template -inline void BasicLink::Timeline::requestBeatAtTime( +inline void Link::SessionState::requestBeatAtTime( const double beat, std::chrono::microseconds time, const double quantum) { if (mbRespectQuantum) @@ -177,8 +206,7 @@ inline void BasicLink::Timeline::requestBeatAtTime( forceBeatAtTime(beat, time, quantum); } -template -inline void BasicLink::Timeline::forceBeatAtTime( +inline void Link::SessionState::forceBeatAtTime( const double beat, const std::chrono::microseconds time, const double quantum) { // There are two components to the beat adjustment: a phase shift @@ -186,9 +214,42 @@ inline void BasicLink::Timeline::forceBeatAtTime( const auto curBeatAtTime = link::Beats{beatAtTime(time, quantum)}; const auto closestInPhase = link::closestPhaseMatch(curBeatAtTime, link::Beats{beat}, link::Beats{quantum}); - mTimeline = shiftClientTimeline(mTimeline, closestInPhase - curBeatAtTime); + mState.timeline = shiftClientTimeline(mState.timeline, closestInPhase - curBeatAtTime); // Now adjust the magnitude - mTimeline.beatOrigin = mTimeline.beatOrigin + (link::Beats{beat} - closestInPhase); + mState.timeline.beatOrigin = + mState.timeline.beatOrigin + (link::Beats{beat} - closestInPhase); +} + +inline void Link::SessionState::setIsPlaying( + const bool isPlaying, const std::chrono::microseconds time) +{ + mState.startStopState = {isPlaying, time}; +} + +inline bool Link::SessionState::isPlaying() const +{ + return mState.startStopState.isPlaying; +} + +inline std::chrono::microseconds Link::SessionState::timeForIsPlaying() const +{ + return mState.startStopState.time; +} + +inline void Link::SessionState::requestBeatAtStartPlayingTime( + const double beat, const double quantum) +{ + if (isPlaying()) + { + requestBeatAtTime(beat, mState.startStopState.time, quantum); + } +} + +inline void Link::SessionState::setIsPlayingAndRequestBeatAtTime( + bool isPlaying, std::chrono::microseconds time, double beat, double quantum) +{ + mState.startStopState = {isPlaying, time}; + requestBeatAtStartPlayingTime(beat, quantum); } -} // ableton +} // namespace ableton diff --git a/source/modules/hylia/link/ableton/discovery/IpV4Interface.hpp b/source/modules/hylia/link/ableton/discovery/IpV4Interface.hpp index ad02fe32f..9967f5125 100644 --- a/source/modules/hylia/link/ableton/discovery/IpV4Interface.hpp +++ b/source/modules/hylia/link/ableton/discovery/IpV4Interface.hpp @@ -19,7 +19,7 @@ #pragma once -#include +#include #include namespace ableton @@ -29,7 +29,7 @@ namespace discovery inline asio::ip::udp::endpoint multicastEndpoint() { - return {asio::ip::address::from_string("224.76.78.75"), 20808}; + return {asio::ip::address_v4::from_string("224.76.78.75"), 20808}; } // Type tags for dispatching between unicast and multicast packets diff --git a/source/modules/hylia/link/ableton/discovery/NetworkByteStreamSerializable.hpp b/source/modules/hylia/link/ableton/discovery/NetworkByteStreamSerializable.hpp index dddf6d349..128271686 100644 --- a/source/modules/hylia/link/ableton/discovery/NetworkByteStreamSerializable.hpp +++ b/source/modules/hylia/link/ableton/discovery/NetworkByteStreamSerializable.hpp @@ -20,9 +20,9 @@ #pragma once #include -#if LINK_PLATFORM_MACOSX +#if defined(LINK_PLATFORM_MACOSX) #include -#elif LINK_PLATFORM_LINUX +#elif defined(LINK_PLATFORM_LINUX) #include #endif @@ -32,10 +32,10 @@ #include #include -#if LINK_PLATFORM_WINDOWS -#include -#include -#include +#if defined(LINK_PLATFORM_WINDOWS) +#include +#include +#include #endif namespace ableton @@ -80,7 +80,8 @@ struct Deserialize // Default size implementation. Works for primitive types. -template +template ::value>::type* = nullptr> std::uint32_t sizeInByteStream(T) { return sizeof(T); @@ -235,29 +236,53 @@ struct Deserialize } }; -// overloads for std::chrono durations -template -std::uint32_t sizeInByteStream(const std::chrono::duration dur) +// bool +inline std::uint32_t sizeInByteStream(bool) +{ + return sizeof(uint8_t); +} + +template +It toNetworkByteStream(bool bl, It out) +{ + return toNetworkByteStream(static_cast(bl), std::move(out)); +} + +template <> +struct Deserialize +{ + template + static std::pair fromNetworkByteStream(It begin, It end) + { + auto result = + Deserialize::fromNetworkByteStream(std::move(begin), std::move(end)); + return std::make_pair(result.first != 0, result.second); + } +}; + +// std::chrono::microseconds +inline std::uint32_t sizeInByteStream(const std::chrono::microseconds micros) { - return sizeInByteStream(dur.count()); + return sizeInByteStream(micros.count()); } -template -It toNetworkByteStream(const std::chrono::duration dur, It out) +template +It toNetworkByteStream(const std::chrono::microseconds micros, It out) { - return toNetworkByteStream(dur.count(), std::move(out)); + static_assert(sizeof(int64_t) == sizeof(std::chrono::microseconds::rep), + "The size of microseconds::rep must matche the size of int64_t."); + return toNetworkByteStream(static_cast(micros.count()), std::move(out)); } -template -struct Deserialize> +template <> +struct Deserialize { template - static std::pair, It> fromNetworkByteStream( - It begin, It end) + static std::pair fromNetworkByteStream(It begin, It end) { using namespace std; - auto result = Deserialize::fromNetworkByteStream(move(begin), move(end)); - return make_pair(std::chrono::duration{result.first}, result.second); + auto result = Deserialize::fromNetworkByteStream(move(begin), move(end)); + return make_pair(chrono::microseconds{result.first}, result.second); } }; @@ -305,7 +330,7 @@ BytesIt deserializeContainer(BytesIt bytesBegin, return bytesBegin; } -} // detail +} // namespace detail // Need specific overloads for each container type, but use above // utilities for common implementation @@ -341,12 +366,13 @@ struct Deserialize> template std::uint32_t sizeInByteStream(const std::vector& vec) { - return detail::containerSizeInByteStream(vec); + return sizeof(uint32_t) + detail::containerSizeInByteStream(vec); } template It toNetworkByteStream(const std::vector& vec, It out) { + out = toNetworkByteStream(static_cast(vec.size()), out); return detail::containerToNetworkByteStream(vec, std::move(out)); } @@ -358,17 +384,42 @@ struct Deserialize> It bytesBegin, It bytesEnd) { using namespace std; + auto result_size = + Deserialize::fromNetworkByteStream(move(bytesBegin), bytesEnd); vector result; - // Use the number of bytes remaining in the stream as the upper - // bound on the number of elements that could be deserialized - // since we don't have a better heuristic. - auto resultIt = detail::deserializeContainer(move(bytesBegin), move(bytesEnd), - back_inserter(result), static_cast(distance(bytesBegin, bytesEnd))); - + auto resultIt = detail::deserializeContainer( + move(result_size.second), move(bytesEnd), back_inserter(result), result_size.first); return make_pair(move(result), move(resultIt)); } }; +// 2-tuple +template +std::uint32_t sizeInByteStream(const std::tuple& tup) +{ + return sizeInByteStream(std::get<0>(tup)) + sizeInByteStream(std::get<1>(tup)); +} + +template +It toNetworkByteStream(const std::tuple& tup, It out) +{ + return toNetworkByteStream( + std::get<1>(tup), toNetworkByteStream(std::get<0>(tup), std::move(out))); +} + +template +struct Deserialize> +{ + template + static std::pair, It> fromNetworkByteStream(It begin, It end) + { + using namespace std; + auto xres = Deserialize::fromNetworkByteStream(begin, end); + auto yres = Deserialize::fromNetworkByteStream(xres.second, end); + return make_pair(make_tuple(move(xres.first), move(yres.first)), move(yres.second)); + } +}; + // 3-tuple template std::uint32_t sizeInByteStream(const std::tuple& tup) @@ -395,8 +446,7 @@ struct Deserialize> auto xres = Deserialize::fromNetworkByteStream(begin, end); auto yres = Deserialize::fromNetworkByteStream(xres.second, end); auto zres = Deserialize::fromNetworkByteStream(yres.second, end); - return make_pair( - std::tuple{move(xres.first), move(yres.first), move(zres.first)}, + return make_pair(make_tuple(move(xres.first), move(yres.first), move(zres.first)), move(zres.second)); } }; diff --git a/source/modules/hylia/link/ableton/discovery/Payload.hpp b/source/modules/hylia/link/ableton/discovery/Payload.hpp index d15b9f349..b313b3073 100644 --- a/source/modules/hylia/link/ableton/discovery/Payload.hpp +++ b/source/modules/hylia/link/ableton/discovery/Payload.hpp @@ -21,6 +21,7 @@ #include #include +#include #include namespace ableton diff --git a/source/modules/hylia/link/ableton/discovery/PeerGateway.hpp b/source/modules/hylia/link/ableton/discovery/PeerGateway.hpp index fe4846db0..87d07ed80 100644 --- a/source/modules/hylia/link/ableton/discovery/PeerGateway.hpp +++ b/source/modules/hylia/link/ableton/discovery/PeerGateway.hpp @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -216,11 +215,10 @@ PeerGateway makePeerGateway( // IpV4 gateway types template -using IpV4Messenger = - UdpMessenger::type&, - v1::kMaxMessageSize>, - StateQuery, - IoContext>; +using IpV4Messenger = UdpMessenger< + IpV4Interface::type&, v1::kMaxMessageSize>, + StateQuery, + IoContext>; template using IpV4Gateway = diff --git a/source/modules/hylia/link/ableton/discovery/Socket.hpp b/source/modules/hylia/link/ableton/discovery/Socket.hpp deleted file mode 100644 index cbc59937d..000000000 --- a/source/modules/hylia/link/ableton/discovery/Socket.hpp +++ /dev/null @@ -1,139 +0,0 @@ -/* 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 - -namespace ableton -{ -namespace discovery -{ - -template -struct Socket -{ - Socket(platforms::asio::AsioService& io) - : mpImpl(std::make_shared(io)) - { - } - - Socket(const Socket&) = delete; - Socket& operator=(const Socket&) = delete; - - Socket(Socket&& rhs) - : mpImpl(std::move(rhs.mpImpl)) - { - } - - std::size_t send( - const uint8_t* const pData, const size_t numBytes, const asio::ip::udp::endpoint& to) - { - assert(numBytes < MaxPacketSize); - return mpImpl->mSocket.send_to(asio::buffer(pData, numBytes), to); - } - - template - void receive(Handler handler) - { - mpImpl->mHandler = std::move(handler); - mpImpl->mSocket.async_receive_from( - asio::buffer(mpImpl->mReceiveBuffer, MaxPacketSize), mpImpl->mSenderEndpoint, - util::makeAsyncSafe(mpImpl)); - } - - asio::ip::udp::endpoint endpoint() const - { - return mpImpl->mSocket.local_endpoint(); - } - - struct Impl - { - Impl(platforms::asio::AsioService& io) - : mSocket(io.mService, asio::ip::udp::v4()) - { - } - - ~Impl() - { - // Ignore error codes in shutdown and close as the socket may - // have already been forcibly closed - asio::error_code ec; - mSocket.shutdown(asio::ip::udp::socket::shutdown_both, ec); - mSocket.close(ec); - } - - void operator()(const asio::error_code& error, const std::size_t numBytes) - { - if (!error && numBytes > 0 && numBytes <= MaxPacketSize) - { - const auto bufBegin = begin(mReceiveBuffer); - mHandler(mSenderEndpoint, bufBegin, bufBegin + static_cast(numBytes)); - } - } - - asio::ip::udp::socket mSocket; - asio::ip::udp::endpoint mSenderEndpoint; - using Buffer = std::array; - Buffer mReceiveBuffer; - using ByteIt = typename Buffer::const_iterator; - std::function mHandler; - }; - - std::shared_ptr mpImpl; -}; - -// Configure an asio socket for receiving multicast messages -template -void configureMulticastSocket(Socket& socket, - const asio::ip::address_v4& addr, - const asio::ip::udp::endpoint& multicastEndpoint) -{ - socket.mpImpl->mSocket.set_option(asio::ip::udp::socket::reuse_address(true)); - // ??? - socket.mpImpl->mSocket.set_option(asio::socket_base::broadcast(!addr.is_loopback())); - // ??? - socket.mpImpl->mSocket.set_option( - asio::ip::multicast::enable_loopback(addr.is_loopback())); - socket.mpImpl->mSocket.set_option(asio::ip::multicast::outbound_interface(addr)); - // Is from_string("0.0.0.0") best approach? - socket.mpImpl->mSocket.bind( - {asio::ip::address::from_string("0.0.0.0"), multicastEndpoint.port()}); - socket.mpImpl->mSocket.set_option( - asio::ip::multicast::join_group(multicastEndpoint.address().to_v4(), addr)); -} - -// Configure an asio socket for receiving unicast messages -template -void configureUnicastSocket( - Socket& socket, const asio::ip::address_v4& addr) -{ - // ??? really necessary? - socket.mpImpl->mSocket.set_option( - asio::ip::multicast::enable_loopback(addr.is_loopback())); - socket.mpImpl->mSocket.set_option(asio::ip::multicast::outbound_interface(addr)); - socket.mpImpl->mSocket.bind(asio::ip::udp::endpoint{addr, 0}); -} - -} // namespace discovery -} // namespace ableton diff --git a/source/modules/hylia/link/ableton/discovery/UdpMessenger.hpp b/source/modules/hylia/link/ableton/discovery/UdpMessenger.hpp index ac58631a3..d98f28aaf 100644 --- a/source/modules/hylia/link/ableton/discovery/UdpMessenger.hpp +++ b/source/modules/hylia/link/ableton/discovery/UdpMessenger.hpp @@ -169,7 +169,7 @@ private: void setReceiveHandler(Handler handler) { mPeerStateHandler = [handler]( - PeerState state) { handler(std::move(state)); }; + PeerState state) { handler(std::move(state)); }; mByeByeHandler = [handler](ByeBye byeBye) { handler(std::move(byeBye)); }; } diff --git a/source/modules/hylia/link/ableton/discovery/test/Interface.hpp b/source/modules/hylia/link/ableton/discovery/test/Interface.hpp index 39d76bc16..f538856ff 100644 --- a/source/modules/hylia/link/ableton/discovery/test/Interface.hpp +++ b/source/modules/hylia/link/ableton/discovery/test/Interface.hpp @@ -42,8 +42,8 @@ public: template void receive(Callback callback, Tag tag) { - mCallback = [callback, tag]( - const asio::ip::udp::endpoint& from, const std::vector& buffer) { + mCallback = [callback, tag](const asio::ip::udp::endpoint& from, + const std::vector& buffer) { callback(tag, from, begin(buffer), end(buffer)); }; } diff --git a/source/modules/hylia/link/ableton/discovery/test/PayloadEntries.hpp b/source/modules/hylia/link/ableton/discovery/test/PayloadEntries.hpp index 5fc50b232..1962dad99 100644 --- a/source/modules/hylia/link/ableton/discovery/test/PayloadEntries.hpp +++ b/source/modules/hylia/link/ableton/discovery/test/PayloadEntries.hpp @@ -21,6 +21,7 @@ #include #include +#include #include namespace ableton @@ -35,10 +36,8 @@ namespace test // A fixed-size entry type struct Foo { - enum - { - key = '_foo' - }; + static const std::int32_t key = '_foo'; + static_assert(key == 0x5f666f6f, "Unexpected byte order"); std::int32_t fooVal; @@ -66,10 +65,8 @@ struct Foo // A variable-size entry type struct Bar { - enum - { - key = '_bar' - }; + static const std::int32_t key = '_bar'; + static_assert(key == 0x5f626172, "Unexpected byte order"); std::vector barVals; @@ -93,6 +90,44 @@ struct Bar } }; +// An entry type with two vectors +struct Foobar +{ + static const std::int32_t key = 'fbar'; + static_assert(key == 0x66626172, "Unexpected byte order"); + + using FoobarVector = std::vector; + using FoobarTuple = std::tuple; + + FoobarVector fooVals; + FoobarVector barVals; + + friend std::uint32_t sizeInByteStream(const Foobar& foobar) + { + return discovery::sizeInByteStream(foobar.asTuple()); + } + + template + friend It toNetworkByteStream(const Foobar& foobar, It out) + { + return discovery::toNetworkByteStream(foobar.asTuple(), out); + } + + template + static std::pair fromNetworkByteStream(It begin, It end) + { + const auto result = + Deserialize::fromNetworkByteStream(std::move(begin), std::move(end)); + const auto foobar = Foobar{std::get<0>(result.first), std::get<1>(result.first)}; + return std::make_pair(std::move(foobar), std::move(result.second)); + } + + FoobarTuple asTuple() const + { + return std::make_tuple(fooVals, barVals); + } +}; + } // namespace test } // namespace discovery } // namespace ableton diff --git a/source/modules/hylia/link/ableton/discovery/test/Socket.hpp b/source/modules/hylia/link/ableton/discovery/test/Socket.hpp index 01685f0b0..2983bcefb 100644 --- a/source/modules/hylia/link/ableton/discovery/test/Socket.hpp +++ b/source/modules/hylia/link/ableton/discovery/test/Socket.hpp @@ -53,7 +53,9 @@ public: void receive(Handler handler) { mCallback = [handler](const asio::ip::udp::endpoint& from, - const std::vector& buffer) { handler(from, begin(buffer), end(buffer)); }; + const std::vector& buffer) { + handler(from, begin(buffer), end(buffer)); + }; } template diff --git a/source/modules/hylia/link/ableton/link/CircularFifo.hpp b/source/modules/hylia/link/ableton/link/CircularFifo.hpp index f634701fd..5e3578f55 100644 --- a/source/modules/hylia/link/ableton/link/CircularFifo.hpp +++ b/source/modules/hylia/link/ableton/link/CircularFifo.hpp @@ -19,6 +19,7 @@ #pragma once +#include #include #include #include @@ -34,12 +35,6 @@ template class CircularFifo { public: - struct PoppedItem - { - Type item; - bool valid; - }; - CircularFifo() : tail(0) , head(0) @@ -60,32 +55,17 @@ public: return false; } - PoppedItem pop() + Optional pop() { const auto currentHead = head.load(); if (currentHead == tail.load()) { - return {Type{}, false}; + return {}; } auto item = data[currentHead]; head.store(nextIndex(currentHead)); - return {std::move(item), true}; - } - - PoppedItem clearAndPopLast() - { - auto hasData = false; - auto currentHead = head.load(); - while (currentHead != tail.load()) - { - currentHead = nextIndex(currentHead); - hasData = true; - } - - auto item = data[previousIndex(currentHead)]; - head.store(currentHead); - return {std::move(item), hasData}; + return Optional{std::move(item)}; } bool isEmpty() const @@ -109,5 +89,5 @@ private: std::array data; }; -} // link -} // ableton +} // namespace link +} // namespace ableton diff --git a/source/modules/hylia/link/ableton/link/ClientSessionTimelines.hpp b/source/modules/hylia/link/ableton/link/ClientSessionTimelines.hpp index 3cc72f736..345cd18b7 100644 --- a/source/modules/hylia/link/ableton/link/ClientSessionTimelines.hpp +++ b/source/modules/hylia/link/ableton/link/ClientSessionTimelines.hpp @@ -111,5 +111,5 @@ inline Timeline shiftClientTimeline(Timeline client, const Beats shift) return client; } -} // link -} // ableton +} // namespace link +} // namespace ableton diff --git a/source/modules/hylia/link/ableton/link/Controller.hpp b/source/modules/hylia/link/ableton/link/Controller.hpp index ed5d10738..99abc9dd4 100644 --- a/source/modules/hylia/link/ableton/link/Controller.hpp +++ b/source/modules/hylia/link/ableton/link/Controller.hpp @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include #include namespace ableton @@ -44,20 +46,79 @@ GhostXForm initXForm(const Clock& clock) return {1.0, -clock.micros()}; } +template +inline SessionState initSessionState(const Tempo tempo, const Clock& clock) +{ + using namespace std::chrono; + return {clampTempo(Timeline{tempo, Beats{0.}, microseconds{0}}), + StartStopState{false, Beats{0.}, microseconds{0}}, initXForm(clock)}; +} + +inline ClientState initClientState(const SessionState& sessionState) +{ + const auto hostTime = sessionState.ghostXForm.ghostToHost(std::chrono::microseconds{0}); + return { + Timeline{sessionState.timeline.tempo, sessionState.timeline.beatOrigin, hostTime}, + StartStopState{sessionState.startStopState.isPlaying, + sessionState.startStopState.beats, hostTime}}; +} + +inline RtClientState initRtClientState(const ClientState& clientState) +{ + using namespace std::chrono; + return { + clientState.timeline, clientState.startStopState, microseconds{0}, microseconds{0}}; +} + // The timespan in which local modifications to the timeline will be // preferred over any modifications coming from the network. const auto kLocalModGracePeriod = std::chrono::milliseconds(1000); const auto kRtHandlerFallbackPeriod = kLocalModGracePeriod / 2; +inline StartStopState selectPreferredStartStopState( + const StartStopState currentStartStopState, const StartStopState startStopState) +{ + return startStopState.timestamp > currentStartStopState.timestamp + ? startStopState + : currentStartStopState; +} + +inline StartStopState mapStartStopStateFromSessionToClient( + const StartStopState& sessionStartStopState, + const Timeline& clientTimeline, + const Timeline& sessionTimeline, + const GhostXForm& xForm) +{ + const auto clientBeats = clientTimeline.toBeats( + xForm.ghostToHost(sessionTimeline.fromBeats(sessionStartStopState.beats))); + const auto clientTime = xForm.ghostToHost(sessionStartStopState.timestamp); + return StartStopState{sessionStartStopState.isPlaying, clientBeats, clientTime}; +} + +inline StartStopState mapStartStopStateFromClientToSession( + const StartStopState& clientStartStopState, + const Timeline& clientTimeline, + const Timeline& sessionTimeline, + const GhostXForm& xForm) +{ + const auto sessionBeats = sessionTimeline.toBeats( + xForm.hostToGhost(clientTimeline.fromBeats(clientStartStopState.beats))); + const auto sessionTime = xForm.hostToGhost(clientStartStopState.timestamp); + return StartStopState{clientStartStopState.isPlaying, sessionBeats, sessionTime}; +} + } // namespace detail // function types corresponding to the Controller callback type params using PeerCountCallback = std::function; using TempoCallback = std::function; +using StartStopStateCallback = std::function; + // The main Link controller template class Controller @@ -66,33 +127,38 @@ public: Controller(Tempo tempo, PeerCountCallback peerCallback, TempoCallback tempoCallback, + StartStopStateCallback startStopStateCallback, Clock clock, util::Injected io) : mTempoCallback(std::move(tempoCallback)) + , mStartStopStateCallback(std::move(startStopStateCallback)) , mClock(std::move(clock)) , mNodeId(NodeId::random()) , mSessionId(mNodeId) - , mGhostXForm(detail::initXForm(mClock)) - , mSessionTimeline(clampTempo({tempo, Beats{0.}, std::chrono::microseconds{0}})) - , mClientTimeline({mSessionTimeline.tempo, Beats{0.}, - mGhostXForm.ghostToHost(std::chrono::microseconds{0})}) - , mRtClientTimeline(mClientTimeline) - , mRtClientTimelineTimestamp(0) + , mSessionState(detail::initSessionState(tempo, mClock)) + , mClientState(detail::initClientState(mSessionState)) + , mLastIsPlayingForStartStopStateCallback(false) + , mRtClientState(detail::initRtClientState(mClientState)) + , mHasPendingRtClientStates(false) , mSessionPeerCounter(*this, std::move(peerCallback)) , mEnabled(false) + , mStartStopSyncEnabled(false) , mIo(std::move(io)) - , mRtTimelineSetter(*this) + , mRtClientStateSetter(*this) , mPeers(util::injectRef(*mIo), std::ref(mSessionPeerCounter), - SessionTimelineCallback{*this}) - , mSessions({mSessionId, mSessionTimeline, {mGhostXForm, mClock.micros()}}, + SessionTimelineCallback{*this}, + SessionStartStopStateCallback{*this}) + , mSessions( + {mSessionId, mSessionState.timeline, {mSessionState.ghostXForm, mClock.micros()}}, util::injectRef(mPeers), MeasurePeer{*this}, JoinSessionCallback{*this}, util::injectRef(*mIo), mClock) - , mDiscovery( - std::make_pair(NodeState{mNodeId, mSessionId, mSessionTimeline}, mGhostXForm), + , mDiscovery(std::make_pair(NodeState{mNodeId, mSessionId, mSessionState.timeline, + mSessionState.startStopState}, + mSessionState.ghostXForm), GatewayFactory{*this}, util::injectVal(mIo->clone(UdpSendExceptionHandler{*this}))) { @@ -126,102 +192,186 @@ public: return mEnabled; } + void enableStartStopSync(const bool bEnable) + { + mStartStopSyncEnabled = bEnable; + } + + bool isStartStopSyncEnabled() const + { + return mStartStopSyncEnabled; + } + std::size_t numPeers() const { return mSessionPeerCounter.mSessionPeerCount; } - // Get the current Link timeline. Thread-safe but may block, so + // Get the current Link client state. Thread-safe but may block, so // it cannot be used from audio thread. - Timeline timeline() const + ClientState clientState() const { - std::lock_guard lock(mClientTimelineGuard); - return mClientTimeline; + std::lock_guard lock(mClientStateGuard); + return mClientState; } - // Set the timeline to be used, starting at the given - // time. Thread-safe but may block, so it cannot be used from audio thread. - void setTimeline(Timeline newTimeline, const std::chrono::microseconds atTime) + // Set the client state to be used, starting at the given time. + // Thread-safe but may block, so it cannot be used from audio thread. + void setClientState(IncomingClientState newClientState) { - newTimeline = clampTempo(newTimeline); { - std::lock_guard lock(mClientTimelineGuard); - mClientTimeline = newTimeline; + std::lock_guard lock(mClientStateGuard); + if (newClientState.timeline) + { + *newClientState.timeline = clampTempo(*newClientState.timeline); + mClientState.timeline = *newClientState.timeline; + } + if (newClientState.startStopState) + { + // Prevent updating client start stop state with an outdated start stop state + *newClientState.startStopState = detail::selectPreferredStartStopState( + mClientState.startStopState, *newClientState.startStopState); + mClientState.startStopState = *newClientState.startStopState; + } } - mIo->async([this, newTimeline, atTime] { - handleTimelineFromClient(updateSessionTimelineFromClient( - mSessionTimeline, newTimeline, atTime, mGhostXForm)); - }); + + mIo->async([this, newClientState] { handleClientState(newClientState); }); } - // Non-blocking timeline access for a realtime context. NOT + // Non-blocking client state access for a realtime context. NOT // thread-safe. Must not be called from multiple threads - // concurrently and must not be called concurrently with setTimelineRtSafe. - Timeline timelineRtSafe() const + // concurrently and must not be called concurrently with setClientStateRtSafe. + ClientState clientStateRtSafe() const { - // Respect the session timing guard but don't block on it. If we - // can't access it because it's being modified we fall back to our - // cached version of the timeline. - const auto now = mClock.micros(); - if (now - mRtClientTimelineTimestamp > detail::kLocalModGracePeriod - && !mRtTimelineSetter.hasPendingTimelines() && mSessionTimingGuard.try_lock()) + // Respect the session state guard and the client state guard but don't + // block on them. If we can't access one or both because of concurrent modification + // we fall back to our cached version of the timeline and/or start stop state. + + if (!mHasPendingRtClientStates) { - const auto clientTimeline = updateClientTimelineFromSession( - mRtClientTimeline, mSessionTimeline, now, mGhostXForm); + const auto now = mClock.micros(); + const auto timelineGracePeriodOver = + now - mRtClientState.timelineTimestamp > detail::kLocalModGracePeriod; + const auto startStopStateGracePeriodOver = + now - mRtClientState.startStopStateTimestamp > detail::kLocalModGracePeriod; + + if ((timelineGracePeriodOver || startStopStateGracePeriodOver) + && mClientStateGuard.try_lock()) + { + const auto clientState = mClientState; + mClientStateGuard.unlock(); - mSessionTimingGuard.unlock(); + if (timelineGracePeriodOver && clientState.timeline != mRtClientState.timeline) + { + mRtClientState.timeline = clientState.timeline; + } - if (clientTimeline != mRtClientTimeline) - { - mRtClientTimeline = clientTimeline; + if (startStopStateGracePeriodOver + && clientState.startStopState != mRtClientState.startStopState) + { + mRtClientState.startStopState = clientState.startStopState; + } } } - return mRtClientTimeline; + return {mRtClientState.timeline, mRtClientState.startStopState}; } // should only be called from the audio thread - void setTimelineRtSafe(Timeline newTimeline, const std::chrono::microseconds atTime) + void setClientStateRtSafe(IncomingClientState newClientState) { - newTimeline = clampTempo(newTimeline); + if (!newClientState.timeline && !newClientState.startStopState) + { + return; + } - // This will fail in case the Fifo in the RtTimelineSetter is full. This indicates a - // very high rate of calls to the setter. In this case we ignore one value because we - // expect the setter to be called again soon. - if (mRtTimelineSetter.tryPush(newTimeline, atTime)) + if (newClientState.timeline) { - // Cache the new timeline for serving back to the client - mRtClientTimeline = newTimeline; - mRtClientTimelineTimestamp = - isEnabled() ? mClock.micros() : std::chrono::microseconds(0); + *newClientState.timeline = clampTempo(*newClientState.timeline); + } + + if (newClientState.startStopState) + { + // Prevent updating client start stop state with an outdated start stop state + *newClientState.startStopState = detail::selectPreferredStartStopState( + mRtClientState.startStopState, *newClientState.startStopState); + } + + // This flag ensures that mRtClientState is only updated after all incoming + // client states were processed + mHasPendingRtClientStates = true; + // This will fail in case the Fifo in the RtClientStateSetter is full. This indicates + // a very high rate of calls to the setter. In this case we ignore one value because + // we expect the setter to be called again soon. + if (mRtClientStateSetter.tryPush(newClientState)) + { + const auto now = mClock.micros(); + // Cache the new timeline and StartStopState for serving back to the client + if (newClientState.timeline) + { + // Cache the new timeline and StartStopState for serving back to the client + mRtClientState.timeline = *newClientState.timeline; + mRtClientState.timelineTimestamp = makeRtTimestamp(now); + } + if (newClientState.startStopState) + { + mRtClientState.startStopState = *newClientState.startStopState; + mRtClientState.startStopStateTimestamp = makeRtTimestamp(now); + } } } private: + std::chrono::microseconds makeRtTimestamp(const std::chrono::microseconds now) const + { + return isEnabled() ? now : std::chrono::microseconds(0); + } + + void invokeStartStopStateCallbackIfChanged() + { + bool shouldInvokeCallback = false; + { + std::lock_guard lock(mClientStateGuard); + shouldInvokeCallback = + mLastIsPlayingForStartStopStateCallback != mClientState.startStopState.isPlaying; + mLastIsPlayingForStartStopStateCallback = mClientState.startStopState.isPlaying; + } + + if (shouldInvokeCallback) + { + mStartStopStateCallback(mLastIsPlayingForStartStopStateCallback); + } + } + + void updateDiscovery() + { + // Push the change to the discovery service + mDiscovery.updateNodeState( + std::make_pair(NodeState{mNodeId, mSessionId, mSessionState.timeline, + mSessionState.startStopState}, + mSessionState.ghostXForm)); + } + void updateSessionTiming(const Timeline newTimeline, const GhostXForm newXForm) { - const auto oldTimeline = mSessionTimeline; - const auto oldXForm = mGhostXForm; + const auto oldTimeline = mSessionState.timeline; + const auto oldXForm = mSessionState.ghostXForm; if (oldTimeline != newTimeline || oldXForm != newXForm) { { - std::lock_guard lock(mSessionTimingGuard); - mSessionTimeline = newTimeline; - mGhostXForm = newXForm; + std::lock_guard lock(mSessionStateGuard); + mSessionState.timeline = newTimeline; + mSessionState.ghostXForm = newXForm; } // Update the client timeline based on the new session timing data { - std::lock_guard lock(mClientTimelineGuard); - mClientTimeline = updateClientTimelineFromSession( - mClientTimeline, mSessionTimeline, mClock.micros(), mGhostXForm); + std::lock_guard lock(mClientStateGuard); + mClientState.timeline = updateClientTimelineFromSession(mClientState.timeline, + mSessionState.timeline, mClock.micros(), mSessionState.ghostXForm); } - // Push the change to the discovery service - mDiscovery.updateNodeState( - std::make_pair(NodeState{mNodeId, mSessionId, newTimeline}, newXForm)); - if (oldTimeline.tempo != newTimeline.tempo) { mTempoCallback(newTimeline.tempo); @@ -229,36 +379,126 @@ private: } } - void handleTimelineFromClient(Timeline tl) - { - mSessions.resetTimeline(tl); - mPeers.setSessionTimeline(mSessionId, tl); - updateSessionTiming(std::move(tl), mGhostXForm); - } - void handleTimelineFromSession(SessionId id, Timeline timeline) { debug(mIo->log()) << "Received timeline with tempo: " << timeline.tempo.bpm() << " for session: " << id; - updateSessionTiming( - mSessions.sawSessionTimeline(std::move(id), std::move(timeline)), mGhostXForm); + updateSessionTiming(mSessions.sawSessionTimeline(std::move(id), std::move(timeline)), + mSessionState.ghostXForm); + updateDiscovery(); + } + + void resetSessionStartStopState() + { + mSessionState.startStopState = StartStopState{}; + } + + void handleStartStopStateFromSession(SessionId sessionId, StartStopState startStopState) + { + debug(mIo->log()) << "Received start stop state. isPlaying: " + << startStopState.isPlaying + << ", beats: " << startStopState.beats.floating() + << ", time: " << startStopState.timestamp.count() + << " for session: " << sessionId; + if (sessionId == mSessionId + && startStopState.timestamp > mSessionState.startStopState.timestamp) + { + mSessionState.startStopState = startStopState; + + // Always propagate the session start stop state so even a client that doesn't have + // the feature enabled can function as a relay. + updateDiscovery(); + + if (mStartStopSyncEnabled) + { + { + std::lock_guard lock(mClientStateGuard); + mClientState.startStopState = + detail::mapStartStopStateFromSessionToClient(startStopState, + mClientState.timeline, mSessionState.timeline, mSessionState.ghostXForm); + } + invokeStartStopStateCallbackIfChanged(); + } + } + } + + void handleClientState(const IncomingClientState clientState) + { + auto mustUpdateDiscovery = false; + + if (clientState.timeline) + { + auto sessionTimeline = updateSessionTimelineFromClient(mSessionState.timeline, + *clientState.timeline, clientState.timelineTimestamp, mSessionState.ghostXForm); + + mSessions.resetTimeline(sessionTimeline); + mPeers.setSessionTimeline(mSessionId, sessionTimeline); + updateSessionTiming(std::move(sessionTimeline), mSessionState.ghostXForm); + + mustUpdateDiscovery = true; + } + + if (mStartStopSyncEnabled && clientState.startStopState) + { + // Prevent updating with an outdated start stop state + const auto newGhostTime = + mSessionState.ghostXForm.hostToGhost(clientState.startStopState->timestamp); + if (newGhostTime > mSessionState.startStopState.timestamp) + { + { + std::lock_guard lock(mClientStateGuard); + mSessionState.startStopState = + detail::mapStartStopStateFromClientToSession(*clientState.startStopState, + mClientState.timeline, mSessionState.timeline, mSessionState.ghostXForm); + mClientState.startStopState = *clientState.startStopState; + } + + mustUpdateDiscovery = true; + } + } + + if (mustUpdateDiscovery) + { + updateDiscovery(); + } + + invokeStartStopStateCallbackIfChanged(); } - void handleRtTimeline(const Timeline timeline, const std::chrono::microseconds time) + void handleRtClientState(IncomingClientState clientState) { { - std::lock_guard lock(mClientTimelineGuard); - mClientTimeline = timeline; + std::lock_guard lock(mClientStateGuard); + if (clientState.timeline) + { + mClientState.timeline = *clientState.timeline; + } + if (clientState.startStopState) + { + // Prevent updating client start stop state with an outdated start stop state + *clientState.startStopState = detail::selectPreferredStartStopState( + mClientState.startStopState, *clientState.startStopState); + mClientState.startStopState = *clientState.startStopState; + } } - handleTimelineFromClient( - updateSessionTimelineFromClient(mSessionTimeline, timeline, time, mGhostXForm)); + + handleClientState(clientState); + mHasPendingRtClientStates = false; } void joinSession(const Session& session) { const bool sessionIdChanged = mSessionId != session.sessionId; mSessionId = session.sessionId; + + // Prevent passing the start stop state of the previous session to the new one. + if (sessionIdChanged) + { + resetSessionStartStopState(); + } + updateSessionTiming(session.timeline, session.measurement.xform); + updateDiscovery(); if (sessionIdChanged) { @@ -279,11 +519,14 @@ private: // the beat on the old session timeline corresponding to the // current host time and mapping it to the new ghost time // representation of the current host time. - const auto newTl = Timeline{mSessionTimeline.tempo, - mSessionTimeline.toBeats(mGhostXForm.hostToGhost(hostTime)), + const auto newTl = Timeline{mSessionState.timeline.tempo, + mSessionState.timeline.toBeats(mSessionState.ghostXForm.hostToGhost(hostTime)), xform.hostToGhost(hostTime)}; + resetSessionStartStopState(); + updateSessionTiming(newTl, xform); + updateDiscovery(); mSessions.resetSession({mNodeId, newTl, {xform, hostTime}}); mPeers.resetPeers(); @@ -299,25 +542,22 @@ private: Controller& mController; }; - struct RtTimelineSetter + struct RtClientStateSetter { using CallbackDispatcher = typename IoContext::template LockFreeCallbackDispatcher, std::chrono::milliseconds>; - using RtTimeline = std::pair; - RtTimelineSetter(Controller& controller) + RtClientStateSetter(Controller& controller) : mController(controller) - , mHasPendingTimelines(false) , mCallbackDispatcher( - [this] { processPendingTimelines(); }, detail::kRtHandlerFallbackPeriod) + [this] { processPendingClientStates(); }, detail::kRtHandlerFallbackPeriod) { } - bool tryPush(const Timeline timeline, const std::chrono::microseconds time) + bool tryPush(const IncomingClientState clientState) { - mHasPendingTimelines = true; - const auto success = mFifo.push({timeline, time}); + const auto success = mClientStateFifo.push(clientState); if (success) { mCallbackDispatcher.invoke(); @@ -325,34 +565,51 @@ private: return success; } - bool hasPendingTimelines() const - { - return mHasPendingTimelines; - } - private: - void processPendingTimelines() + IncomingClientState buildMergedPendingClientState() { - auto result = mFifo.clearAndPopLast(); - - if (result.valid) + auto clientState = IncomingClientState{}; + while (const auto result = mClientStateFifo.pop()) { - auto timeline = std::move(result.item); - mController.mIo->async([this, timeline]() { - mController.handleRtTimeline(timeline.first, timeline.second); - mHasPendingTimelines = false; - }); + if (result->timeline) + { + clientState.timeline = result->timeline; + clientState.timelineTimestamp = result->timelineTimestamp; + } + if (result->startStopState) + { + clientState.startStopState = result->startStopState; + } } + return clientState; + } + + void processPendingClientStates() + { + const auto clientState = buildMergedPendingClientState(); + mController.mIo->async( + [this, clientState]() { mController.handleRtClientState(clientState); }); } Controller& mController; // Assuming a wake up time of one ms for the threads owned by the CallbackDispatcher - // and the ioService, buffering 16 timelines allows to set eight timelines per ms. - CircularFifo mFifo; - std::atomic mHasPendingTimelines; + // and the ioService, buffering 16 client states allows to set eight client states + // per ms. + static const std::size_t kBufferSize = 16; + CircularFifo mClientStateFifo; CallbackDispatcher mCallbackDispatcher; }; + struct SessionStartStopStateCallback + { + void operator()(SessionId sessionId, StartStopState startStopState) + { + mController.handleStartStopStateFromSession(sessionId, startStopState); + } + + Controller& mController; + }; + struct SessionPeerCounter { SessionPeerCounter(Controller& controller, PeerCountCallback callback) @@ -423,8 +680,10 @@ private: using IoType = typename util::Injected::type; - using ControllerPeers = - Peers, SessionTimelineCallback>; + using ControllerPeers = Peers, + SessionTimelineCallback, + SessionStartStopStateCallback>; using ControllerGateway = Gateway; @@ -464,28 +723,30 @@ private: }; TempoCallback mTempoCallback; + StartStopStateCallback mStartStopStateCallback; Clock mClock; NodeId mNodeId; SessionId mSessionId; - // Mutex that controls access to mGhostXForm and mSessionTimeline - mutable std::mutex mSessionTimingGuard; - GhostXForm mGhostXForm; - Timeline mSessionTimeline; + mutable std::mutex mSessionStateGuard; + SessionState mSessionState; - mutable std::mutex mClientTimelineGuard; - Timeline mClientTimeline; + mutable std::mutex mClientStateGuard; + ClientState mClientState; + bool mLastIsPlayingForStartStopStateCallback; - mutable Timeline mRtClientTimeline; - std::chrono::microseconds mRtClientTimelineTimestamp; + mutable RtClientState mRtClientState; + std::atomic mHasPendingRtClientStates; SessionPeerCounter mSessionPeerCounter; std::atomic mEnabled; + std::atomic mStartStopSyncEnabled; + util::Injected mIo; - RtTimelineSetter mRtTimelineSetter; + RtClientStateSetter mRtClientStateSetter; ControllerPeers mPeers; diff --git a/source/modules/hylia/link/ableton/link/Gateway.hpp b/source/modules/hylia/link/ableton/link/Gateway.hpp index 354bee68c..086abfa97 100644 --- a/source/modules/hylia/link/ableton/link/Gateway.hpp +++ b/source/modules/hylia/link/ableton/link/Gateway.hpp @@ -38,14 +38,13 @@ public: NodeState nodeState, GhostXForm ghostXForm, Clock clock) - // TODO: Measurement should have an IoContext injected - : mIo(std::move(io)), - mMeasurement(addr, + : mIo(std::move(io)) + , mMeasurement(addr, nodeState.sessionId, std::move(ghostXForm), std::move(clock), - util::injectVal(channel(mIo->log(), "gateway@" + addr.to_string()))), - mPeerGateway(discovery::makeIpV4Gateway(util::injectRef(*mIo), + util::injectVal(mIo->clone())) + , mPeerGateway(discovery::makeIpV4Gateway(util::injectRef(*mIo), std::move(addr), std::move(observer), PeerState{std::move(nodeState), mMeasurement.endpoint()})) @@ -84,7 +83,7 @@ public: private: util::Injected mIo; - MeasurementService::type::Log> mMeasurement; + MeasurementService::type> mMeasurement; discovery:: IpV4Gateway::type&> mPeerGateway; diff --git a/source/modules/hylia/link/ableton/link/Measurement.hpp b/source/modules/hylia/link/ableton/link/Measurement.hpp index 83bc0d956..c8d0f2b52 100644 --- a/source/modules/hylia/link/ableton/link/Measurement.hpp +++ b/source/modules/hylia/link/ableton/link/Measurement.hpp @@ -20,13 +20,12 @@ #pragma once #include -#include #include #include #include #include -#include #include +#include #include #include @@ -35,83 +34,57 @@ namespace ableton namespace link { -template +template struct Measurement { using Point = std::pair; using Callback = std::function)>; using Micros = std::chrono::microseconds; - using Timer = typename IoService::Timer; static const std::size_t kNumberDataPoints = 100; static const std::size_t kNumberMeasurements = 5; - Measurement() = default; - Measurement(const PeerState& state, Callback callback, asio::ip::address_v4 address, Clock clock, - util::Injected log) - : mpIo(new IoService{}) - , mpImpl(std::make_shared(*mpIo, - std::move(state), + IoContext io) + : mIo(std::move(io)) + , mpImpl(std::make_shared(std::move(state), std::move(callback), std::move(address), std::move(clock), - std::move(log))) + mIo)) { mpImpl->listen(); } - Measurement(Measurement&& rhs) - : mpIo(std::move(rhs.mpIo)) - , mpImpl(std::move(rhs.mpImpl)) - { - } - - ~Measurement() - { - postImplDestruction(); - } - - Measurement& operator=(Measurement&& rhs) - { - postImplDestruction(); - mpIo = std::move(rhs.mpIo); - mpImpl = std::move(rhs.mpImpl); - return *this; - } - - void postImplDestruction() - { - // Post destruction of the impl object into the io thread if valid - if (mpIo) - { - mpIo->post(ImplDeleter{*this}); - } - } + Measurement(const Measurement&) = delete; + Measurement& operator=(Measurement&) = delete; + Measurement(const Measurement&&) = delete; + Measurement& operator=(Measurement&&) = delete; struct Impl : std::enable_shared_from_this { - Impl(IoService& io, - const PeerState& state, + using Socket = typename IoContext::template Socket; + using Timer = typename IoContext::Timer; + using Log = typename IoContext::Log; + + Impl(const PeerState& state, Callback callback, asio::ip::address_v4 address, Clock clock, - util::Injected log) - : mpSocket(std::make_shared(io)) + IoContext& io) + : mSocket(io.template openUnicastSocket(address)) , mSessionId(state.nodeState.sessionId) , mEndpoint(state.endpoint) , mCallback(std::move(callback)) , mClock(std::move(clock)) - , mTimer(util::injectVal(io.makeTimer())) + , mTimer(io.makeTimer()) , mMeasurementsStarted(0) - , mLog(std::move(log)) + , mLog(channel(io.log(), "Measurement on gateway@" + address.to_string())) , mSuccess(false) { - configureUnicastSocket(*mpSocket, address); - const auto ht = HostTime{mClock.micros()}; sendPing(mEndpoint, discovery::makePayload(ht)); resetTimer(); @@ -119,9 +92,9 @@ struct Measurement void resetTimer() { - mTimer->cancel(); - mTimer->expires_from_now(std::chrono::milliseconds(50)); - mTimer->async_wait([this](const typename Timer::ErrorCode e) { + mTimer.cancel(); + mTimer.expires_from_now(std::chrono::milliseconds(50)); + mTimer.async_wait([this](const typename Timer::ErrorCode e) { if (!e) { if (mMeasurementsStarted < kNumberMeasurements) @@ -141,7 +114,7 @@ struct Measurement void listen() { - mpSocket->receive(util::makeAsyncSafe(this->shared_from_this())); + mSocket.receive(util::makeAsyncSafe(this->shared_from_this())); } // Operator to handle incoming messages on the interface @@ -156,7 +129,7 @@ struct Measurement if (header.messageType == v1::kPong) { - debug(*mLog) << "Received Pong message from " << from; + debug(mLog) << "Received Pong message from " << from; // parse for all entries SessionId sessionId{}; @@ -175,7 +148,7 @@ struct Measurement } catch (const std::runtime_error& err) { - warning(*mLog) << "Failed parsing payload, caught exception: " << err.what(); + warning(mLog) << "Failed parsing payload, caught exception: " << err.what(); listen(); return; } @@ -215,7 +188,7 @@ struct Measurement } else { - debug(*mLog) << "Received invalid message from " << from; + debug(mLog) << "Received invalid message from " << from; listen(); } } @@ -230,40 +203,40 @@ struct Measurement try { - mpSocket->send(buffer.data(), numBytes, to); + mSocket.send(buffer.data(), numBytes, to); } catch (const std::runtime_error& err) { - info(*mLog) << "Failed to send Ping to " << to.address().to_string() << ": " - << err.what(); + info(mLog) << "Failed to send Ping to " << to.address().to_string() << ": " + << err.what(); } } void finish() { - mTimer->cancel(); + mTimer.cancel(); mCallback(std::move(mData)); mData = {}; mSuccess = true; - debug(*mLog) << "Measuring " << mEndpoint << " done."; + debug(mLog) << "Measuring " << mEndpoint << " done."; } void fail() { mCallback(std::vector{}); mData = {}; - debug(*mLog) << "Measuring " << mEndpoint << " failed."; + debug(mLog) << "Measuring " << mEndpoint << " failed."; } - std::shared_ptr mpSocket; + Socket mSocket; SessionId mSessionId; asio::ip::udp::endpoint mEndpoint; std::vector> mData; Callback mCallback; Clock mClock; - util::Injected mTimer; + Timer mTimer; std::size_t mMeasurementsStarted; - util::Injected mLog; + Log mLog; bool mSuccess; }; @@ -288,7 +261,7 @@ struct Measurement std::shared_ptr mpImpl; }; - std::unique_ptr mpIo; + IoContext mIo; std::shared_ptr mpImpl; }; diff --git a/source/modules/hylia/link/ableton/link/MeasurementEndpointV4.hpp b/source/modules/hylia/link/ableton/link/MeasurementEndpointV4.hpp index c0d9d5f01..2fdd3ecd9 100644 --- a/source/modules/hylia/link/ableton/link/MeasurementEndpointV4.hpp +++ b/source/modules/hylia/link/ableton/link/MeasurementEndpointV4.hpp @@ -29,10 +29,8 @@ namespace link struct MeasurementEndpointV4 { - enum - { - key = 'mep4' - }; + static const std::int32_t key = 'mep4'; + static_assert(key == 0x6d657034, "Unexpected byte order"); // Model the NetworkByteStreamSerializable concept friend std::uint32_t sizeInByteStream(const MeasurementEndpointV4 mep) diff --git a/source/modules/hylia/link/ableton/link/MeasurementService.hpp b/source/modules/hylia/link/ableton/link/MeasurementService.hpp index 58c7075c5..f9a98ec40 100644 --- a/source/modules/hylia/link/ableton/link/MeasurementService.hpp +++ b/source/modules/hylia/link/ableton/link/MeasurementService.hpp @@ -19,7 +19,6 @@ #pragma once -#include #include #include #include @@ -28,48 +27,34 @@ #include #include #include -#include -#include #include #include -#include namespace ableton { namespace link { -template +template class MeasurementService { public: + using IoType = util::Injected; using Point = std::pair; - - using MeasurementInstance = Measurement, - Log>; - - using MeasurementServicePingResponder = PingResponder, - Log>; - - static const std::size_t kNumberThreads = 1; + using MeasurementInstance = Measurement; MeasurementService(asio::ip::address_v4 address, SessionId sessionId, GhostXForm ghostXForm, Clock clock, - util::Injected log) + IoType io) : mClock(std::move(clock)) - , mLog(std::move(log)) + , mIo(std::move(io)) , mPingResponder(std::move(address), std::move(sessionId), std::move(ghostXForm), - util::injectRef(mIo), mClock, - mLog) + util::injectRef(*mIo)) { } @@ -78,10 +63,10 @@ public: ~MeasurementService() { - // Clear the measurement map in the io service so that whatever + // Clear the measurement map in the IoContext so that whatever // cleanup code executes in response to the destruction of the - // measurement objects still have access to the io service - mIo.post([this] { mMeasurementMap.clear(); }); + // measurement objects still have access to the IoContext. + mIo->async([this] { mMeasurementMap.clear(); }); } void updateNodeState(const SessionId& sessionId, const GhostXForm& xform) @@ -100,19 +85,22 @@ public: { using namespace std; - mIo.post([this, state, handler] { + mIo->async([this, state, handler] { const auto nodeId = state.nodeState.nodeId; auto addr = mPingResponder.endpoint().address().to_v4(); auto callback = CompletionCallback{*this, nodeId, handler}; try { + mMeasurementMap[nodeId] = - MeasurementInstance{state, move(callback), move(addr), mClock, mLog}; + std::unique_ptr(new MeasurementInstance{ + state, move(callback), move(addr), mClock, mIo->clone()}); } catch (const runtime_error& err) { - info(*mLog) << "Failed to measure. Reason: " << err.what(); + info(mIo->log()) << "gateway@" + addr.to_string() + << " Failed to measure. Reason: " << err.what(); handler(GhostXForm{}); } }); @@ -142,14 +130,14 @@ private: using namespace std; using std::chrono::microseconds; - // Post this to the measurement service's io service so that we + // 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 = mService.mMeasurementMap; - mService.mIo.post([nodeId, handler, &measurementMap, data] { + auto& measurementMap = mMeasurementService.mMeasurementMap; + mMeasurementService.mIo->async([nodeId, handler, &measurementMap, data] { const auto it = measurementMap.find(nodeId); if (it != measurementMap.end()) { @@ -166,20 +154,19 @@ private: }); } - MeasurementService& mService; + MeasurementService& mMeasurementService; NodeId mNodeId; Handler mHandler; }; - // Make sure the measurement map outlives the io service so that the rest of + // 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; + using MeasurementMap = std::map>; MeasurementMap mMeasurementMap; Clock mClock; - util::Injected mLog; - platforms::asio::AsioService mIo; - MeasurementServicePingResponder mPingResponder; + IoType mIo; + PingResponder mPingResponder; }; } // namespace link diff --git a/source/modules/hylia/link/ableton/link/NodeState.hpp b/source/modules/hylia/link/ableton/link/NodeState.hpp index d96ab4b52..c7b9961d2 100644 --- a/source/modules/hylia/link/ableton/link/NodeState.hpp +++ b/source/modules/hylia/link/ableton/link/NodeState.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include namespace ableton @@ -31,7 +32,8 @@ namespace link struct NodeState { - using Payload = decltype(discovery::makePayload(Timeline{}, SessionMembership{})); + using Payload = + decltype(discovery::makePayload(Timeline{}, SessionMembership{}, StartStopState{})); NodeId ident() const { @@ -40,29 +42,34 @@ struct NodeState friend bool operator==(const NodeState& lhs, const NodeState& rhs) { - return std::tie(lhs.nodeId, lhs.sessionId, lhs.timeline) - == std::tie(rhs.nodeId, rhs.sessionId, rhs.timeline); + return std::tie(lhs.nodeId, lhs.sessionId, lhs.timeline, lhs.startStopState) + == std::tie(rhs.nodeId, rhs.sessionId, rhs.timeline, rhs.startStopState); } friend Payload toPayload(const NodeState& state) { - return discovery::makePayload(state.timeline, SessionMembership{state.sessionId}); + return discovery::makePayload( + state.timeline, SessionMembership{state.sessionId}, state.startStopState); } template - static NodeState fromPayload(NodeId id, It begin, It end) + static NodeState fromPayload(NodeId nodeId, It begin, It end) { using namespace std; - auto state = NodeState{move(id), {}, {}}; - discovery::parsePayload(move(begin), move(end), - [&state](Timeline tl) { state.timeline = move(tl); }, - [&state](SessionMembership sm) { state.sessionId = move(sm.sessionId); }); - return state; + auto nodeState = NodeState{move(nodeId), {}, {}, {}}; + discovery::parsePayload(move(begin), + move(end), [&nodeState](Timeline tl) { nodeState.timeline = move(tl); }, + [&nodeState](SessionMembership membership) { + nodeState.sessionId = move(membership.sessionId); + }, + [&nodeState](StartStopState ststst) { nodeState.startStopState = move(ststst); }); + return nodeState; } NodeId nodeId; SessionId sessionId; Timeline timeline; + StartStopState startStopState; }; } // namespace link diff --git a/source/modules/hylia/link/ableton/link/Optional.hpp b/source/modules/hylia/link/ableton/link/Optional.hpp new file mode 100644 index 000000000..01b35ed10 --- /dev/null +++ b/source/modules/hylia/link/ableton/link/Optional.hpp @@ -0,0 +1,97 @@ +/* Copyright 2017, 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 + +namespace ableton +{ +namespace link +{ + +// Subset of the C++ 17 std::optional API. T has to be default constructible. +template +struct Optional +{ + Optional() + : mHasValue(false) + { + } + + explicit Optional(T value) + : mValue(std::move(value)) + , mHasValue(true) + { + } + + Optional(const Optional&) = default; + + Optional(Optional&& other) + : mValue(std::move(other.mValue)) + , mHasValue(other.mHasValue) + { + } + + Optional& operator=(const Optional&) = default; + + Optional& operator=(Optional&& other) + { + mValue = std::move(other.mValue); + mHasValue = other.mHasValue; + return *this; + } + + explicit operator bool() const + { + return mHasValue; + } + + const T& operator*() const + { + assert(mHasValue); + return mValue; + } + + T& operator*() + { + assert(mHasValue); + return mValue; + } + + const T* operator->() const + { + assert(mHasValue); + return &mValue; + } + + T* operator->() + { + assert(mHasValue); + return &mValue; + } + +private: + T mValue; + bool mHasValue; +}; + +} // namespace link +} // namespace ableton diff --git a/source/modules/hylia/link/ableton/link/PayloadEntries.hpp b/source/modules/hylia/link/ableton/link/PayloadEntries.hpp index 22cd4fbce..bc5a45834 100644 --- a/source/modules/hylia/link/ableton/link/PayloadEntries.hpp +++ b/source/modules/hylia/link/ableton/link/PayloadEntries.hpp @@ -30,10 +30,8 @@ namespace link struct HostTime { - enum - { - key = '__ht' - }; + static const std::int32_t key = '__ht'; + static_assert(key == 0x5f5f6874, "Unexpected byte order"); HostTime() = default; @@ -66,12 +64,10 @@ struct HostTime std::chrono::microseconds time; }; -struct GHostTime : HostTime +struct GHostTime { - enum - { - key = '__gt' - }; + static const std::int32_t key = '__gt'; + static_assert(key == 0x5f5f6774, "Unexpected byte order"); GHostTime() = default; @@ -106,10 +102,8 @@ struct GHostTime : HostTime struct PrevGHostTime { - enum - { - key = '_pgt' - }; + static const std::int32_t key = '_pgt'; + static_assert(key == 0x5f706774, "Unexpected byte order"); PrevGHostTime() = default; diff --git a/source/modules/hylia/link/ableton/link/PeerState.hpp b/source/modules/hylia/link/ableton/link/PeerState.hpp index 6a6ac60e0..8b58df4d4 100644 --- a/source/modules/hylia/link/ableton/link/PeerState.hpp +++ b/source/modules/hylia/link/ableton/link/PeerState.hpp @@ -51,6 +51,11 @@ struct PeerState return nodeState.timeline; } + StartStopState startStopState() const + { + return nodeState.startStopState; + } + friend bool operator==(const PeerState& lhs, const PeerState& rhs) { return lhs.nodeState == rhs.nodeState && lhs.endpoint == rhs.endpoint; diff --git a/source/modules/hylia/link/ableton/link/Peers.hpp b/source/modules/hylia/link/ableton/link/Peers.hpp index c6cf8c0ba..24172b33d 100644 --- a/source/modules/hylia/link/ableton/link/Peers.hpp +++ b/source/modules/hylia/link/ableton/link/Peers.hpp @@ -33,10 +33,14 @@ namespace link // // SessionTimelineCallback is invoked with a session id and a timeline // whenever a new combination of these values is seen +// +// SessionStartStopStateCallback is invoked with a session id and a startStopState +// whenever a new combination of these values is seen template + typename SessionTimelineCallback, + typename SessionStartStopStateCallback> class Peers { // non-movable private implementation type @@ -47,9 +51,10 @@ public: Peers(util::Injected io, SessionMembershipCallback membership, - SessionTimelineCallback timeline) - : mpImpl( - std::make_shared(std::move(io), std::move(membership), std::move(timeline))) + SessionTimelineCallback timeline, + SessionStartStopStateCallback startStop) + : mpImpl(std::make_shared( + std::move(io), std::move(membership), std::move(timeline), std::move(startStop))) { } @@ -196,10 +201,12 @@ private: { Impl(util::Injected io, SessionMembershipCallback membership, - SessionTimelineCallback timeline) + SessionTimelineCallback timeline, + SessionStartStopStateCallback startStop) : mIo(std::move(io)) , mSessionMembershipCallback(std::move(membership)) , mSessionTimelineCallback(std::move(timeline)) + , mSessionStartStopStateCallback(std::move(startStop)) { } @@ -209,10 +216,14 @@ private: const auto peerSession = peerState.sessionId(); const auto peerTimeline = peerState.timeline(); + const auto peerStartStopState = peerState.startStopState(); bool isNewSessionTimeline = false; + bool isNewSessionStartStopState = false; bool didSessionMembershipChange = false; { isNewSessionTimeline = !sessionTimelineExists(peerSession, peerTimeline); + isNewSessionStartStopState = + !sessionStartStopStateExists(peerSession, peerStartStopState); auto peer = make_pair(move(peerState), move(gatewayAddr)); const auto idRange = equal_range(begin(mPeers), end(mPeers), peer, PeerIdComp{}); @@ -254,6 +265,15 @@ private: mSessionTimelineCallback(peerSession, peerTimeline); } + // Pass the start stop state to the Controller after it processed the timeline. + // A new timeline can cause a session Id change which will prevent processing the + // new start stop state. By handling the start stop state after the timeline we + // assure that the start stop state is processed with the correct session Id. + if (isNewSessionStartStopState) + { + mSessionStartStopStateCallback(peerSession, peerStartStopState); + } + if (didSessionMembershipChange) { mSessionMembershipCallback(); @@ -297,14 +317,29 @@ private: mSessionMembershipCallback(); } - bool sessionTimelineExists(const SessionId& session, const Timeline& tl) + template + bool hasPeerWith(const SessionId& sessionId, Predicate predicate) { using namespace std; return find_if(begin(mPeers), end(mPeers), [&](const Peer& peer) { - return peer.first.sessionId() == session && peer.first.timeline() == tl; + return peer.first.sessionId() == sessionId && predicate(peer.first); }) != end(mPeers); } + bool sessionTimelineExists(const SessionId& session, const Timeline& timeline) + { + return hasPeerWith(session, + [&](const PeerState& peerState) { return peerState.timeline() == timeline; }); + } + + bool sessionStartStopStateExists( + const SessionId& sessionId, const StartStopState& startStopState) + { + return hasPeerWith(sessionId, [&](const PeerState& peerState) { + return peerState.startStopState() == startStopState; + }); + } + struct PeerIdComp { bool operator()(const Peer& lhs, const Peer& rhs) const @@ -324,6 +359,7 @@ private: util::Injected mIo; SessionMembershipCallback mSessionMembershipCallback; SessionTimelineCallback mSessionTimelineCallback; + SessionStartStopStateCallback mSessionStartStopStateCallback; std::vector mPeers; // sorted by peerId, unique by (peerId, addr) }; @@ -342,13 +378,19 @@ private: template -Peers makePeers( - util::Injected io, + typename SessionTimelineCallback, + typename SessionStartStopStateCallback> +Peers +makePeers(util::Injected io, SessionMembershipCallback membershipCallback, - SessionTimelineCallback timelineCallback) + SessionTimelineCallback timelineCallback, + SessionStartStopStateCallback startStopStateCallback) { - return {std::move(io), std::move(membershipCallback), std::move(timelineCallback)}; + return {std::move(io), std::move(membershipCallback), std::move(timelineCallback), + std::move(startStopStateCallback)}; } } // namespace link diff --git a/source/modules/hylia/link/ableton/link/Phase.hpp b/source/modules/hylia/link/ableton/link/Phase.hpp index 505af5771..7268a62e8 100644 --- a/source/modules/hylia/link/ableton/link/Phase.hpp +++ b/source/modules/hylia/link/ableton/link/Phase.hpp @@ -96,5 +96,5 @@ inline std::chrono::microseconds fromPhaseEncodedBeats( return tl.fromBeats(tl.beatOrigin + originOffset + quantum - inversePhaseOffset); } -} // link -} // ableton +} // namespace link +} // namespace ableton diff --git a/source/modules/hylia/link/ableton/link/PingResponder.hpp b/source/modules/hylia/link/ableton/link/PingResponder.hpp index 0650cce18..e8a5e8b1c 100644 --- a/source/modules/hylia/link/ableton/link/PingResponder.hpp +++ b/source/modules/hylia/link/ableton/link/PingResponder.hpp @@ -23,35 +23,34 @@ #include #include #include -#include #include #include #include #include -#include namespace ableton { namespace link { -template +template class PingResponder { + using IoType = util::Injected; + using Socket = typename IoType::type::template Socket; + public: PingResponder(asio::ip::address_v4 address, SessionId sessionId, GhostXForm ghostXForm, - util::Injected io, Clock clock, - util::Injected log) - : mIo(std::move(io)) - , mpImpl(std::make_shared(*mIo, - std::move(address), + IoType io) + : mIo(io) + , mpImpl(std::make_shared(std::move(address), std::move(sessionId), std::move(ghostXForm), std::move(clock), - std::move(log))) + std::move(io))) { mpImpl->listen(); } @@ -61,16 +60,16 @@ public: ~PingResponder() { - // post the release of the impl object into the io service so that + // post the release of the impl object into the IoContext so that // it happens in the same thread as its handlers auto pImpl = mpImpl; - mIo->post([pImpl]() mutable { pImpl.reset(); }); + mIo->async([pImpl]() mutable { pImpl.reset(); }); } void updateNodeState(const SessionId& sessionId, const GhostXForm& xform) { auto pImpl = mpImpl; - mIo->post([pImpl, sessionId, xform] { + mIo->async([pImpl, sessionId, xform] { pImpl->mSessionId = std::move(sessionId); pImpl->mGhostXForm = std::move(xform); }); @@ -94,19 +93,17 @@ public: private: struct Impl : std::enable_shared_from_this { - Impl(typename util::Injected::type& io, - asio::ip::address_v4 address, + Impl(asio::ip::address_v4 address, SessionId sessionId, GhostXForm ghostXForm, Clock clock, - util::Injected log) + IoType io) : mSessionId(std::move(sessionId)) , mGhostXForm(std::move(ghostXForm)) , mClock(std::move(clock)) - , mLog(std::move(log)) - , mSocket(io) + , mLog(channel(io->log(), "gateway@" + address.to_string())) + , mSocket(io->template openUnicastSocket(address)) { - configureUnicastSocket(mSocket, address); } void listen() @@ -131,7 +128,7 @@ private: sizeInByteStream(makePayload(HostTime{}, PrevGHostTime{})); if (header.messageType == v1::kPing && payloadSize <= maxPayloadSize) { - debug(*mLog) << "Received ping message from " << from; + debug(mLog) << " Received ping message from " << from; try { @@ -139,12 +136,12 @@ private: } catch (const std::runtime_error& err) { - info(*mLog) << "Failed to send pong to " << from << ". Reason: " << err.what(); + info(mLog) << " Failed to send pong to " << from << ". Reason: " << err.what(); } } else { - info(*mLog) << "Received invalid Message from " << from << "."; + info(mLog) << " Received invalid Message from " << from << "."; } listen(); } @@ -173,11 +170,11 @@ private: SessionId mSessionId; GhostXForm mGhostXForm; Clock mClock; - util::Injected mLog; + typename IoType::type::Log mLog; Socket mSocket; }; - util::Injected mIo; + IoType mIo; std::shared_ptr mpImpl; }; diff --git a/source/modules/hylia/link/ableton/link/SessionId.hpp b/source/modules/hylia/link/ableton/link/SessionId.hpp index 8402ebf03..ac8967246 100644 --- a/source/modules/hylia/link/ableton/link/SessionId.hpp +++ b/source/modules/hylia/link/ableton/link/SessionId.hpp @@ -33,10 +33,8 @@ using SessionId = NodeId; // A payload entry indicating membership in a particular session struct SessionMembership { - enum - { - key = 'sess' - }; + static const std::int32_t key = 'sess'; + static_assert(key == 0x73657373, "Unexpected byte order"); // Model the NetworkByteStreamSerializable concept friend std::uint32_t sizeInByteStream(const SessionMembership& sm) diff --git a/source/modules/hylia/link/ableton/link/SessionState.hpp b/source/modules/hylia/link/ableton/link/SessionState.hpp new file mode 100644 index 000000000..f64325f41 --- /dev/null +++ b/source/modules/hylia/link/ableton/link/SessionState.hpp @@ -0,0 +1,80 @@ +/* Copyright 2017, 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 link +{ + +using OptionalTimeline = Optional; +using OptionalStartStopState = Optional; + +struct SessionState +{ + Timeline timeline; + StartStopState startStopState; + GhostXForm ghostXForm; +}; + +struct ClientState +{ + friend bool operator==(const ClientState& lhs, const ClientState& rhs) + { + return std::tie(lhs.timeline, lhs.startStopState) + == std::tie(rhs.timeline, rhs.startStopState); + } + + friend bool operator!=(const ClientState& lhs, const ClientState& rhs) + { + return !(lhs == rhs); + } + + Timeline timeline; + StartStopState startStopState; +}; + +struct RtClientState +{ + Timeline timeline; + StartStopState startStopState; + std::chrono::microseconds timelineTimestamp; + std::chrono::microseconds startStopStateTimestamp; +}; + +struct IncomingClientState +{ + OptionalTimeline timeline; + OptionalStartStopState startStopState; + std::chrono::microseconds timelineTimestamp; +}; + +struct ApiState +{ + Timeline timeline; + ApiStartStopState startStopState; +}; + +} // namespace link +} // namespace ableton diff --git a/source/modules/hylia/link/ableton/link/Sessions.hpp b/source/modules/hylia/link/ableton/link/Sessions.hpp index 6bd5b9f19..fc82bdd65 100644 --- a/source/modules/hylia/link/ableton/link/Sessions.hpp +++ b/source/modules/hylia/link/ableton/link/Sessions.hpp @@ -159,8 +159,9 @@ private: range.first->measurement = move(measurement); // If session times too close - fall back to session id order const auto ghostDiff = newGhost - curGhost; - if (ghostDiff > SESSION_EPS || (std::abs(ghostDiff.count()) < SESSION_EPS.count() - && id < mCurrent.sessionId)) + if (ghostDiff > SESSION_EPS + || (std::abs(ghostDiff.count()) < SESSION_EPS.count() + && id < mCurrent.sessionId)) { // The new session wins, switch over to it auto current = mCurrent; diff --git a/source/modules/hylia/link/ableton/link/StartStopState.hpp b/source/modules/hylia/link/ableton/link/StartStopState.hpp new file mode 100644 index 000000000..74654ef1b --- /dev/null +++ b/source/modules/hylia/link/ableton/link/StartStopState.hpp @@ -0,0 +1,123 @@ +/* Copyright 2017, 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 + +namespace ableton +{ +namespace link +{ + +// A tuple of (isPlaying, time) that represents the playing state +// with an according timestamp in microseconds. It also serves as a +// payload entry. + +struct StartStopState +{ + static const std::int32_t key = 'stst'; + static_assert(key == 0x73747374, "Unexpected byte order"); + + using StartStopStateTuple = std::tuple; + + StartStopState() = default; + + StartStopState( + const bool aIsPlaying, const Beats aBeats, const std::chrono::microseconds aTimestamp) + : isPlaying(aIsPlaying) + , beats(aBeats) + , timestamp(aTimestamp) + { + } + + friend bool operator==(const StartStopState& lhs, const StartStopState& rhs) + { + return std::tie(lhs.isPlaying, lhs.beats, lhs.timestamp) + == std::tie(rhs.isPlaying, rhs.beats, rhs.timestamp); + } + + friend bool operator!=(const StartStopState& lhs, const StartStopState& rhs) + { + return !(lhs == rhs); + } + + // Model the NetworkByteStreamSerializable concept + friend std::uint32_t sizeInByteStream(const StartStopState& state) + { + return discovery::sizeInByteStream(state.asTuple()); + } + + template + friend It toNetworkByteStream(const StartStopState& state, It out) + { + return discovery::toNetworkByteStream(state.asTuple(), std::move(out)); + } + + template + static std::pair fromNetworkByteStream(It begin, It end) + { + using namespace std; + using namespace discovery; + auto result = + Deserialize::fromNetworkByteStream(move(begin), move(end)); + auto state = + StartStopState{get<0>(result.first), get<1>(result.first), get<2>(result.first)}; + return make_pair(move(state), move(result.second)); + } + + bool isPlaying{false}; + Beats beats{0.}; + std::chrono::microseconds timestamp{0}; + +private: + StartStopStateTuple asTuple() const + { + return std::make_tuple(isPlaying, beats, timestamp); + } +}; + +struct ApiStartStopState +{ + ApiStartStopState() = default; + + ApiStartStopState(const bool aIsPlaying, const std::chrono::microseconds aTime) + : isPlaying(aIsPlaying) + , time(aTime) + { + } + + friend bool operator==(const ApiStartStopState& lhs, const ApiStartStopState& rhs) + { + return std::tie(lhs.isPlaying, lhs.time) == std::tie(rhs.isPlaying, rhs.time); + } + + friend bool operator!=(const ApiStartStopState& lhs, const ApiStartStopState& rhs) + { + return !(lhs == rhs); + } + + bool isPlaying{false}; + std::chrono::microseconds time{0}; +}; +} // namespace link +} // namespace ableton diff --git a/source/modules/hylia/link/ableton/link/Timeline.hpp b/source/modules/hylia/link/ableton/link/Timeline.hpp index c88a62ce8..f0f084e9d 100644 --- a/source/modules/hylia/link/ableton/link/Timeline.hpp +++ b/source/modules/hylia/link/ableton/link/Timeline.hpp @@ -38,10 +38,8 @@ namespace link struct Timeline { - enum - { - key = 'tmln' - }; + static const std::int32_t key = 'tmln'; + static_assert(key == 0x746d6c6e, "Unexpected byte order"); Beats toBeats(const std::chrono::microseconds time) const { diff --git a/source/modules/hylia/link/ableton/platforms/Config.hpp b/source/modules/hylia/link/ableton/platforms/Config.hpp index a2df41a1f..cd1506cf3 100644 --- a/source/modules/hylia/link/ableton/platforms/Config.hpp +++ b/source/modules/hylia/link/ableton/platforms/Config.hpp @@ -22,15 +22,15 @@ #include #include -#if LINK_PLATFORM_WINDOWS +#if defined(LINK_PLATFORM_WINDOWS) #include #include #include -#elif LINK_PLATFORM_MACOSX +#elif defined(LINK_PLATFORM_MACOSX) #include #include #include -#elif LINK_PLATFORM_LINUX +#elif defined(LINK_PLATFORM_LINUX) #include #include #include @@ -43,28 +43,25 @@ namespace link namespace platform { -#if LINK_PLATFORM_WINDOWS +#if defined(LINK_PLATFORM_WINDOWS) using Clock = platforms::windows::Clock; using IoContext = platforms::asio::Context; -#elif LINK_PLATFORM_MACOSX +#elif defined(LINK_PLATFORM_MACOSX) using Clock = platforms::darwin::Clock; using IoContext = platforms::asio::Context; -#elif LINK_PLATFORM_LINUX - #ifdef __ARM_ARCH_7A__ -using Clock = platforms::linux::ClockMonotonicRaw; - #else +#elif defined(LINK_PLATFORM_LINUX) using Clock = platforms::linux::ClockMonotonic; - #endif using IoContext = platforms::asio::Context; #endif -using Controller = Controller; +using Controller = + Controller; -} // platform -} // link -} // ableton +} // namespace platform +} // namespace link +} // namespace ableton diff --git a/source/modules/hylia/link/ableton/platforms/asio/AsioService.hpp b/source/modules/hylia/link/ableton/platforms/asio/AsioService.hpp deleted file mode 100644 index c33b892b6..000000000 --- a/source/modules/hylia/link/ableton/platforms/asio/AsioService.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* 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 asio -{ - -class AsioService -{ -public: - using Timer = AsioTimer; - - AsioService() - : AsioService(DefaultHandler{}) - { - } - - template - explicit AsioService(ExceptionHandler exceptHandler) - : mpWork(new ::asio::io_service::work(mService)) - { - mThread = - std::thread{[](::asio::io_service& service, ExceptionHandler handler) { - for (;;) - { - try - { - service.run(); - break; - } - catch (const typename ExceptionHandler::Exception& exception) - { - handler(exception); - } - } - }, - std::ref(mService), std::move(exceptHandler)}; - } - - ~AsioService() - { - mpWork.reset(); - mThread.join(); - } - - AsioTimer makeTimer() - { - return {mService}; - } - - template - void post(Handler handler) - { - mService.post(std::move(handler)); - } - - ::asio::io_service mService; - -private: - // Default handler is hidden and defines a hidden exception type - // that will never be thrown by other code, so it effectively does - // not catch. - struct DefaultHandler - { - struct Exception - { - }; - - void operator()(const Exception&) - { - } - }; - - std::unique_ptr<::asio::io_service::work> mpWork; - std::thread mThread; -}; - -} // namespace asio -} // namespace platforms -} // namespace ableton diff --git a/source/modules/hylia/link/ableton/platforms/asio/AsioWrapper.hpp b/source/modules/hylia/link/ableton/platforms/asio/AsioWrapper.hpp index 37351c5c0..51711df43 100644 --- a/source/modules/hylia/link/ableton/platforms/asio/AsioWrapper.hpp +++ b/source/modules/hylia/link/ableton/platforms/asio/AsioWrapper.hpp @@ -32,35 +32,41 @@ #pragma push_macro("ASIO_NO_TYPEID") #define ASIO_NO_TYPEID 1 -#if LINK_PLATFORM_WINDOWS +#if defined(LINK_PLATFORM_WINDOWS) #pragma push_macro("INCL_EXTRA_HTON_FUNCTIONS") #define INCL_EXTRA_HTON_FUNCTIONS 1 #endif +#if defined(WIN32) || defined(_WIN32) +#if !defined(_WIN32_WINNT) +#define _WIN32_WINNT 0x0501 +#endif +#endif + #if defined(__clang__) #pragma clang diagnostic push #if __has_warning("-Wcomma") #pragma clang diagnostic ignored "-Wcomma" #endif +#if __has_warning("-Wshorten-64-to-32") +#pragma clang diagnostic ignored "-Wshorten-64-to-32" +#endif +#if __has_warning("-Wunused-local-typedef") +#pragma clang diagnostic ignored "-Wunused-local-typedef" +#endif #endif #if defined(_MSC_VER) -#pragma warning(push) -// C4191: 'operator/operation': unsafe conversion from 'type of expression' to -// 'type required' -#pragma warning(disable : 4191) -// C4548: expression before comma has no effect; expected expression with side-effect -#pragma warning(disable : 4548) -// C4619: #pragma warning : there is no warning number 'number' -#pragma warning(disable : 4619) -// C4675: 'function' : resolved overload was found by argument-dependent lookup -#pragma warning(disable : 4675) +#define _SCL_SECURE_NO_WARNINGS 1 +#pragma warning(push, 0) +#pragma warning(disable : 4242) +#pragma warning(disable : 4702) #endif #include #include -#if LINK_PLATFORM_WINDOWS +#if defined(LINK_PLATFORM_WINDOWS) #pragma pop_macro("INCL_EXTRA_HTON_FUNCTIONS") #endif @@ -69,6 +75,7 @@ #if defined(_MSC_VER) #pragma warning(pop) +#undef _SCL_SECURE_NO_WARNINGS #endif #if defined(__clang__) diff --git a/source/modules/hylia/link/ableton/platforms/asio/LockFreeCallbackDispatcher.hpp b/source/modules/hylia/link/ableton/platforms/asio/LockFreeCallbackDispatcher.hpp index a97ec877e..a1e85df79 100644 --- a/source/modules/hylia/link/ableton/platforms/asio/LockFreeCallbackDispatcher.hpp +++ b/source/modules/hylia/link/ableton/platforms/asio/LockFreeCallbackDispatcher.hpp @@ -85,5 +85,5 @@ private: std::thread mThread; }; -} // platforms -} // ableton +} // namespace platforms +} // namespace ableton diff --git a/source/modules/hylia/link/ableton/platforms/asio/Util.hpp b/source/modules/hylia/link/ableton/platforms/asio/Util.hpp index 8fd3ccb0e..b974c10cc 100644 --- a/source/modules/hylia/link/ableton/platforms/asio/Util.hpp +++ b/source/modules/hylia/link/ableton/platforms/asio/Util.hpp @@ -38,6 +38,6 @@ AsioAddrType makeAddress(const char* pAddr) return AsioAddrType{bytes}; } -} // asio -} // platforms -} // ableton +} // namespace asio +} // namespace platforms +} // namespace ableton diff --git a/source/modules/hylia/link/ableton/platforms/linux/Clock.hpp b/source/modules/hylia/link/ableton/platforms/linux/Clock.hpp index e289be7ca..0cc4d9acc 100644 --- a/source/modules/hylia/link/ableton/platforms/linux/Clock.hpp +++ b/source/modules/hylia/link/ableton/platforms/linux/Clock.hpp @@ -49,17 +49,7 @@ public: }; using ClockMonotonic = Clock; -#ifdef CLOCK_MONOTONIC_RAW using ClockMonotonicRaw = Clock; -#endif - -/* Warning: the realtime clock can be subject to time change. - * One of the hard requirements of Ableton Link is that the clock must be - * monotonic. - * Only use the Realtime Clock if you can't use the monotonic ones, and - * beware that things might go wrong if time jumps. - */ -using ClockRealtime = Clock; } // namespace linux } // namespace platforms diff --git a/source/modules/hylia/link/ableton/platforms/posix/ScanIpIfAddrs.hpp b/source/modules/hylia/link/ableton/platforms/posix/ScanIpIfAddrs.hpp index e0d89ca0e..8d24bc2f4 100644 --- a/source/modules/hylia/link/ableton/platforms/posix/ScanIpIfAddrs.hpp +++ b/source/modules/hylia/link/ableton/platforms/posix/ScanIpIfAddrs.hpp @@ -67,7 +67,7 @@ private: struct ifaddrs* interfaces = NULL; }; -} // detail +} // namespace detail // Posix implementation of ip interface address scanner diff --git a/source/modules/hylia/link/ableton/platforms/windows/ScanIpIfAddrs.hpp b/source/modules/hylia/link/ableton/platforms/windows/ScanIpIfAddrs.hpp index 2ef00165f..e2b6f8459 100644 --- a/source/modules/hylia/link/ableton/platforms/windows/ScanIpIfAddrs.hpp +++ b/source/modules/hylia/link/ableton/platforms/windows/ScanIpIfAddrs.hpp @@ -93,7 +93,7 @@ private: IP_ADAPTER_ADDRESSES* adapter; }; -} // detail +} // namespace detail struct ScanIpIfAddrs { diff --git a/source/modules/hylia/link/ableton/test/CatchWrapper.hpp b/source/modules/hylia/link/ableton/test/CatchWrapper.hpp index cf2eb76d2..77edf508d 100644 --- a/source/modules/hylia/link/ableton/test/CatchWrapper.hpp +++ b/source/modules/hylia/link/ableton/test/CatchWrapper.hpp @@ -26,18 +26,15 @@ * which are specific to that library. */ -// Visual Studio #if defined(_MSC_VER) -#pragma warning(push) -// C4388: signed/unsigned mismatch -#pragma warning(disable : 4388) -// C4702: unreachable code +#pragma warning(push, 0) +#pragma warning(disable : 4242) +#pragma warning(disable : 4244) #pragma warning(disable : 4702) #endif #include -// Visual Studio #if defined(_MSC_VER) #pragma warning(pop) #endif diff --git a/source/modules/hylia/link/ableton/test/serial_io/Socket.hpp b/source/modules/hylia/link/ableton/test/serial_io/Socket.hpp deleted file mode 100644 index 9941f998a..000000000 --- a/source/modules/hylia/link/ableton/test/serial_io/Socket.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* 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 - -namespace ableton -{ -namespace test -{ -namespace serial_io -{ - -struct Socket -{ - using SendFn = std::function; - - struct ReceiveHandler - { - template - void operator()(const asio::ip::udp::endpoint& from, const It begin, const It end) - { - std::vector buffer{begin, end}; - mReceive(from, buffer); - } - - std::function&> mReceive; - }; - - using ReceiveFn = std::function; - - Socket(SendFn sendFn, ReceiveFn receiveFn) - : mSendFn(std::move(sendFn)) - , mReceiveFn(std::move(receiveFn)) - { - } - - std::size_t send( - const uint8_t* const pData, const size_t numBytes, const asio::ip::udp::endpoint& to) - { - return mSendFn(pData, numBytes, to); - } - - template - void receive(Handler handler) - { - mReceiveFn(ReceiveHandler{ - [handler](const asio::ip::udp::endpoint& from, const std::vector& buffer) { - handler(from, begin(buffer), end(buffer)); - }}); - } - - asio::ip::udp::endpoint endpoint() const - { - return asio::ip::udp::endpoint({}, 0); - } - - SendFn mSendFn; - ReceiveFn mReceiveFn; -}; - -} // namespace test -} // namespace platforms -} // namespace ableton diff --git a/source/modules/hylia/link/ableton/util/SampleTiming.hpp b/source/modules/hylia/link/ableton/util/SampleTiming.hpp index 04347bbe4..fbd5c1ee0 100644 --- a/source/modules/hylia/link/ableton/util/SampleTiming.hpp +++ b/source/modules/hylia/link/ableton/util/SampleTiming.hpp @@ -48,5 +48,5 @@ struct SampleTiming double mSampleRate; }; -} // util -} // ableton +} // namespace util +} // namespace ableton diff --git a/source/modules/hylia/link/asio.hpp b/source/modules/hylia/link/asio.hpp index 8dcf27342..4b47b92c5 100644 --- a/source/modules/hylia/link/asio.hpp +++ b/source/modules/hylia/link/asio.hpp @@ -2,7 +2,7 @@ // asio.hpp // ~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,6 +18,7 @@ #include "asio/associated_allocator.hpp" #include "asio/associated_executor.hpp" #include "asio/async_result.hpp" +#include "asio/awaitable.hpp" #include "asio/basic_datagram_socket.hpp" #include "asio/basic_deadline_timer.hpp" #include "asio/basic_io_object.hpp" @@ -25,6 +26,7 @@ #include "asio/basic_seq_packet_socket.hpp" #include "asio/basic_serial_port.hpp" #include "asio/basic_signal_set.hpp" +#include "asio/basic_socket.hpp" #include "asio/basic_socket_acceptor.hpp" #include "asio/basic_socket_iostream.hpp" #include "asio/basic_socket_streambuf.hpp" @@ -40,13 +42,14 @@ #include "asio/buffered_write_stream_fwd.hpp" #include "asio/buffered_write_stream.hpp" #include "asio/buffers_iterator.hpp" +#include "asio/co_spawn.hpp" #include "asio/completion_condition.hpp" +#include "asio/compose.hpp" #include "asio/connect.hpp" #include "asio/coroutine.hpp" -#include "asio/datagram_socket_service.hpp" -#include "asio/deadline_timer_service.hpp" #include "asio/deadline_timer.hpp" #include "asio/defer.hpp" +#include "asio/detached.hpp" #include "asio/dispatch.hpp" #include "asio/error.hpp" #include "asio/error_code.hpp" @@ -61,7 +64,7 @@ #include "asio/handler_alloc_hook.hpp" #include "asio/handler_continuation_hook.hpp" #include "asio/handler_invoke_hook.hpp" -#include "asio/handler_type.hpp" +#include "asio/high_resolution_timer.hpp" #include "asio/io_context.hpp" #include "asio/io_context_strand.hpp" #include "asio/io_service.hpp" @@ -73,6 +76,8 @@ #include "asio/ip/address_v6.hpp" #include "asio/ip/address_v6_iterator.hpp" #include "asio/ip/address_v6_range.hpp" +#include "asio/ip/network_v4.hpp" +#include "asio/ip/network_v6.hpp" #include "asio/ip/bad_address_cast.hpp" #include "asio/ip/basic_endpoint.hpp" #include "asio/ip/basic_resolver.hpp" @@ -84,7 +89,6 @@ #include "asio/ip/multicast.hpp" #include "asio/ip/resolver_base.hpp" #include "asio/ip/resolver_query_base.hpp" -#include "asio/ip/resolver_service.hpp" #include "asio/ip/tcp.hpp" #include "asio/ip/udp.hpp" #include "asio/ip/unicast.hpp" @@ -96,48 +100,47 @@ #include "asio/local/connect_pair.hpp" #include "asio/local/datagram_protocol.hpp" #include "asio/local/stream_protocol.hpp" +#include "asio/packaged_task.hpp" #include "asio/placeholders.hpp" #include "asio/posix/basic_descriptor.hpp" #include "asio/posix/basic_stream_descriptor.hpp" +#include "asio/posix/descriptor.hpp" #include "asio/posix/descriptor_base.hpp" #include "asio/posix/stream_descriptor.hpp" -#include "asio/posix/stream_descriptor_service.hpp" #include "asio/post.hpp" -#include "asio/raw_socket_service.hpp" #include "asio/read.hpp" #include "asio/read_at.hpp" #include "asio/read_until.hpp" -#include "asio/seq_packet_socket_service.hpp" +#include "asio/redirect_error.hpp" #include "asio/serial_port.hpp" #include "asio/serial_port_base.hpp" -#include "asio/serial_port_service.hpp" #include "asio/signal_set.hpp" -#include "asio/signal_set_service.hpp" -#include "asio/socket_acceptor_service.hpp" #include "asio/socket_base.hpp" +#include "asio/steady_timer.hpp" #include "asio/strand.hpp" -#include "asio/stream_socket_service.hpp" #include "asio/streambuf.hpp" +#include "asio/system_context.hpp" #include "asio/system_error.hpp" #include "asio/system_executor.hpp" +#include "asio/system_timer.hpp" +#include "asio/this_coro.hpp" #include "asio/thread.hpp" #include "asio/thread_pool.hpp" #include "asio/time_traits.hpp" +#include "asio/use_awaitable.hpp" +#include "asio/use_future.hpp" #include "asio/uses_executor.hpp" #include "asio/version.hpp" #include "asio/wait_traits.hpp" -#include "asio/waitable_timer_service.hpp" -#include "asio/windows/basic_handle.hpp" #include "asio/windows/basic_object_handle.hpp" +#include "asio/windows/basic_overlapped_handle.hpp" #include "asio/windows/basic_random_access_handle.hpp" #include "asio/windows/basic_stream_handle.hpp" #include "asio/windows/object_handle.hpp" -#include "asio/windows/object_handle_service.hpp" +#include "asio/windows/overlapped_handle.hpp" #include "asio/windows/overlapped_ptr.hpp" #include "asio/windows/random_access_handle.hpp" -#include "asio/windows/random_access_handle_service.hpp" #include "asio/windows/stream_handle.hpp" -#include "asio/windows/stream_handle_service.hpp" #include "asio/write.hpp" #include "asio/write_at.hpp" diff --git a/source/modules/hylia/link/asio/associated_allocator.hpp b/source/modules/hylia/link/asio/associated_allocator.hpp index 4b78ba423..02d6538e5 100644 --- a/source/modules/hylia/link/asio/associated_allocator.hpp +++ b/source/modules/hylia/link/asio/associated_allocator.hpp @@ -2,7 +2,7 @@ // associated_allocator.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -116,6 +116,14 @@ get_associated_allocator(const T& t, const Allocator& a) ASIO_NOEXCEPT return associated_allocator::get(t, a); } +#if defined(ASIO_HAS_ALIAS_TEMPLATES) + +template > +using associated_allocator_t + = typename associated_allocator::type; + +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + } // namespace asio #include "asio/detail/pop_options.hpp" diff --git a/source/modules/hylia/link/asio/associated_executor.hpp b/source/modules/hylia/link/asio/associated_executor.hpp index 9389d554c..7fe6af5b0 100644 --- a/source/modules/hylia/link/asio/associated_executor.hpp +++ b/source/modules/hylia/link/asio/associated_executor.hpp @@ -2,7 +2,7 @@ // associated_executor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -135,6 +135,13 @@ get_associated_executor(const T& t, ExecutionContext& ctx, typename ExecutionContext::executor_type>::get(t, ctx.get_executor()); } +#if defined(ASIO_HAS_ALIAS_TEMPLATES) + +template +using associated_executor_t = typename associated_executor::type; + +#endif // defined(ASIO_HAS_ALIAS_TEMPLATES) + } // namespace asio #include "asio/detail/pop_options.hpp" diff --git a/source/modules/hylia/link/asio/async_result.hpp b/source/modules/hylia/link/asio/async_result.hpp index 12ccd1ec5..6113c5990 100644 --- a/source/modules/hylia/link/asio/async_result.hpp +++ b/source/modules/hylia/link/asio/async_result.hpp @@ -2,7 +2,7 @@ // async_result.hpp // ~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,7 +17,7 @@ #include "asio/detail/config.hpp" #include "asio/detail/type_traits.hpp" -#include "asio/handler_type.hpp" +#include "asio/detail/variadic_templates.hpp" #include "asio/detail/push_options.hpp" @@ -25,58 +25,134 @@ namespace asio { /// An interface for customising the behaviour of an initiating function. /** - * This template may be specialised for user-defined handler types. + * The async_result traits class is used for determining: + * + * @li the concrete completion handler type to be called at the end of the + * asynchronous operation; + * + * @li the initiating function return type; and + * + * @li how the return value of the initiating function is obtained. + * + * The trait allows the handler and return types to be determined at the point + * where the specific completion handler signature is known. + * + * This template may be specialised for user-defined completion token types. + * The primary template assumes that the CompletionToken is the completion + * handler. */ -template +template class async_result { public: + /// The concrete completion handler type for the specific signature. + typedef CompletionToken completion_handler_type; + /// The return type of the initiating function. - typedef void type; + typedef void return_type; /// Construct an async result from a given handler. /** * When using a specalised async_result, the constructor has an opportunity - * to initialise some state associated with the handler, which is then - * returned from the initiating function. + * to initialise some state associated with the completion handler, which is + * then returned from the initiating function. */ - explicit async_result(Handler&) + explicit async_result(completion_handler_type& h) { + (void)h; } /// Obtain the value to be returned from the initiating function. - type get() + return_type get() + { + } + +#if defined(ASIO_HAS_VARIADIC_TEMPLATES) \ + || defined(GENERATING_DOCUMENTATION) + + /// Initiate the asynchronous operation that will produce the result, and + /// obtain the value to be returned from the initiating function. + template + static return_type initiate( + ASIO_MOVE_ARG(Initiation) initiation, + ASIO_MOVE_ARG(RawCompletionToken) token, + ASIO_MOVE_ARG(Args)... args) { + ASIO_MOVE_CAST(Initiation)(initiation)( + ASIO_MOVE_CAST(RawCompletionToken)(token), + ASIO_MOVE_CAST(Args)(args)...); } + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + + template + static return_type initiate( + ASIO_MOVE_ARG(Initiation) initiation, + ASIO_MOVE_ARG(RawCompletionToken) token) + { + ASIO_MOVE_CAST(Initiation)(initiation)( + ASIO_MOVE_CAST(RawCompletionToken)(token)); + } + +#define ASIO_PRIVATE_INITIATE_DEF(n) \ + template \ + static return_type initiate( \ + ASIO_MOVE_ARG(Initiation) initiation, \ + ASIO_MOVE_ARG(RawCompletionToken) token, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + ASIO_MOVE_CAST(Initiation)(initiation)( \ + ASIO_MOVE_CAST(RawCompletionToken)(token), \ + ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_INITIATE_DEF) +#undef ASIO_PRIVATE_INITIATE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + // || defined(GENERATING_DOCUMENTATION) + +private: + async_result(const async_result&) ASIO_DELETED; + async_result& operator=(const async_result&) ASIO_DELETED; }; -/// Helper template to deduce the real type of a handler, capture a local copy -/// of the handler, and then create an async_result for the handler. -template +/// Helper template to deduce the handler type from a CompletionToken, capture +/// a local copy of the handler, and then create an async_result for the +/// handler. +template struct async_completion { /// The real handler type to be used for the asynchronous operation. - typedef typename asio::handler_type< - Handler, Signature>::type handler_type; + typedef typename asio::async_result< + typename decay::type, + Signature>::completion_handler_type completion_handler_type; +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) /// Constructor. /** - * The constructor creates the concrete handler and makes the link between - * the handler and the asynchronous result. + * The constructor creates the concrete completion handler and makes the link + * between the handler and the asynchronous result. */ -#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) - explicit async_completion( - Handler& orig_handler) - : handler(static_cast::value, - handler_type&, Handler&&>::type>(orig_handler)), - result(handler) + explicit async_completion(CompletionToken& token) + : completion_handler(static_cast::value, + completion_handler_type&, CompletionToken&&>::type>(token)), + result(completion_handler) { } #else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) - explicit async_completion(const Handler& orig_handler) - : handler(orig_handler), - result(handler) + explicit async_completion(typename decay::type& token) + : completion_handler(token), + result(completion_handler) + { + } + + explicit async_completion(const typename decay::type& token) + : completion_handler(token), + result(completion_handler) { } #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) @@ -84,42 +160,197 @@ struct async_completion /// A copy of, or reference to, a real handler object. #if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) typename conditional< - is_same::value, - handler_type&, handler_type>::type handler; + is_same::value, + completion_handler_type&, completion_handler_type>::type completion_handler; #else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) - typename asio::handler_type::type handler; + completion_handler_type completion_handler; #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) /// The result of the asynchronous operation's initiating function. - async_result::type> result; + async_result::type, Signature> result; }; namespace detail { -template -struct async_result_type_helper +template +struct async_result_helper + : async_result::type, Signature> { - typedef typename async_result< - typename handler_type::type - >::type type; }; -} // namespace detail -} // namespace asio +struct async_result_memfns_base +{ + void initiate(); +}; -#include "asio/detail/pop_options.hpp" +template +struct async_result_memfns_derived + : T, async_result_memfns_base +{ +}; + +template +struct async_result_memfns_check +{ +}; + +template +char (&async_result_initiate_memfn_helper(...))[2]; + +template +char async_result_initiate_memfn_helper( + async_result_memfns_check< + void (async_result_memfns_base::*)(), + &async_result_memfns_derived::initiate>*); + +template +struct async_result_has_initiate_memfn + : integral_constant::type, Signature> + >(0)) != 1> +{ +}; + +} // namespace detail #if defined(GENERATING_DOCUMENTATION) -# define ASIO_INITFN_RESULT_TYPE(h, sig) \ +# define ASIO_INITFN_RESULT_TYPE(ct, sig) \ void_or_deduced #elif defined(_MSC_VER) && (_MSC_VER < 1500) -# define ASIO_INITFN_RESULT_TYPE(h, sig) \ - typename ::asio::detail::async_result_type_helper::type +# define ASIO_INITFN_RESULT_TYPE(ct, sig) \ + typename ::asio::detail::async_result_helper< \ + ct, sig>::return_type +#define ASIO_HANDLER_TYPE(ct, sig) \ + typename ::asio::detail::async_result_helper< \ + ct, sig>::completion_handler_type #else -# define ASIO_INITFN_RESULT_TYPE(h, sig) \ +# define ASIO_INITFN_RESULT_TYPE(ct, sig) \ + typename ::asio::async_result< \ + typename ::asio::decay::type, sig>::return_type +#define ASIO_HANDLER_TYPE(ct, sig) \ typename ::asio::async_result< \ - typename ::asio::handler_type::type>::type + typename ::asio::decay::type, sig>::completion_handler_type #endif +#if defined(GENERATING_DOCUMENTATION) + +template +ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature) +async_initiate(ASIO_MOVE_ARG(Initiation) initiation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken), + ASIO_MOVE_ARG(Args)... args); + +#elif defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +inline typename enable_if< + detail::async_result_has_initiate_memfn::value, + ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature)>::type +async_initiate(ASIO_MOVE_ARG(Initiation) initiation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, + ASIO_MOVE_ARG(Args)... args) +{ + return async_result::type, + Signature>::initiate(ASIO_MOVE_CAST(Initiation)(initiation), + ASIO_MOVE_CAST(CompletionToken)(token), + ASIO_MOVE_CAST(Args)(args)...); +} + +template +inline typename enable_if< + !detail::async_result_has_initiate_memfn::value, + ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature)>::type +async_initiate(ASIO_MOVE_ARG(Initiation) initiation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, + ASIO_MOVE_ARG(Args)... args) +{ + async_completion completion(token); + + ASIO_MOVE_CAST(Initiation)(initiation)( + ASIO_MOVE_CAST(ASIO_HANDLER_TYPE(CompletionToken, + Signature))(completion.completion_handler), + ASIO_MOVE_CAST(Args)(args)...); + + return completion.result.get(); +} + +#else // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +template +inline typename enable_if< + detail::async_result_has_initiate_memfn::value, + ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature)>::type +async_initiate(ASIO_MOVE_ARG(Initiation) initiation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token) +{ + return async_result::type, + Signature>::initiate(ASIO_MOVE_CAST(Initiation)(initiation), + ASIO_MOVE_CAST(CompletionToken)(token)); +} + +template +inline typename enable_if< + !detail::async_result_has_initiate_memfn::value, + ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature)>::type +async_initiate(ASIO_MOVE_ARG(Initiation) initiation, + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token) +{ + async_completion completion(token); + + ASIO_MOVE_CAST(Initiation)(initiation)( + ASIO_MOVE_CAST(ASIO_HANDLER_TYPE(CompletionToken, + Signature))(completion.completion_handler)); + + return completion.result.get(); +} + +#define ASIO_PRIVATE_INITIATE_DEF(n) \ + template \ + inline typename enable_if< \ + detail::async_result_has_initiate_memfn< \ + CompletionToken, Signature>::value, \ + ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature)>::type \ + async_initiate(ASIO_MOVE_ARG(Initiation) initiation, \ + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + return async_result::type, \ + Signature>::initiate(ASIO_MOVE_CAST(Initiation)(initiation), \ + ASIO_MOVE_CAST(CompletionToken)(token), \ + ASIO_VARIADIC_MOVE_ARGS(n)); \ + } \ + \ + template \ + inline typename enable_if< \ + !detail::async_result_has_initiate_memfn< \ + CompletionToken, Signature>::value, \ + ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature)>::type \ + async_initiate(ASIO_MOVE_ARG(Initiation) initiation, \ + ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, \ + ASIO_VARIADIC_MOVE_PARAMS(n)) \ + { \ + async_completion completion(token); \ + \ + ASIO_MOVE_CAST(Initiation)(initiation)( \ + ASIO_MOVE_CAST(ASIO_HANDLER_TYPE(CompletionToken, \ + Signature))(completion.completion_handler), \ + ASIO_VARIADIC_MOVE_ARGS(n)); \ + \ + return completion.result.get(); \ + } \ + /**/ + ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_INITIATE_DEF) +#undef ASIO_PRIVATE_INITIATE_DEF + +#endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + #endif // ASIO_ASYNC_RESULT_HPP diff --git a/source/modules/hylia/link/asio/awaitable.hpp b/source/modules/hylia/link/asio/awaitable.hpp new file mode 100644 index 000000000..890fb67e4 --- /dev/null +++ b/source/modules/hylia/link/asio/awaitable.hpp @@ -0,0 +1,123 @@ +// +// awaitable.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_AWAITABLE_HPP +#define ASIO_AWAITABLE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) + +#include +#include "asio/executor.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +using std::experimental::coroutine_handle; +using std::experimental::suspend_always; + +template class awaitable_thread; +template class awaitable_frame; + +} // namespace detail + +/// The return type of a coroutine or asynchronous operation. +template +class awaitable +{ +public: + /// The type of the awaited value. + typedef T value_type; + + /// The executor type that will be used for the coroutine. + typedef Executor executor_type; + + /// Default constructor. + constexpr awaitable() noexcept + : frame_(nullptr) + { + } + + /// Move constructor. + awaitable(awaitable&& other) noexcept + : frame_(std::exchange(other.frame_, nullptr)) + { + } + + /// Destructor + ~awaitable() + { + if (frame_) + frame_->destroy(); + } + + /// Checks if the awaitable refers to a future result. + bool valid() const noexcept + { + return !!frame_; + } + +#if !defined(GENERATING_DOCUMENTATION) + + // Support for co_await keyword. + bool await_ready() const noexcept + { + return false; + } + + // Support for co_await keyword. + template + void await_suspend( + detail::coroutine_handle> h) + { + frame_->push_frame(&h.promise()); + } + + // Support for co_await keyword. + T await_resume() + { + return frame_->get(); + } + +#endif // !defined(GENERATING_DOCUMENTATION) + +private: + template friend class detail::awaitable_thread; + template friend class detail::awaitable_frame; + + // Not copy constructible or copy assignable. + awaitable(const awaitable&) = delete; + awaitable& operator=(const awaitable&) = delete; + + // Construct the awaitable from a coroutine's frame object. + explicit awaitable(detail::awaitable_frame* a) + : frame_(a) + { + } + + detail::awaitable_frame* frame_; +}; + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/awaitable.hpp" + +#endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) + +#endif // ASIO_AWAITABLE_HPP diff --git a/source/modules/hylia/link/asio/basic_datagram_socket.hpp b/source/modules/hylia/link/asio/basic_datagram_socket.hpp index d9748eea2..bbd4885e1 100644 --- a/source/modules/hylia/link/asio/basic_datagram_socket.hpp +++ b/source/modules/hylia/link/asio/basic_datagram_socket.hpp @@ -2,7 +2,7 @@ // basic_datagram_socket.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,8 +18,8 @@ #include "asio/detail/config.hpp" #include #include "asio/basic_socket.hpp" -#include "asio/datagram_socket_service.hpp" #include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/non_const_lvalue.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/type_traits.hpp" #include "asio/error.hpp" @@ -28,6 +28,15 @@ namespace asio { +#if !defined(ASIO_BASIC_DATAGRAM_SOCKET_FWD_DECL) +#define ASIO_BASIC_DATAGRAM_SOCKET_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_datagram_socket; + +#endif // !defined(ASIO_BASIC_DATAGRAM_SOCKET_FWD_DECL) + /// Provides datagram-oriented socket functionality. /** * The basic_datagram_socket class template provides asynchronous and blocking @@ -37,14 +46,29 @@ namespace asio { * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ -template > +template class basic_datagram_socket - : public basic_socket + : public basic_socket { public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_datagram_socket other; + }; + /// The native representation of a socket. - typedef typename DatagramSocketService::native_handle_type native_handle_type; +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket::native_handle_type native_handle_type; +#endif /// The protocol type. typedef Protocol protocol_type; @@ -57,12 +81,29 @@ public: * This constructor creates a datagram socket without opening it. The open() * function must be called before data can be sent or received on the socket. * - * @param io_context The io_context object that the datagram socket will use - * to dispatch handlers for any asynchronous operations performed on the - * socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + */ + explicit basic_datagram_socket(const executor_type& ex) + : basic_socket(ex) + { + } + + /// Construct a basic_datagram_socket without opening it. + /** + * This constructor creates a datagram socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. */ - explicit basic_datagram_socket(asio::io_context& io_context) - : basic_socket(io_context) + template + explicit basic_datagram_socket(ExecutionContext& context, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context) { } @@ -70,17 +111,37 @@ public: /** * This constructor creates and opens a datagram socket. * - * @param io_context The io_context object that the datagram socket will use - * to dispatch handlers for any asynchronous operations performed on the - * socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + basic_datagram_socket(const executor_type& ex, const protocol_type& protocol) + : basic_socket(ex, protocol) + { + } + + /// Construct and open a basic_datagram_socket. + /** + * This constructor creates and opens a datagram socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * * @throws asio::system_error Thrown on failure. */ - basic_datagram_socket(asio::io_context& io_context, - const protocol_type& protocol) - : basic_socket(io_context, protocol) + template + basic_datagram_socket(ExecutionContext& context, + const protocol_type& protocol, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context, protocol) { } @@ -91,18 +152,42 @@ public: * to the specified endpoint on the local machine. The protocol used is the * protocol associated with the given endpoint. * - * @param io_context The io_context object that the datagram socket will use - * to dispatch handlers for any asynchronous operations performed on the - * socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. * * @param endpoint An endpoint on the local machine to which the datagram * socket will be bound. * * @throws asio::system_error Thrown on failure. */ - basic_datagram_socket(asio::io_context& io_context, - const endpoint_type& endpoint) - : basic_socket(io_context, endpoint) + basic_datagram_socket(const executor_type& ex, const endpoint_type& endpoint) + : basic_socket(ex, endpoint) + { + } + + /// Construct a basic_datagram_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a datagram socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the datagram + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_datagram_socket(ExecutionContext& context, + const endpoint_type& endpoint, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context, endpoint) { } @@ -111,9 +196,8 @@ public: * This constructor creates a datagram socket object to hold an existing * native socket. * - * @param io_context The io_context object that the datagram socket will use - * to dispatch handlers for any asynchronous operations performed on the - * socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * @@ -121,10 +205,34 @@ public: * * @throws asio::system_error Thrown on failure. */ - basic_datagram_socket(asio::io_context& io_context, + basic_datagram_socket(const executor_type& ex, const protocol_type& protocol, const native_handle_type& native_socket) - : basic_socket( - io_context, protocol, native_socket) + : basic_socket(ex, protocol, native_socket) + { + } + + /// Construct a basic_datagram_socket on an existing native socket. + /** + * This constructor creates a datagram socket object to hold an existing + * native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_datagram_socket(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_socket, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context, protocol, native_socket) { } @@ -137,11 +245,11 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_datagram_socket(io_context&) constructor. + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. */ basic_datagram_socket(basic_datagram_socket&& other) - : basic_socket( - ASIO_MOVE_CAST(basic_datagram_socket)(other)) + : basic_socket(std::move(other)) { } @@ -154,12 +262,12 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_datagram_socket(io_context&) constructor. + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. */ basic_datagram_socket& operator=(basic_datagram_socket&& other) { - basic_socket::operator=( - ASIO_MOVE_CAST(basic_datagram_socket)(other)); + basic_socket::operator=(std::move(other)); return *this; } @@ -172,15 +280,16 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_datagram_socket(io_context&) constructor. + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. */ - template - basic_datagram_socket( - basic_datagram_socket&& other, - typename enable_if::value>::type* = 0) - : basic_socket( - ASIO_MOVE_CAST2(basic_datagram_socket< - Protocol1, DatagramSocketService1>)(other)) + template + basic_datagram_socket(basic_datagram_socket&& other, + typename enable_if< + is_convertible::value + && is_convertible::value + >::type* = 0) + : basic_socket(std::move(other)) { } @@ -194,20 +303,30 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_datagram_socket(io_context&) constructor. + * constructed using the @c basic_datagram_socket(const executor_type&) + * constructor. */ - template - typename enable_if::value, - basic_datagram_socket>::type& operator=( - basic_datagram_socket&& other) + template + typename enable_if< + is_convertible::value + && is_convertible::value, + basic_datagram_socket& + >::type operator=(basic_datagram_socket&& other) { - basic_socket::operator=( - ASIO_MOVE_CAST2(basic_datagram_socket< - Protocol1, DatagramSocketService1>)(other)); + basic_socket::operator=(std::move(other)); return *this; } #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_datagram_socket() + { + } + /// Send some data on a connected socket. /** * This function is used to send data on the datagram socket. The function @@ -234,8 +353,8 @@ public: std::size_t send(const ConstBufferSequence& buffers) { asio::error_code ec; - std::size_t s = this->get_service().send( - this->get_implementation(), buffers, 0, ec); + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); asio::detail::throw_error(ec, "send"); return s; } @@ -262,8 +381,8 @@ public: socket_base::message_flags flags) { asio::error_code ec; - std::size_t s = this->get_service().send( - this->get_implementation(), buffers, flags, ec); + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); asio::detail::throw_error(ec, "send"); return s; } @@ -289,8 +408,8 @@ public: std::size_t send(const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { - return this->get_service().send( - this->get_implementation(), buffers, flags, ec); + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); } /// Start an asynchronous send on a connected socket. @@ -311,9 +430,9 @@ public: * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @note The async_send operation can only be used with a connected socket. * Use the async_send_to function to send data on an unconnected datagram @@ -334,12 +453,10 @@ public: async_send(const ConstBufferSequence& buffers, ASIO_MOVE_ARG(WriteHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WriteHandler. - ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; - - return this->get_service().async_send(this->get_implementation(), - buffers, 0, ASIO_MOVE_CAST(WriteHandler)(handler)); + return async_initiate( + initiate_async_send(), handler, this, + buffers, socket_base::message_flags(0)); } /// Start an asynchronous send on a connected socket. @@ -362,9 +479,9 @@ public: * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @note The async_send operation can only be used with a connected socket. * Use the async_send_to function to send data on an unconnected datagram @@ -377,12 +494,9 @@ public: socket_base::message_flags flags, ASIO_MOVE_ARG(WriteHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WriteHandler. - ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; - - return this->get_service().async_send(this->get_implementation(), - buffers, flags, ASIO_MOVE_CAST(WriteHandler)(handler)); + return async_initiate( + initiate_async_send(), handler, this, buffers, flags); } /// Send a datagram to the specified endpoint. @@ -415,8 +529,8 @@ public: const endpoint_type& destination) { asio::error_code ec; - std::size_t s = this->get_service().send_to( - this->get_implementation(), buffers, destination, 0, ec); + std::size_t s = this->impl_.get_service().send_to( + this->impl_.get_implementation(), buffers, destination, 0, ec); asio::detail::throw_error(ec, "send_to"); return s; } @@ -442,8 +556,8 @@ public: const endpoint_type& destination, socket_base::message_flags flags) { asio::error_code ec; - std::size_t s = this->get_service().send_to( - this->get_implementation(), buffers, destination, flags, ec); + std::size_t s = this->impl_.get_service().send_to( + this->impl_.get_implementation(), buffers, destination, flags, ec); asio::detail::throw_error(ec, "send_to"); return s; } @@ -469,7 +583,7 @@ public: const endpoint_type& destination, socket_base::message_flags flags, asio::error_code& ec) { - return this->get_service().send_to(this->get_implementation(), + return this->impl_.get_service().send_to(this->impl_.get_implementation(), buffers, destination, flags, ec); } @@ -494,9 +608,9 @@ public: * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * To send a single data buffer use the @ref buffer function as follows: @@ -517,13 +631,10 @@ public: const endpoint_type& destination, ASIO_MOVE_ARG(WriteHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WriteHandler. - ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; - - return this->get_service().async_send_to( - this->get_implementation(), buffers, destination, 0, - ASIO_MOVE_CAST(WriteHandler)(handler)); + return async_initiate( + initiate_async_send_to(), handler, this, buffers, + destination, socket_base::message_flags(0)); } /// Start an asynchronous send. @@ -549,9 +660,9 @@ public: * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). */ template ASIO_INITFN_RESULT_TYPE(WriteHandler, @@ -560,13 +671,9 @@ public: const endpoint_type& destination, socket_base::message_flags flags, ASIO_MOVE_ARG(WriteHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WriteHandler. - ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; - - return this->get_service().async_send_to( - this->get_implementation(), buffers, destination, flags, - ASIO_MOVE_CAST(WriteHandler)(handler)); + return async_initiate( + initiate_async_send_to(), handler, this, buffers, destination, flags); } /// Receive some data on a connected socket. @@ -597,8 +704,8 @@ public: std::size_t receive(const MutableBufferSequence& buffers) { asio::error_code ec; - std::size_t s = this->get_service().receive( - this->get_implementation(), buffers, 0, ec); + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); asio::detail::throw_error(ec, "receive"); return s; } @@ -626,8 +733,8 @@ public: socket_base::message_flags flags) { asio::error_code ec; - std::size_t s = this->get_service().receive( - this->get_implementation(), buffers, flags, ec); + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); asio::detail::throw_error(ec, "receive"); return s; } @@ -654,8 +761,8 @@ public: std::size_t receive(const MutableBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { - return this->get_service().receive( - this->get_implementation(), buffers, flags, ec); + return this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); } /// Start an asynchronous receive on a connected socket. @@ -676,9 +783,9 @@ public: * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @note The async_receive operation can only be used with a connected socket. * Use the async_receive_from function to receive data on an unconnected @@ -700,12 +807,10 @@ public: async_receive(const MutableBufferSequence& buffers, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_receive(this->get_implementation(), - buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_receive(), handler, this, + buffers, socket_base::message_flags(0)); } /// Start an asynchronous receive on a connected socket. @@ -728,9 +833,9 @@ public: * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @note The async_receive operation can only be used with a connected socket. * Use the async_receive_from function to receive data on an unconnected @@ -743,12 +848,9 @@ public: socket_base::message_flags flags, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_receive(this->get_implementation(), - buffers, flags, ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_receive(), handler, this, buffers, flags); } /// Receive a datagram with the endpoint of the sender. @@ -782,8 +884,8 @@ public: endpoint_type& sender_endpoint) { asio::error_code ec; - std::size_t s = this->get_service().receive_from( - this->get_implementation(), buffers, sender_endpoint, 0, ec); + std::size_t s = this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, 0, ec); asio::detail::throw_error(ec, "receive_from"); return s; } @@ -809,8 +911,8 @@ public: endpoint_type& sender_endpoint, socket_base::message_flags flags) { asio::error_code ec; - std::size_t s = this->get_service().receive_from( - this->get_implementation(), buffers, sender_endpoint, flags, ec); + std::size_t s = this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, flags, ec); asio::detail::throw_error(ec, "receive_from"); return s; } @@ -836,8 +938,8 @@ public: endpoint_type& sender_endpoint, socket_base::message_flags flags, asio::error_code& ec) { - return this->get_service().receive_from(this->get_implementation(), - buffers, sender_endpoint, flags, ec); + return this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, flags, ec); } /// Start an asynchronous receive. @@ -863,9 +965,9 @@ public: * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * To receive into a single data buffer use the @ref buffer function as @@ -883,13 +985,10 @@ public: endpoint_type& sender_endpoint, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_receive_from( - this->get_implementation(), buffers, sender_endpoint, 0, - ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_receive_from(), handler, this, buffers, + &sender_endpoint, socket_base::message_flags(0)); } /// Start an asynchronous receive. @@ -917,9 +1016,9 @@ public: * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). */ template ASIO_INITFN_RESULT_TYPE(ReadHandler, @@ -928,14 +1027,85 @@ public: endpoint_type& sender_endpoint, socket_base::message_flags flags, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_receive_from( - this->get_implementation(), buffers, sender_endpoint, flags, - ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_receive_from(), handler, + this, buffers, &sender_endpoint, flags); } + +private: + struct initiate_async_send + { + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + basic_datagram_socket* self, const ConstBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_send( + self->impl_.get_implementation(), buffers, flags, + handler2.value, self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_send_to + { + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + basic_datagram_socket* self, const ConstBufferSequence& buffers, + const endpoint_type& destination, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_send_to( + self->impl_.get_implementation(), buffers, destination, flags, + handler2.value, self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_receive + { + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + basic_datagram_socket* self, const MutableBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_receive( + self->impl_.get_implementation(), buffers, flags, + handler2.value, self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_receive_from + { + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + basic_datagram_socket* self, const MutableBufferSequence& buffers, + endpoint_type* sender_endpoint, socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_receive_from( + self->impl_.get_implementation(), buffers, *sender_endpoint, flags, + handler2.value, self->impl_.get_implementation_executor()); + } + }; }; } // namespace asio diff --git a/source/modules/hylia/link/asio/basic_deadline_timer.hpp b/source/modules/hylia/link/asio/basic_deadline_timer.hpp index 030053eab..d8100a793 100644 --- a/source/modules/hylia/link/asio/basic_deadline_timer.hpp +++ b/source/modules/hylia/link/asio/basic_deadline_timer.hpp @@ -2,7 +2,7 @@ // basic_deadline_timer.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -21,11 +21,15 @@ || defined(GENERATING_DOCUMENTATION) #include -#include "asio/basic_io_object.hpp" -#include "asio/deadline_timer_service.hpp" +#include "asio/detail/deadline_timer_service.hpp" #include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/executor.hpp" +#include "asio/time_traits.hpp" #include "asio/detail/push_options.hpp" @@ -50,7 +54,7 @@ namespace asio { * Performing a blocking wait: * @code * // Construct a timer without setting an expiry time. - * asio::deadline_timer timer(io_context); + * asio::deadline_timer timer(my_context); * * // Set an expiry time relative to now. * timer.expires_from_now(boost::posix_time::seconds(5)); @@ -73,7 +77,7 @@ namespace asio { * ... * * // Construct a timer with an absolute expiry time. - * asio::deadline_timer timer(io_context, + * asio::deadline_timer timer(my_context, * boost::posix_time::time_from_string("2005-12-07 23:59:59.000")); * * // Start an asynchronous wait. @@ -121,11 +125,13 @@ namespace asio { */ template , - typename TimerService = deadline_timer_service > + typename Executor = executor> class basic_deadline_timer - : public basic_io_object { public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + /// The time traits type. typedef TimeTraits traits_type; @@ -141,11 +147,30 @@ public: * expires_at() or expires_from_now() functions must be called to set an * expiry time before the timer can be waited on. * - * @param io_context The io_context object that the timer will use to dispatch - * handlers for any asynchronous operations performed on the timer. + * @param ex The I/O executor that the timer will use, by default, to + * dispatch handlers for any asynchronous operations performed on the timer. */ - explicit basic_deadline_timer(asio::io_context& io_context) - : basic_io_object(io_context) + explicit basic_deadline_timer(const executor_type& ex) + : impl_(ex) + { + } + + /// Constructor. + /** + * This constructor creates a timer without setting an expiry time. The + * expires_at() or expires_from_now() functions must be called to set an + * expiry time before the timer can be waited on. + * + * @param context An execution context which provides the I/O executor that + * the timer will use, by default, to dispatch handlers for any asynchronous + * operations performed on the timer. + */ + template + explicit basic_deadline_timer(ExecutionContext& context, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) { } @@ -153,18 +178,40 @@ public: /** * This constructor creates a timer and sets the expiry time. * - * @param io_context The io_context object that the timer will use to dispatch - * handlers for any asynchronous operations performed on the timer. + * @param ex The I/O executor that the timer will use, by default, to + * dispatch handlers for any asynchronous operations performed on the timer. * * @param expiry_time The expiry time to be used for the timer, expressed * as an absolute time. */ - basic_deadline_timer(asio::io_context& io_context, - const time_type& expiry_time) - : basic_io_object(io_context) + basic_deadline_timer(const executor_type& ex, const time_type& expiry_time) + : impl_(ex) { asio::error_code ec; - this->get_service().expires_at(this->get_implementation(), expiry_time, ec); + impl_.get_service().expires_at(impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_at"); + } + + /// Constructor to set a particular expiry time as an absolute time. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param context An execution context which provides the I/O executor that + * the timer will use, by default, to dispatch handlers for any asynchronous + * operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, expressed + * as an absolute time. + */ + template + basic_deadline_timer(ExecutionContext& context, const time_type& expiry_time, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + asio::error_code ec; + impl_.get_service().expires_at(impl_.get_implementation(), expiry_time, ec); asio::detail::throw_error(ec, "expires_at"); } @@ -172,22 +219,98 @@ public: /** * This constructor creates a timer and sets the expiry time. * - * @param io_context The io_context object that the timer will use to dispatch - * handlers for any asynchronous operations performed on the timer. + * @param ex The I/O executor that the timer will use, by default, to + * dispatch handlers for any asynchronous operations performed on the timer. * * @param expiry_time The expiry time to be used for the timer, relative to * now. */ - basic_deadline_timer(asio::io_context& io_context, + basic_deadline_timer(const executor_type& ex, const duration_type& expiry_time) - : basic_io_object(io_context) + : impl_(ex) { asio::error_code ec; - this->get_service().expires_from_now( - this->get_implementation(), expiry_time, ec); + impl_.get_service().expires_from_now( + impl_.get_implementation(), expiry_time, ec); asio::detail::throw_error(ec, "expires_from_now"); } + /// Constructor to set a particular expiry time relative to now. + /** + * This constructor creates a timer and sets the expiry time. + * + * @param context An execution context which provides the I/O executor that + * the timer will use, by default, to dispatch handlers for any asynchronous + * operations performed on the timer. + * + * @param expiry_time The expiry time to be used for the timer, relative to + * now. + */ + template + basic_deadline_timer(ExecutionContext& context, + const duration_type& expiry_time, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + asio::error_code ec; + impl_.get_service().expires_from_now( + impl_.get_implementation(), expiry_time, ec); + asio::detail::throw_error(ec, "expires_from_now"); + } + +#if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Move-construct a basic_deadline_timer from another. + /** + * This constructor moves a timer from one object to another. + * + * @param other The other basic_deadline_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_deadline_timer(const executor_type&) + * constructor. + */ + basic_deadline_timer(basic_deadline_timer&& other) + : impl_(std::move(other.impl_)) + { + } + + /// Move-assign a basic_deadline_timer from another. + /** + * This assignment operator moves a timer from one object to another. Cancels + * any outstanding asynchronous operations associated with the target object. + * + * @param other The other basic_deadline_timer object from which the move will + * occur. + * + * @note Following the move, the moved-from object is in the same state as if + * constructed using the @c basic_deadline_timer(const executor_type&) + * constructor. + */ + basic_deadline_timer& operator=(basic_deadline_timer&& other) + { + impl_ = std::move(other.impl_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + + /// Destroys the timer. + /** + * This function destroys the timer, cancelling any outstanding asynchronous + * wait operations associated with the timer as if by calling @c cancel. + */ + ~basic_deadline_timer() + { + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + /// Cancel any asynchronous operations that are waiting on the timer. /** * This function forces the completion of any pending asynchronous wait @@ -213,7 +336,7 @@ public: std::size_t cancel() { asio::error_code ec; - std::size_t s = this->get_service().cancel(this->get_implementation(), ec); + std::size_t s = impl_.get_service().cancel(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "cancel"); return s; } @@ -242,7 +365,7 @@ public: */ std::size_t cancel(asio::error_code& ec) { - return this->get_service().cancel(this->get_implementation(), ec); + return impl_.get_service().cancel(impl_.get_implementation(), ec); } /// Cancels one asynchronous operation that is waiting on the timer. @@ -272,8 +395,8 @@ public: std::size_t cancel_one() { asio::error_code ec; - std::size_t s = this->get_service().cancel_one( - this->get_implementation(), ec); + std::size_t s = impl_.get_service().cancel_one( + impl_.get_implementation(), ec); asio::detail::throw_error(ec, "cancel_one"); return s; } @@ -304,7 +427,7 @@ public: */ std::size_t cancel_one(asio::error_code& ec) { - return this->get_service().cancel_one(this->get_implementation(), ec); + return impl_.get_service().cancel_one(impl_.get_implementation(), ec); } /// Get the timer's expiry time as an absolute time. @@ -314,7 +437,7 @@ public: */ time_type expires_at() const { - return this->get_service().expires_at(this->get_implementation()); + return impl_.get_service().expires_at(impl_.get_implementation()); } /// Set the timer's expiry time as an absolute time. @@ -342,8 +465,8 @@ public: std::size_t expires_at(const time_type& expiry_time) { asio::error_code ec; - std::size_t s = this->get_service().expires_at( - this->get_implementation(), expiry_time, ec); + std::size_t s = impl_.get_service().expires_at( + impl_.get_implementation(), expiry_time, ec); asio::detail::throw_error(ec, "expires_at"); return s; } @@ -373,8 +496,8 @@ public: std::size_t expires_at(const time_type& expiry_time, asio::error_code& ec) { - return this->get_service().expires_at( - this->get_implementation(), expiry_time, ec); + return impl_.get_service().expires_at( + impl_.get_implementation(), expiry_time, ec); } /// Get the timer's expiry time relative to now. @@ -384,7 +507,7 @@ public: */ duration_type expires_from_now() const { - return this->get_service().expires_from_now(this->get_implementation()); + return impl_.get_service().expires_from_now(impl_.get_implementation()); } /// Set the timer's expiry time relative to now. @@ -412,8 +535,8 @@ public: std::size_t expires_from_now(const duration_type& expiry_time) { asio::error_code ec; - std::size_t s = this->get_service().expires_from_now( - this->get_implementation(), expiry_time, ec); + std::size_t s = impl_.get_service().expires_from_now( + impl_.get_implementation(), expiry_time, ec); asio::detail::throw_error(ec, "expires_from_now"); return s; } @@ -443,8 +566,8 @@ public: std::size_t expires_from_now(const duration_type& expiry_time, asio::error_code& ec) { - return this->get_service().expires_from_now( - this->get_implementation(), expiry_time, ec); + return impl_.get_service().expires_from_now( + impl_.get_implementation(), expiry_time, ec); } /// Perform a blocking wait on the timer. @@ -457,7 +580,7 @@ public: void wait() { asio::error_code ec; - this->get_service().wait(this->get_implementation(), ec); + impl_.get_service().wait(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "wait"); } @@ -470,7 +593,7 @@ public: */ void wait(asio::error_code& ec) { - this->get_service().wait(this->get_implementation(), ec); + impl_.get_service().wait(impl_.get_implementation(), ec); } /// Start an asynchronous wait on the timer. @@ -493,22 +616,44 @@ public: * const asio::error_code& error // Result of operation. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). */ template ASIO_INITFN_RESULT_TYPE(WaitHandler, void (asio::error_code)) async_wait(ASIO_MOVE_ARG(WaitHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WaitHandler. - ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; - - return this->get_service().async_wait(this->get_implementation(), - ASIO_MOVE_CAST(WaitHandler)(handler)); + return async_initiate( + initiate_async_wait(), handler, this); } + +private: + // Disallow copying and assignment. + basic_deadline_timer(const basic_deadline_timer&) ASIO_DELETED; + basic_deadline_timer& operator=( + const basic_deadline_timer&) ASIO_DELETED; + + struct initiate_async_wait + { + template + void operator()(ASIO_MOVE_ARG(WaitHandler) handler, + basic_deadline_timer* self) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_wait( + self->impl_.get_implementation(), handler2.value, + self->impl_.get_implementation_executor()); + } + }; + + detail::io_object_impl< + detail::deadline_timer_service, Executor> impl_; }; } // namespace asio diff --git a/source/modules/hylia/link/asio/basic_io_object.hpp b/source/modules/hylia/link/asio/basic_io_object.hpp index 40cf89632..faf200344 100644 --- a/source/modules/hylia/link/asio/basic_io_object.hpp +++ b/source/modules/hylia/link/asio/basic_io_object.hpp @@ -2,7 +2,7 @@ // basic_io_object.hpp // ~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -34,12 +34,13 @@ namespace detail typedef typename service_type::implementation_type implementation_type; template - static auto eval(T* t, U* u) -> decltype(t->move_construct(*u, *u), char()); - static char (&eval(...))[2]; + static auto asio_service_has_move_eval(T* t, U* u) + -> decltype(t->move_construct(*u, *u), char()); + static char (&asio_service_has_move_eval(...))[2]; public: static const bool value = - sizeof(service_has_move::eval( + sizeof(asio_service_has_move_eval( static_cast(0), static_cast(0))) == 1; }; @@ -137,6 +138,11 @@ protected: * @note Available only for services that support movability, */ basic_io_object& operator=(basic_io_object&& other); + + /// Perform a converting move-construction of a basic_io_object. + template + basic_io_object(IoObjectService1& other_service, + typename IoObjectService1::implementation_type& other_implementation); #endif // defined(GENERATING_DOCUMENTATION) /// Protected destructor to prevent deletion through this type. @@ -225,6 +231,16 @@ protected: service_->move_construct(implementation_, other.implementation_); } + template + basic_io_object(IoObjectService1& other_service, + typename IoObjectService1::implementation_type& other_implementation) + : service_(&asio::use_service( + other_service.get_io_context())) + { + service_->converting_move_construct(implementation_, + other_service, other_implementation); + } + ~basic_io_object() { service_->destroy(implementation_); diff --git a/source/modules/hylia/link/asio/basic_raw_socket.hpp b/source/modules/hylia/link/asio/basic_raw_socket.hpp index 3ca392b79..1cc54f49e 100644 --- a/source/modules/hylia/link/asio/basic_raw_socket.hpp +++ b/source/modules/hylia/link/asio/basic_raw_socket.hpp @@ -2,7 +2,7 @@ // basic_raw_socket.hpp // ~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -19,15 +19,24 @@ #include #include "asio/basic_socket.hpp" #include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/non_const_lvalue.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/type_traits.hpp" #include "asio/error.hpp" -#include "asio/raw_socket_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { +#if !defined(ASIO_BASIC_RAW_SOCKET_FWD_DECL) +#define ASIO_BASIC_RAW_SOCKET_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_raw_socket; + +#endif // !defined(ASIO_BASIC_RAW_SOCKET_FWD_DECL) + /// Provides raw-oriented socket functionality. /** * The basic_raw_socket class template provides asynchronous and blocking @@ -37,14 +46,29 @@ namespace asio { * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ -template > +template class basic_raw_socket - : public basic_socket + : public basic_socket { public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_raw_socket other; + }; + /// The native representation of a socket. - typedef typename RawSocketService::native_handle_type native_handle_type; +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket::native_handle_type native_handle_type; +#endif /// The protocol type. typedef Protocol protocol_type; @@ -57,12 +81,29 @@ public: * This constructor creates a raw socket without opening it. The open() * function must be called before data can be sent or received on the socket. * - * @param io_context The io_context object that the raw socket will use - * to dispatch handlers for any asynchronous operations performed on the - * socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. */ - explicit basic_raw_socket(asio::io_context& io_context) - : basic_socket(io_context) + explicit basic_raw_socket(const executor_type& ex) + : basic_socket(ex) + { + } + + /// Construct a basic_raw_socket without opening it. + /** + * This constructor creates a raw socket without opening it. The open() + * function must be called before data can be sent or received on the socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template + explicit basic_raw_socket(ExecutionContext& context, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context) { } @@ -70,17 +111,36 @@ public: /** * This constructor creates and opens a raw socket. * - * @param io_context The io_context object that the raw socket will use - * to dispatch handlers for any asynchronous operations performed on the - * socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * * @throws asio::system_error Thrown on failure. */ - basic_raw_socket(asio::io_context& io_context, - const protocol_type& protocol) - : basic_socket(io_context, protocol) + basic_raw_socket(const executor_type& ex, const protocol_type& protocol) + : basic_socket(ex, protocol) + { + } + + /// Construct and open a basic_raw_socket. + /** + * This constructor creates and opens a raw socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_raw_socket(ExecutionContext& context, const protocol_type& protocol, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context, protocol) { } @@ -91,18 +151,41 @@ public: * to the specified endpoint on the local machine. The protocol used is the * protocol associated with the given endpoint. * - * @param io_context The io_context object that the raw socket will use - * to dispatch handlers for any asynchronous operations performed on the - * socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. * * @param endpoint An endpoint on the local machine to which the raw * socket will be bound. * * @throws asio::system_error Thrown on failure. */ - basic_raw_socket(asio::io_context& io_context, - const endpoint_type& endpoint) - : basic_socket(io_context, endpoint) + basic_raw_socket(const executor_type& ex, const endpoint_type& endpoint) + : basic_socket(ex, endpoint) + { + } + + /// Construct a basic_raw_socket, opening it and binding it to the given + /// local endpoint. + /** + * This constructor creates a raw socket and automatically opens it bound + * to the specified endpoint on the local machine. The protocol used is the + * protocol associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the raw + * socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_raw_socket(ExecutionContext& context, const endpoint_type& endpoint, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context, endpoint) { } @@ -111,9 +194,8 @@ public: * This constructor creates a raw socket object to hold an existing * native socket. * - * @param io_context The io_context object that the raw socket will use - * to dispatch handlers for any asynchronous operations performed on the - * socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * @@ -121,10 +203,34 @@ public: * * @throws asio::system_error Thrown on failure. */ - basic_raw_socket(asio::io_context& io_context, + basic_raw_socket(const executor_type& ex, const protocol_type& protocol, const native_handle_type& native_socket) - : basic_socket( - io_context, protocol, native_socket) + : basic_socket(ex, protocol, native_socket) + { + } + + /// Construct a basic_raw_socket on an existing native socket. + /** + * This constructor creates a raw socket object to hold an existing + * native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_raw_socket(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_socket, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context, protocol, native_socket) { } @@ -137,11 +243,11 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_raw_socket(io_context&) constructor. + * constructed using the @c basic_raw_socket(const executor_type&) + * constructor. */ basic_raw_socket(basic_raw_socket&& other) - : basic_socket( - ASIO_MOVE_CAST(basic_raw_socket)(other)) + : basic_socket(std::move(other)) { } @@ -153,31 +259,34 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_raw_socket(io_context&) constructor. + * constructed using the @c basic_raw_socket(const executor_type&) + * constructor. */ basic_raw_socket& operator=(basic_raw_socket&& other) { - basic_socket::operator=( - ASIO_MOVE_CAST(basic_raw_socket)(other)); + basic_socket::operator=(std::move(other)); return *this; } - /// Move-construct a basic_raw_socket from a socket of another protocol type. + /// Move-construct a basic_raw_socket from a socket of another protocol + /// type. /** * This constructor moves a raw socket from one object to another. * - * @param other The other basic_raw_socket object from which the move will - * occur. + * @param other The other basic_raw_socket object from which the move + * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_raw_socket(io_context&) constructor. + * constructed using the @c basic_raw_socket(const executor_type&) + * constructor. */ - template - basic_raw_socket(basic_raw_socket&& other, - typename enable_if::value>::type* = 0) - : basic_socket( - ASIO_MOVE_CAST2(basic_raw_socket< - Protocol1, RawSocketService1>)(other)) + template + basic_raw_socket(basic_raw_socket&& other, + typename enable_if< + is_convertible::value + && is_convertible::value + >::type* = 0) + : basic_socket(std::move(other)) { } @@ -189,20 +298,30 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_raw_socket(io_context&) constructor. + * constructed using the @c basic_raw_socket(const executor_type&) + * constructor. */ - template - typename enable_if::value, - basic_raw_socket>::type& operator=( - basic_raw_socket&& other) + template + typename enable_if< + is_convertible::value + && is_convertible::value, + basic_raw_socket& + >::type operator=(basic_raw_socket&& other) { - basic_socket::operator=( - ASIO_MOVE_CAST2(basic_raw_socket< - Protocol1, RawSocketService1>)(other)); + basic_socket::operator=(std::move(other)); return *this; } #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_raw_socket() + { + } + /// Send some data on a connected socket. /** * This function is used to send data on the raw socket. The function call @@ -228,8 +347,8 @@ public: std::size_t send(const ConstBufferSequence& buffers) { asio::error_code ec; - std::size_t s = this->get_service().send( - this->get_implementation(), buffers, 0, ec); + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, 0, ec); asio::detail::throw_error(ec, "send"); return s; } @@ -255,8 +374,8 @@ public: socket_base::message_flags flags) { asio::error_code ec; - std::size_t s = this->get_service().send( - this->get_implementation(), buffers, flags, ec); + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); asio::detail::throw_error(ec, "send"); return s; } @@ -281,8 +400,8 @@ public: std::size_t send(const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { - return this->get_service().send( - this->get_implementation(), buffers, flags, ec); + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); } /// Start an asynchronous send on a connected socket. @@ -303,9 +422,9 @@ public: * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @note The async_send operation can only be used with a connected socket. * Use the async_send_to function to send data on an unconnected raw @@ -326,12 +445,10 @@ public: async_send(const ConstBufferSequence& buffers, ASIO_MOVE_ARG(WriteHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WriteHandler. - ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; - - return this->get_service().async_send(this->get_implementation(), - buffers, 0, ASIO_MOVE_CAST(WriteHandler)(handler)); + return async_initiate( + initiate_async_send(), handler, this, + buffers, socket_base::message_flags(0)); } /// Start an asynchronous send on a connected socket. @@ -354,9 +471,9 @@ public: * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @note The async_send operation can only be used with a connected socket. * Use the async_send_to function to send data on an unconnected raw @@ -369,12 +486,9 @@ public: socket_base::message_flags flags, ASIO_MOVE_ARG(WriteHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WriteHandler. - ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; - - return this->get_service().async_send(this->get_implementation(), - buffers, flags, ASIO_MOVE_CAST(WriteHandler)(handler)); + return async_initiate( + initiate_async_send(), handler, this, buffers, flags); } /// Send raw data to the specified endpoint. @@ -407,8 +521,8 @@ public: const endpoint_type& destination) { asio::error_code ec; - std::size_t s = this->get_service().send_to( - this->get_implementation(), buffers, destination, 0, ec); + std::size_t s = this->impl_.get_service().send_to( + this->impl_.get_implementation(), buffers, destination, 0, ec); asio::detail::throw_error(ec, "send_to"); return s; } @@ -434,8 +548,8 @@ public: const endpoint_type& destination, socket_base::message_flags flags) { asio::error_code ec; - std::size_t s = this->get_service().send_to( - this->get_implementation(), buffers, destination, flags, ec); + std::size_t s = this->impl_.get_service().send_to( + this->impl_.get_implementation(), buffers, destination, flags, ec); asio::detail::throw_error(ec, "send_to"); return s; } @@ -461,7 +575,7 @@ public: const endpoint_type& destination, socket_base::message_flags flags, asio::error_code& ec) { - return this->get_service().send_to(this->get_implementation(), + return this->impl_.get_service().send_to(this->impl_.get_implementation(), buffers, destination, flags, ec); } @@ -486,9 +600,9 @@ public: * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * To send a single data buffer use the @ref buffer function as follows: @@ -509,12 +623,10 @@ public: const endpoint_type& destination, ASIO_MOVE_ARG(WriteHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WriteHandler. - ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; - - return this->get_service().async_send_to(this->get_implementation(), - buffers, destination, 0, ASIO_MOVE_CAST(WriteHandler)(handler)); + return async_initiate( + initiate_async_send_to(), handler, this, buffers, + destination, socket_base::message_flags(0)); } /// Start an asynchronous send. @@ -540,9 +652,9 @@ public: * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). */ template ASIO_INITFN_RESULT_TYPE(WriteHandler, @@ -551,13 +663,9 @@ public: const endpoint_type& destination, socket_base::message_flags flags, ASIO_MOVE_ARG(WriteHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WriteHandler. - ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; - - return this->get_service().async_send_to( - this->get_implementation(), buffers, destination, flags, - ASIO_MOVE_CAST(WriteHandler)(handler)); + return async_initiate( + initiate_async_send_to(), handler, this, buffers, destination, flags); } /// Receive some data on a connected socket. @@ -588,8 +696,8 @@ public: std::size_t receive(const MutableBufferSequence& buffers) { asio::error_code ec; - std::size_t s = this->get_service().receive( - this->get_implementation(), buffers, 0, ec); + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, 0, ec); asio::detail::throw_error(ec, "receive"); return s; } @@ -617,8 +725,8 @@ public: socket_base::message_flags flags) { asio::error_code ec; - std::size_t s = this->get_service().receive( - this->get_implementation(), buffers, flags, ec); + std::size_t s = this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); asio::detail::throw_error(ec, "receive"); return s; } @@ -645,8 +753,8 @@ public: std::size_t receive(const MutableBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { - return this->get_service().receive( - this->get_implementation(), buffers, flags, ec); + return this->impl_.get_service().receive( + this->impl_.get_implementation(), buffers, flags, ec); } /// Start an asynchronous receive on a connected socket. @@ -667,9 +775,9 @@ public: * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @note The async_receive operation can only be used with a connected socket. * Use the async_receive_from function to receive data on an unconnected @@ -691,12 +799,10 @@ public: async_receive(const MutableBufferSequence& buffers, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_receive(this->get_implementation(), - buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_receive(), handler, this, + buffers, socket_base::message_flags(0)); } /// Start an asynchronous receive on a connected socket. @@ -719,9 +825,9 @@ public: * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @note The async_receive operation can only be used with a connected socket. * Use the async_receive_from function to receive data on an unconnected @@ -734,12 +840,9 @@ public: socket_base::message_flags flags, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_receive(this->get_implementation(), - buffers, flags, ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_receive(), handler, this, buffers, flags); } /// Receive raw data with the endpoint of the sender. @@ -773,8 +876,8 @@ public: endpoint_type& sender_endpoint) { asio::error_code ec; - std::size_t s = this->get_service().receive_from( - this->get_implementation(), buffers, sender_endpoint, 0, ec); + std::size_t s = this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, 0, ec); asio::detail::throw_error(ec, "receive_from"); return s; } @@ -800,8 +903,8 @@ public: endpoint_type& sender_endpoint, socket_base::message_flags flags) { asio::error_code ec; - std::size_t s = this->get_service().receive_from( - this->get_implementation(), buffers, sender_endpoint, flags, ec); + std::size_t s = this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, flags, ec); asio::detail::throw_error(ec, "receive_from"); return s; } @@ -827,8 +930,8 @@ public: endpoint_type& sender_endpoint, socket_base::message_flags flags, asio::error_code& ec) { - return this->get_service().receive_from(this->get_implementation(), - buffers, sender_endpoint, flags, ec); + return this->impl_.get_service().receive_from( + this->impl_.get_implementation(), buffers, sender_endpoint, flags, ec); } /// Start an asynchronous receive. @@ -854,9 +957,9 @@ public: * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * To receive into a single data buffer use the @ref buffer function as @@ -874,13 +977,10 @@ public: endpoint_type& sender_endpoint, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_receive_from( - this->get_implementation(), buffers, sender_endpoint, 0, - ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_receive_from(), handler, this, buffers, + &sender_endpoint, socket_base::message_flags(0)); } /// Start an asynchronous receive. @@ -908,9 +1008,9 @@ public: * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). */ template ASIO_INITFN_RESULT_TYPE(ReadHandler, @@ -919,14 +1019,85 @@ public: endpoint_type& sender_endpoint, socket_base::message_flags flags, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_receive_from( - this->get_implementation(), buffers, sender_endpoint, flags, - ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_receive_from(), handler, + this, buffers, &sender_endpoint, flags); } + +private: + struct initiate_async_send + { + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + basic_raw_socket* self, const ConstBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_send( + self->impl_.get_implementation(), buffers, flags, + handler2.value, self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_send_to + { + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + basic_raw_socket* self, const ConstBufferSequence& buffers, + const endpoint_type& destination, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_send_to( + self->impl_.get_implementation(), buffers, destination, flags, + handler2.value, self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_receive + { + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + basic_raw_socket* self, const MutableBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_receive( + self->impl_.get_implementation(), buffers, flags, + handler2.value, self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_receive_from + { + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + basic_raw_socket* self, const MutableBufferSequence& buffers, + endpoint_type* sender_endpoint, socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_receive_from( + self->impl_.get_implementation(), buffers, *sender_endpoint, flags, + handler2.value, self->impl_.get_implementation_executor()); + } + }; }; } // namespace asio diff --git a/source/modules/hylia/link/asio/basic_seq_packet_socket.hpp b/source/modules/hylia/link/asio/basic_seq_packet_socket.hpp index 2271ceaf2..4ee1079bb 100644 --- a/source/modules/hylia/link/asio/basic_seq_packet_socket.hpp +++ b/source/modules/hylia/link/asio/basic_seq_packet_socket.hpp @@ -2,7 +2,7 @@ // basic_seq_packet_socket.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -21,12 +21,20 @@ #include "asio/detail/handler_type_requirements.hpp" #include "asio/detail/throw_error.hpp" #include "asio/error.hpp" -#include "asio/seq_packet_socket_service.hpp" #include "asio/detail/push_options.hpp" namespace asio { +#if !defined(ASIO_BASIC_SEQ_PACKET_SOCKET_FWD_DECL) +#define ASIO_BASIC_SEQ_PACKET_SOCKET_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_seq_packet_socket; + +#endif // !defined(ASIO_BASIC_SEQ_PACKET_SOCKET_FWD_DECL) + /// Provides sequenced packet socket functionality. /** * The basic_seq_packet_socket class template provides asynchronous and blocking @@ -36,15 +44,29 @@ namespace asio { * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ -template > +template class basic_seq_packet_socket - : public basic_socket + : public basic_socket { public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_seq_packet_socket other; + }; + /// The native representation of a socket. - typedef typename SeqPacketSocketService::native_handle_type - native_handle_type; +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef typename basic_socket::native_handle_type native_handle_type; +#endif /// The protocol type. typedef Protocol protocol_type; @@ -58,12 +80,30 @@ public: * socket needs to be opened and then connected or accepted before data can * be sent or received on it. * - * @param io_context The io_context object that the sequenced packet socket - * will use to dispatch handlers for any asynchronous operations performed on - * the socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. */ - explicit basic_seq_packet_socket(asio::io_context& io_context) - : basic_socket(io_context) + explicit basic_seq_packet_socket(const executor_type& ex) + : basic_socket(ex) + { + } + + /// Construct a basic_seq_packet_socket without opening it. + /** + * This constructor creates a sequenced packet socket without opening it. The + * socket needs to be opened and then connected or accepted before data can + * be sent or received on it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template + explicit basic_seq_packet_socket(ExecutionContext& context, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context) { } @@ -73,17 +113,40 @@ public: * needs to be connected or accepted before data can be sent or received on * it. * - * @param io_context The io_context object that the sequenced packet socket - * will use to dispatch handlers for any asynchronous operations performed on - * the socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * * @throws asio::system_error Thrown on failure. */ - basic_seq_packet_socket(asio::io_context& io_context, + basic_seq_packet_socket(const executor_type& ex, const protocol_type& protocol) - : basic_socket(io_context, protocol) + : basic_socket(ex, protocol) + { + } + + /// Construct and open a basic_seq_packet_socket. + /** + * This constructor creates and opens a sequenced_packet socket. The socket + * needs to be connected or accepted before data can be sent or received on + * it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_seq_packet_socket(ExecutionContext& context, + const protocol_type& protocol, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context, protocol) { } @@ -94,18 +157,43 @@ public: * it bound to the specified endpoint on the local machine. The protocol used * is the protocol associated with the given endpoint. * - * @param io_context The io_context object that the sequenced packet socket - * will use to dispatch handlers for any asynchronous operations performed on - * the socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. * * @param endpoint An endpoint on the local machine to which the sequenced * packet socket will be bound. * * @throws asio::system_error Thrown on failure. */ - basic_seq_packet_socket(asio::io_context& io_context, + basic_seq_packet_socket(const executor_type& ex, const endpoint_type& endpoint) - : basic_socket(io_context, endpoint) + : basic_socket(ex, endpoint) + { + } + + /// Construct a basic_seq_packet_socket, opening it and binding it to the + /// given local endpoint. + /** + * This constructor creates a sequenced packet socket and automatically opens + * it bound to the specified endpoint on the local machine. The protocol used + * is the protocol associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the sequenced + * packet socket will be bound. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_seq_packet_socket(ExecutionContext& context, + const endpoint_type& endpoint, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context, endpoint) { } @@ -114,9 +202,8 @@ public: * This constructor creates a sequenced packet socket object to hold an * existing native socket. * - * @param io_context The io_context object that the sequenced packet socket - * will use to dispatch handlers for any asynchronous operations performed on - * the socket. + * @param ex The I/O executor that the socket will use, by default, to + * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * @@ -124,10 +211,34 @@ public: * * @throws asio::system_error Thrown on failure. */ - basic_seq_packet_socket(asio::io_context& io_context, + basic_seq_packet_socket(const executor_type& ex, const protocol_type& protocol, const native_handle_type& native_socket) - : basic_socket( - io_context, protocol, native_socket) + : basic_socket(ex, protocol, native_socket) + { + } + + /// Construct a basic_seq_packet_socket on an existing native socket. + /** + * This constructor creates a sequenced packet socket object to hold an + * existing native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket The new underlying socket implementation. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_seq_packet_socket(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_socket, + typename enable_if< + is_convertible::value + >::type* = 0) + : basic_socket(context, protocol, native_socket) { } @@ -141,11 +252,11 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_seq_packet_socket(io_context&) constructor. + * constructed using the @c basic_seq_packet_socket(const executor_type&) + * constructor. */ basic_seq_packet_socket(basic_seq_packet_socket&& other) - : basic_socket( - ASIO_MOVE_CAST(basic_seq_packet_socket)(other)) + : basic_socket(std::move(other)) { } @@ -158,12 +269,12 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_seq_packet_socket(io_context&) constructor. + * constructed using the @c basic_seq_packet_socket(const executor_type&) + * constructor. */ basic_seq_packet_socket& operator=(basic_seq_packet_socket&& other) { - basic_socket::operator=( - ASIO_MOVE_CAST(basic_seq_packet_socket)(other)); + basic_socket::operator=(std::move(other)); return *this; } @@ -177,15 +288,16 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_seq_packet_socket(io_context&) constructor. + * constructed using the @c basic_seq_packet_socket(const executor_type&) + * constructor. */ - template - basic_seq_packet_socket( - basic_seq_packet_socket&& other, - typename enable_if::value>::type* = 0) - : basic_socket( - ASIO_MOVE_CAST2(basic_seq_packet_socket< - Protocol1, SeqPacketSocketService1>)(other)) + template + basic_seq_packet_socket(basic_seq_packet_socket&& other, + typename enable_if< + is_convertible::value + && is_convertible::value + >::type* = 0) + : basic_socket(std::move(other)) { } @@ -199,20 +311,30 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_seq_packet_socket(io_context&) constructor. + * constructed using the @c basic_seq_packet_socket(const executor_type&) + * constructor. */ - template - typename enable_if::value, - basic_seq_packet_socket>::type& operator=( - basic_seq_packet_socket&& other) + template + typename enable_if< + is_convertible::value + && is_convertible::value, + basic_seq_packet_socket& + >::type operator=(basic_seq_packet_socket&& other) { - basic_socket::operator=( - ASIO_MOVE_CAST2(basic_seq_packet_socket< - Protocol1, SeqPacketSocketService1>)(other)); + basic_socket::operator=(std::move(other)); return *this; } #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Destroys the socket. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ + ~basic_seq_packet_socket() + { + } + /// Send some data on the socket. /** * This function is used to send data on the sequenced packet socket. The @@ -241,8 +363,8 @@ public: socket_base::message_flags flags) { asio::error_code ec; - std::size_t s = this->get_service().send( - this->get_implementation(), buffers, flags, ec); + std::size_t s = this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); asio::detail::throw_error(ec, "send"); return s; } @@ -269,8 +391,8 @@ public: std::size_t send(const ConstBufferSequence& buffers, socket_base::message_flags flags, asio::error_code& ec) { - return this->get_service().send( - this->get_implementation(), buffers, flags, ec); + return this->impl_.get_service().send( + this->impl_.get_implementation(), buffers, flags, ec); } /// Start an asynchronous send. @@ -293,9 +415,9 @@ public: * std::size_t bytes_transferred // Number of bytes sent. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * To send a single data buffer use the @ref buffer function as follows: @@ -313,12 +435,9 @@ public: socket_base::message_flags flags, ASIO_MOVE_ARG(WriteHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WriteHandler. - ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; - - return this->get_service().async_send(this->get_implementation(), - buffers, flags, ASIO_MOVE_CAST(WriteHandler)(handler)); + return async_initiate( + initiate_async_send(), handler, this, buffers, flags); } /// Receive some data on the socket. @@ -355,8 +474,8 @@ public: socket_base::message_flags& out_flags) { asio::error_code ec; - std::size_t s = this->get_service().receive( - this->get_implementation(), buffers, 0, out_flags, ec); + std::size_t s = this->impl_.get_service().receive_with_flags( + this->impl_.get_implementation(), buffers, 0, out_flags, ec); asio::detail::throw_error(ec, "receive"); return s; } @@ -402,8 +521,8 @@ public: socket_base::message_flags& out_flags) { asio::error_code ec; - std::size_t s = this->get_service().receive( - this->get_implementation(), buffers, in_flags, out_flags, ec); + std::size_t s = this->impl_.get_service().receive_with_flags( + this->impl_.get_implementation(), buffers, in_flags, out_flags, ec); asio::detail::throw_error(ec, "receive"); return s; } @@ -436,8 +555,8 @@ public: socket_base::message_flags in_flags, socket_base::message_flags& out_flags, asio::error_code& ec) { - return this->get_service().receive(this->get_implementation(), - buffers, in_flags, out_flags, ec); + return this->impl_.get_service().receive_with_flags( + this->impl_.get_implementation(), buffers, in_flags, out_flags, ec); } /// Start an asynchronous receive. @@ -464,9 +583,9 @@ public: * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * To receive into a single data buffer use the @ref buffer function as @@ -485,13 +604,10 @@ public: socket_base::message_flags& out_flags, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_receive( - this->get_implementation(), buffers, 0, out_flags, - ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_receive_with_flags(), handler, this, + buffers, socket_base::message_flags(0), &out_flags); } /// Start an asynchronous receive. @@ -520,9 +636,9 @@ public: * std::size_t bytes_transferred // Number of bytes received. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * To receive into a single data buffer use the @ref buffer function as @@ -544,14 +660,49 @@ public: socket_base::message_flags& out_flags, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_receive( - this->get_implementation(), buffers, in_flags, out_flags, - ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_receive_with_flags(), handler, + this, buffers, in_flags, &out_flags); } + +private: + struct initiate_async_send + { + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + basic_seq_packet_socket* self, const ConstBufferSequence& buffers, + socket_base::message_flags flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_send( + self->impl_.get_implementation(), buffers, flags, + handler2.value, self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_receive_with_flags + { + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + basic_seq_packet_socket* self, const MutableBufferSequence& buffers, + socket_base::message_flags in_flags, + socket_base::message_flags* out_flags) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_receive_with_flags( + self->impl_.get_implementation(), buffers, in_flags, *out_flags, + handler2.value, self->impl_.get_implementation_executor()); + } + }; }; } // namespace asio diff --git a/source/modules/hylia/link/asio/basic_serial_port.hpp b/source/modules/hylia/link/asio/basic_serial_port.hpp index e47211778..b5925ee27 100644 --- a/source/modules/hylia/link/asio/basic_serial_port.hpp +++ b/source/modules/hylia/link/asio/basic_serial_port.hpp @@ -2,7 +2,7 @@ // basic_serial_port.hpp // ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -22,12 +22,25 @@ || defined(GENERATING_DOCUMENTATION) #include -#include "asio/basic_io_object.hpp" +#include "asio/async_result.hpp" #include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" #include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" #include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/executor.hpp" #include "asio/serial_port_base.hpp" -#include "asio/serial_port_service.hpp" +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_serial_port_service.hpp" +#else +# include "asio/detail/reactive_serial_port_service.hpp" +#endif + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) #include "asio/detail/push_options.hpp" @@ -35,35 +48,84 @@ namespace asio { /// Provides serial port functionality. /** - * The basic_serial_port class template provides functionality that is common - * to all serial ports. + * The basic_serial_port class provides a wrapper over serial port + * functionality. * * @par Thread Safety * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ -template +template class basic_serial_port - : public basic_io_object, - public serial_port_base + : public serial_port_base { public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + /// The native representation of a serial port. - typedef typename SerialPortService::native_handle_type native_handle_type; +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#elif defined(ASIO_HAS_IOCP) + typedef detail::win_iocp_serial_port_service::native_handle_type + native_handle_type; +#else + typedef detail::reactive_serial_port_service::native_handle_type + native_handle_type; +#endif + + /// A basic_basic_serial_port is always the lowest layer. + typedef basic_serial_port lowest_layer_type; - /// A basic_serial_port is always the lowest layer. - typedef basic_serial_port lowest_layer_type; + /// Construct a basic_serial_port without opening it. + /** + * This constructor creates a serial port without opening it. + * + * @param ex The I/O executor that the serial port will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * serial port. + */ + explicit basic_serial_port(const executor_type& ex) + : impl_(ex) + { + } /// Construct a basic_serial_port without opening it. /** * This constructor creates a serial port without opening it. * - * @param io_context The io_context object that the serial port will use to - * dispatch handlers for any asynchronous operations performed on the port. + * @param context An execution context which provides the I/O executor that + * the serial port will use, by default, to dispatch handlers for any + * asynchronous operations performed on the serial port. + */ + template + explicit basic_serial_port(ExecutionContext& context, + typename enable_if< + is_convertible::value, + basic_serial_port + >::type* = 0) + : impl_(context) + { + } + + /// Construct and open a basic_serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param ex The I/O executor that the serial port will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * serial port. + * + * @param device The platform-specific device name for this serial + * port. */ - explicit basic_serial_port(asio::io_context& io_context) - : basic_io_object(io_context) + basic_serial_port(const executor_type& ex, const char* device) + : impl_(ex) { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); } /// Construct and open a basic_serial_port. @@ -71,18 +133,22 @@ public: * This constructor creates and opens a serial port for the specified device * name. * - * @param io_context The io_context object that the serial port will use to - * dispatch handlers for any asynchronous operations performed on the port. + * @param context An execution context which provides the I/O executor that + * the serial port will use, by default, to dispatch handlers for any + * asynchronous operations performed on the serial port. * * @param device The platform-specific device name for this serial * port. */ - explicit basic_serial_port(asio::io_context& io_context, - const char* device) - : basic_io_object(io_context) + template + basic_serial_port(ExecutionContext& context, const char* device, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) { asio::error_code ec; - this->get_service().open(this->get_implementation(), device, ec); + impl_.get_service().open(impl_.get_implementation(), device, ec); asio::detail::throw_error(ec, "open"); } @@ -91,18 +157,42 @@ public: * This constructor creates and opens a serial port for the specified device * name. * - * @param io_context The io_context object that the serial port will use to - * dispatch handlers for any asynchronous operations performed on the port. + * @param ex The I/O executor that the serial port will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * serial port. * * @param device The platform-specific device name for this serial * port. */ - explicit basic_serial_port(asio::io_context& io_context, - const std::string& device) - : basic_io_object(io_context) + basic_serial_port(const executor_type& ex, const std::string& device) + : impl_(ex) { asio::error_code ec; - this->get_service().open(this->get_implementation(), device, ec); + impl_.get_service().open(impl_.get_implementation(), device, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct and open a basic_serial_port. + /** + * This constructor creates and opens a serial port for the specified device + * name. + * + * @param context An execution context which provides the I/O executor that + * the serial port will use, by default, to dispatch handlers for any + * asynchronous operations performed on the serial port. + * + * @param device The platform-specific device name for this serial + * port. + */ + template + basic_serial_port(ExecutionContext& context, const std::string& device, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), device, ec); asio::detail::throw_error(ec, "open"); } @@ -111,19 +201,47 @@ public: * This constructor creates a serial port object to hold an existing native * serial port. * - * @param io_context The io_context object that the serial port will use to - * dispatch handlers for any asynchronous operations performed on the port. + * @param ex The I/O executor that the serial port will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * serial port. * * @param native_serial_port A native serial port. * * @throws asio::system_error Thrown on failure. */ - basic_serial_port(asio::io_context& io_context, + basic_serial_port(const executor_type& ex, const native_handle_type& native_serial_port) - : basic_io_object(io_context) + : impl_(ex) { asio::error_code ec; - this->get_service().assign(this->get_implementation(), + impl_.get_service().assign(impl_.get_implementation(), + native_serial_port, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Construct a basic_serial_port on an existing native serial port. + /** + * This constructor creates a serial port object to hold an existing native + * serial port. + * + * @param context An execution context which provides the I/O executor that + * the serial port will use, by default, to dispatch handlers for any + * asynchronous operations performed on the serial port. + * + * @param native_serial_port A native serial port. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_serial_port(ExecutionContext& context, + const native_handle_type& native_serial_port, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), native_serial_port, ec); asio::detail::throw_error(ec, "assign"); } @@ -137,11 +255,11 @@ public: * occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_serial_port(io_context&) constructor. + * constructed using the @c basic_serial_port(const executor_type&) + * constructor. */ basic_serial_port(basic_serial_port&& other) - : basic_io_object( - ASIO_MOVE_CAST(basic_serial_port)(other)) + : impl_(std::move(other.impl_)) { } @@ -153,16 +271,32 @@ public: * occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_serial_port(io_context&) constructor. + * constructed using the @c basic_serial_port(const executor_type&) + * constructor. */ basic_serial_port& operator=(basic_serial_port&& other) { - basic_io_object::operator=( - ASIO_MOVE_CAST(basic_serial_port)(other)); + impl_ = std::move(other.impl_); return *this; } #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Destroys the serial port. + /** + * This function destroys the serial port, cancelling any outstanding + * asynchronous wait operations associated with the serial port as if by + * calling @c cancel. + */ + ~basic_serial_port() + { + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + /// Get a reference to the lowest layer. /** * This function returns a reference to the lowest layer in a stack of @@ -202,7 +336,7 @@ public: void open(const std::string& device) { asio::error_code ec; - this->get_service().open(this->get_implementation(), device, ec); + impl_.get_service().open(impl_.get_implementation(), device, ec); asio::detail::throw_error(ec, "open"); } @@ -215,10 +349,11 @@ public: * * @param ec Set the indicate what error occurred, if any. */ - asio::error_code open(const std::string& device, + ASIO_SYNC_OP_VOID open(const std::string& device, asio::error_code& ec) { - return this->get_service().open(this->get_implementation(), device, ec); + impl_.get_service().open(impl_.get_implementation(), device, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Assign an existing native serial port to the serial port. @@ -232,7 +367,7 @@ public: void assign(const native_handle_type& native_serial_port) { asio::error_code ec; - this->get_service().assign(this->get_implementation(), + impl_.get_service().assign(impl_.get_implementation(), native_serial_port, ec); asio::detail::throw_error(ec, "assign"); } @@ -245,17 +380,18 @@ public: * * @param ec Set to indicate what error occurred, if any. */ - asio::error_code assign(const native_handle_type& native_serial_port, + ASIO_SYNC_OP_VOID assign(const native_handle_type& native_serial_port, asio::error_code& ec) { - return this->get_service().assign(this->get_implementation(), + impl_.get_service().assign(impl_.get_implementation(), native_serial_port, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Determine whether the serial port is open. bool is_open() const { - return this->get_service().is_open(this->get_implementation()); + return impl_.get_service().is_open(impl_.get_implementation()); } /// Close the serial port. @@ -269,7 +405,7 @@ public: void close() { asio::error_code ec; - this->get_service().close(this->get_implementation(), ec); + impl_.get_service().close(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "close"); } @@ -281,9 +417,10 @@ public: * * @param ec Set to indicate what error occurred, if any. */ - asio::error_code close(asio::error_code& ec) + ASIO_SYNC_OP_VOID close(asio::error_code& ec) { - return this->get_service().close(this->get_implementation(), ec); + impl_.get_service().close(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Get the native serial port representation. @@ -294,7 +431,7 @@ public: */ native_handle_type native_handle() { - return this->get_service().native_handle(this->get_implementation()); + return impl_.get_service().native_handle(impl_.get_implementation()); } /// Cancel all asynchronous operations associated with the serial port. @@ -308,7 +445,7 @@ public: void cancel() { asio::error_code ec; - this->get_service().cancel(this->get_implementation(), ec); + impl_.get_service().cancel(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "cancel"); } @@ -320,9 +457,10 @@ public: * * @param ec Set to indicate what error occurred, if any. */ - asio::error_code cancel(asio::error_code& ec) + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) { - return this->get_service().cancel(this->get_implementation(), ec); + impl_.get_service().cancel(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Send a break sequence to the serial port. @@ -335,7 +473,7 @@ public: void send_break() { asio::error_code ec; - this->get_service().send_break(this->get_implementation(), ec); + impl_.get_service().send_break(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "send_break"); } @@ -346,9 +484,10 @@ public: * * @param ec Set to indicate what error occurred, if any. */ - asio::error_code send_break(asio::error_code& ec) + ASIO_SYNC_OP_VOID send_break(asio::error_code& ec) { - return this->get_service().send_break(this->get_implementation(), ec); + impl_.get_service().send_break(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Set an option on the serial port. @@ -370,7 +509,7 @@ public: void set_option(const SettableSerialPortOption& option) { asio::error_code ec; - this->get_service().set_option(this->get_implementation(), option, ec); + impl_.get_service().set_option(impl_.get_implementation(), option, ec); asio::detail::throw_error(ec, "set_option"); } @@ -390,11 +529,11 @@ public: * asio::serial_port_base::character_size */ template - asio::error_code set_option(const SettableSerialPortOption& option, + ASIO_SYNC_OP_VOID set_option(const SettableSerialPortOption& option, asio::error_code& ec) { - return this->get_service().set_option( - this->get_implementation(), option, ec); + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Get an option from the serial port. @@ -417,7 +556,7 @@ public: void get_option(GettableSerialPortOption& option) { asio::error_code ec; - this->get_service().get_option(this->get_implementation(), option, ec); + impl_.get_service().get_option(impl_.get_implementation(), option, ec); asio::detail::throw_error(ec, "get_option"); } @@ -438,11 +577,11 @@ public: * asio::serial_port_base::character_size */ template - asio::error_code get_option(GettableSerialPortOption& option, + ASIO_SYNC_OP_VOID get_option(GettableSerialPortOption& option, asio::error_code& ec) { - return this->get_service().get_option( - this->get_implementation(), option, ec); + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Write some data to the serial port. @@ -466,7 +605,7 @@ public: * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code - * serial_port.write_some(asio::buffer(data, size)); + * basic_serial_port.write_some(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or @@ -476,8 +615,8 @@ public: std::size_t write_some(const ConstBufferSequence& buffers) { asio::error_code ec; - std::size_t s = this->get_service().write_some( - this->get_implementation(), buffers, ec); + std::size_t s = impl_.get_service().write_some( + impl_.get_implementation(), buffers, ec); asio::detail::throw_error(ec, "write_some"); return s; } @@ -502,8 +641,8 @@ public: std::size_t write_some(const ConstBufferSequence& buffers, asio::error_code& ec) { - return this->get_service().write_some( - this->get_implementation(), buffers, ec); + return impl_.get_service().write_some( + impl_.get_implementation(), buffers, ec); } /// Start an asynchronous write. @@ -524,9 +663,9 @@ public: * std::size_t bytes_transferred // Number of bytes written. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @note The write operation may not transmit all of the data to the peer. * Consider using the @ref async_write function if you need to ensure that all @@ -535,7 +674,8 @@ public: * @par Example * To write a single data buffer use the @ref buffer function as follows: * @code - * serial_port.async_write_some(asio::buffer(data, size), handler); + * basic_serial_port.async_write_some( + * asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on writing multiple * buffers in one go, and how to use it with arrays, boost::array or @@ -547,12 +687,9 @@ public: async_write_some(const ConstBufferSequence& buffers, ASIO_MOVE_ARG(WriteHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WriteHandler. - ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; - - return this->get_service().async_write_some(this->get_implementation(), - buffers, ASIO_MOVE_CAST(WriteHandler)(handler)); + return async_initiate( + initiate_async_write_some(), handler, this, buffers); } /// Read some data from the serial port. @@ -577,7 +714,7 @@ public: * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code - * serial_port.read_some(asio::buffer(data, size)); + * basic_serial_port.read_some(asio::buffer(data, size)); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or @@ -587,8 +724,8 @@ public: std::size_t read_some(const MutableBufferSequence& buffers) { asio::error_code ec; - std::size_t s = this->get_service().read_some( - this->get_implementation(), buffers, ec); + std::size_t s = impl_.get_service().read_some( + impl_.get_implementation(), buffers, ec); asio::detail::throw_error(ec, "read_some"); return s; } @@ -614,8 +751,8 @@ public: std::size_t read_some(const MutableBufferSequence& buffers, asio::error_code& ec) { - return this->get_service().read_some( - this->get_implementation(), buffers, ec); + return impl_.get_service().read_some( + impl_.get_implementation(), buffers, ec); } /// Start an asynchronous read. @@ -636,9 +773,9 @@ public: * std::size_t bytes_transferred // Number of bytes read. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @note The read operation may not read all of the requested number of bytes. * Consider using the @ref async_read function if you need to ensure that the @@ -648,7 +785,8 @@ public: * @par Example * To read into a single data buffer use the @ref buffer function as follows: * @code - * serial_port.async_read_some(asio::buffer(data, size), handler); + * basic_serial_port.async_read_some( + * asio::buffer(data, size), handler); * @endcode * See the @ref buffer documentation for information on reading into multiple * buffers in one go, and how to use it with arrays, boost::array or @@ -660,13 +798,55 @@ public: async_read_some(const MutableBufferSequence& buffers, ASIO_MOVE_ARG(ReadHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ReadHandler. - ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; - - return this->get_service().async_read_some(this->get_implementation(), - buffers, ASIO_MOVE_CAST(ReadHandler)(handler)); + return async_initiate( + initiate_async_read_some(), handler, this, buffers); } + +private: + // Disallow copying and assignment. + basic_serial_port(const basic_serial_port&) ASIO_DELETED; + basic_serial_port& operator=(const basic_serial_port&) ASIO_DELETED; + + struct initiate_async_write_some + { + template + void operator()(ASIO_MOVE_ARG(WriteHandler) handler, + basic_serial_port* self, const ConstBufferSequence& buffers) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WriteHandler. + ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_write_some( + self->impl_.get_implementation(), buffers, handler2.value, + self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_read_some + { + template + void operator()(ASIO_MOVE_ARG(ReadHandler) handler, + basic_serial_port* self, const MutableBufferSequence& buffers) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ReadHandler. + ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_read_some( + self->impl_.get_implementation(), buffers, handler2.value, + self->impl_.get_implementation_executor()); + } + }; + +#if defined(ASIO_HAS_IOCP) + detail::io_object_impl impl_; +#else + detail::io_object_impl impl_; +#endif }; } // namespace asio diff --git a/source/modules/hylia/link/asio/basic_signal_set.hpp b/source/modules/hylia/link/asio/basic_signal_set.hpp index 642d10a00..f044e5750 100644 --- a/source/modules/hylia/link/asio/basic_signal_set.hpp +++ b/source/modules/hylia/link/asio/basic_signal_set.hpp @@ -2,7 +2,7 @@ // basic_signal_set.hpp // ~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,22 +17,23 @@ #include "asio/detail/config.hpp" -#include "asio/basic_io_object.hpp" +#include "asio/async_result.hpp" #include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" +#include "asio/detail/signal_set_service.hpp" #include "asio/detail/throw_error.hpp" +#include "asio/detail/type_traits.hpp" #include "asio/error.hpp" -#include "asio/signal_set_service.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/execution_context.hpp" +#include "asio/executor.hpp" namespace asio { /// Provides signal functionality. /** - * The basic_signal_set class template provides the ability to perform an - * asynchronous wait for one or more signals to occur. - * - * Most applications will use the asio::signal_set typedef. + * The basic_signal_set class provides the ability to perform an asynchronous + * wait for one or more signals to occur. * * @par Thread Safety * @e Distinct @e objects: Safe.@n @@ -54,7 +55,7 @@ namespace asio { * ... * * // Construct a signal set registered for process termination. - * asio::signal_set signals(io_context, SIGINT, SIGTERM); + * asio::signal_set signals(my_context, SIGINT, SIGTERM); * * // Start an asynchronous wait for one of the signals to occur. * signals.async_wait(handler); @@ -89,41 +90,88 @@ namespace asio { * that any signals registered using signal_set objects are unblocked in at * least one thread. */ -template +template class basic_signal_set - : public basic_io_object { public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Construct a signal set without adding any signals. + /** + * This constructor creates a signal set without registering for any signals. + * + * @param ex The I/O executor that the signal set will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * signal set. + */ + explicit basic_signal_set(const executor_type& ex) + : impl_(ex) + { + } + /// Construct a signal set without adding any signals. /** * This constructor creates a signal set without registering for any signals. * - * @param io_context The io_context object that the signal set will use to - * dispatch handlers for any asynchronous operations performed on the set. + * @param context An execution context which provides the I/O executor that + * the signal set will use, by default, to dispatch handlers for any + * asynchronous operations performed on the signal set. + */ + template + explicit basic_signal_set(ExecutionContext& context, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + } + + /// Construct a signal set and add one signal. + /** + * This constructor creates a signal set and registers for one signal. + * + * @param ex The I/O executor that the signal set will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * signal set. + * + * @param signal_number_1 The signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(ex); + * signals.add(signal_number_1); @endcode */ - explicit basic_signal_set(asio::io_context& io_context) - : basic_io_object(io_context) + basic_signal_set(const executor_type& ex, int signal_number_1) + : impl_(ex) { + asio::error_code ec; + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); } /// Construct a signal set and add one signal. /** * This constructor creates a signal set and registers for one signal. * - * @param io_context The io_context object that the signal set will use to - * dispatch handlers for any asynchronous operations performed on the set. + * @param context An execution context which provides the I/O executor that + * the signal set will use, by default, to dispatch handlers for any + * asynchronous operations performed on the signal set. * * @param signal_number_1 The signal number to be added. * * @note This constructor is equivalent to performing: - * @code asio::signal_set signals(io_context); + * @code asio::signal_set signals(context); * signals.add(signal_number_1); @endcode */ - basic_signal_set(asio::io_context& io_context, int signal_number_1) - : basic_io_object(io_context) + template + basic_signal_set(ExecutionContext& context, int signal_number_1, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) { asio::error_code ec; - this->get_service().add(this->get_implementation(), signal_number_1, ec); + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); asio::detail::throw_error(ec, "add"); } @@ -131,26 +179,59 @@ public: /** * This constructor creates a signal set and registers for two signals. * - * @param io_context The io_context object that the signal set will use to - * dispatch handlers for any asynchronous operations performed on the set. + * @param ex The I/O executor that the signal set will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * signal set. * * @param signal_number_1 The first signal number to be added. * * @param signal_number_2 The second signal number to be added. * * @note This constructor is equivalent to performing: - * @code asio::signal_set signals(io_context); + * @code asio::signal_set signals(ex); * signals.add(signal_number_1); * signals.add(signal_number_2); @endcode */ - basic_signal_set(asio::io_context& io_context, int signal_number_1, + basic_signal_set(const executor_type& ex, int signal_number_1, int signal_number_2) - : basic_io_object(io_context) + : impl_(ex) { asio::error_code ec; - this->get_service().add(this->get_implementation(), signal_number_1, ec); + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); asio::detail::throw_error(ec, "add"); - this->get_service().add(this->get_implementation(), signal_number_2, ec); + impl_.get_service().add(impl_.get_implementation(), signal_number_2, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Construct a signal set and add two signals. + /** + * This constructor creates a signal set and registers for two signals. + * + * @param context An execution context which provides the I/O executor that + * the signal set will use, by default, to dispatch handlers for any + * asynchronous operations performed on the signal set. + * + * @param signal_number_1 The first signal number to be added. + * + * @param signal_number_2 The second signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(context); + * signals.add(signal_number_1); + * signals.add(signal_number_2); @endcode + */ + template + basic_signal_set(ExecutionContext& context, int signal_number_1, + int signal_number_2, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + asio::error_code ec; + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + impl_.get_service().add(impl_.get_implementation(), signal_number_2, ec); asio::detail::throw_error(ec, "add"); } @@ -158,8 +239,9 @@ public: /** * This constructor creates a signal set and registers for three signals. * - * @param io_context The io_context object that the signal set will use to - * dispatch handlers for any asynchronous operations performed on the set. + * @param ex The I/O executor that the signal set will use, by default, to + * dispatch handlers for any asynchronous operations performed on the + * signal set. * * @param signal_number_1 The first signal number to be added. * @@ -168,24 +250,77 @@ public: * @param signal_number_3 The third signal number to be added. * * @note This constructor is equivalent to performing: - * @code asio::signal_set signals(io_context); + * @code asio::signal_set signals(ex); * signals.add(signal_number_1); * signals.add(signal_number_2); * signals.add(signal_number_3); @endcode */ - basic_signal_set(asio::io_context& io_context, int signal_number_1, + basic_signal_set(const executor_type& ex, int signal_number_1, int signal_number_2, int signal_number_3) - : basic_io_object(io_context) + : impl_(ex) { asio::error_code ec; - this->get_service().add(this->get_implementation(), signal_number_1, ec); + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); asio::detail::throw_error(ec, "add"); - this->get_service().add(this->get_implementation(), signal_number_2, ec); + impl_.get_service().add(impl_.get_implementation(), signal_number_2, ec); asio::detail::throw_error(ec, "add"); - this->get_service().add(this->get_implementation(), signal_number_3, ec); + impl_.get_service().add(impl_.get_implementation(), signal_number_3, ec); asio::detail::throw_error(ec, "add"); } + /// Construct a signal set and add three signals. + /** + * This constructor creates a signal set and registers for three signals. + * + * @param context An execution context which provides the I/O executor that + * the signal set will use, by default, to dispatch handlers for any + * asynchronous operations performed on the signal set. + * + * @param signal_number_1 The first signal number to be added. + * + * @param signal_number_2 The second signal number to be added. + * + * @param signal_number_3 The third signal number to be added. + * + * @note This constructor is equivalent to performing: + * @code asio::signal_set signals(context); + * signals.add(signal_number_1); + * signals.add(signal_number_2); + * signals.add(signal_number_3); @endcode + */ + template + basic_signal_set(ExecutionContext& context, int signal_number_1, + int signal_number_2, int signal_number_3, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + asio::error_code ec; + impl_.get_service().add(impl_.get_implementation(), signal_number_1, ec); + asio::detail::throw_error(ec, "add"); + impl_.get_service().add(impl_.get_implementation(), signal_number_2, ec); + asio::detail::throw_error(ec, "add"); + impl_.get_service().add(impl_.get_implementation(), signal_number_3, ec); + asio::detail::throw_error(ec, "add"); + } + + /// Destroys the signal set. + /** + * This function destroys the signal set, cancelling any outstanding + * asynchronous wait operations associated with the signal set as if by + * calling @c cancel. + */ + ~basic_signal_set() + { + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + /// Add a signal to a signal_set. /** * This function adds the specified signal to the set. It has no effect if the @@ -198,7 +333,7 @@ public: void add(int signal_number) { asio::error_code ec; - this->get_service().add(this->get_implementation(), signal_number, ec); + impl_.get_service().add(impl_.get_implementation(), signal_number, ec); asio::detail::throw_error(ec, "add"); } @@ -211,11 +346,11 @@ public: * * @param ec Set to indicate what error occurred, if any. */ - asio::error_code add(int signal_number, + ASIO_SYNC_OP_VOID add(int signal_number, asio::error_code& ec) { - return this->get_service().add( - this->get_implementation(), signal_number, ec); + impl_.get_service().add(impl_.get_implementation(), signal_number, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Remove a signal from a signal_set. @@ -233,7 +368,7 @@ public: void remove(int signal_number) { asio::error_code ec; - this->get_service().remove(this->get_implementation(), signal_number, ec); + impl_.get_service().remove(impl_.get_implementation(), signal_number, ec); asio::detail::throw_error(ec, "remove"); } @@ -249,11 +384,11 @@ public: * @note Removes any notifications that have been queued for the specified * signal number. */ - asio::error_code remove(int signal_number, + ASIO_SYNC_OP_VOID remove(int signal_number, asio::error_code& ec) { - return this->get_service().remove( - this->get_implementation(), signal_number, ec); + impl_.get_service().remove(impl_.get_implementation(), signal_number, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Remove all signals from a signal_set. @@ -268,7 +403,7 @@ public: void clear() { asio::error_code ec; - this->get_service().clear(this->get_implementation(), ec); + impl_.get_service().clear(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "clear"); } @@ -281,9 +416,10 @@ public: * * @note Removes all queued notifications. */ - asio::error_code clear(asio::error_code& ec) + ASIO_SYNC_OP_VOID clear(asio::error_code& ec) { - return this->get_service().clear(this->get_implementation(), ec); + impl_.get_service().clear(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Cancel all operations associated with the signal set. @@ -310,7 +446,7 @@ public: void cancel() { asio::error_code ec; - this->get_service().cancel(this->get_implementation(), ec); + impl_.get_service().cancel(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "cancel"); } @@ -335,9 +471,10 @@ public: * These handlers can no longer be cancelled, and therefore are passed an * error code that indicates the successful completion of the wait operation. */ - asio::error_code cancel(asio::error_code& ec) + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) { - return this->get_service().cancel(this->get_implementation(), ec); + impl_.get_service().cancel(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Start an asynchronous operation to wait for a signal to be delivered. @@ -361,26 +498,44 @@ public: * int signal_number // Indicates which signal occurred. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). */ template ASIO_INITFN_RESULT_TYPE(SignalHandler, void (asio::error_code, int)) async_wait(ASIO_MOVE_ARG(SignalHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a SignalHandler. - ASIO_SIGNAL_HANDLER_CHECK(SignalHandler, handler) type_check; - - return this->get_service().async_wait(this->get_implementation(), - ASIO_MOVE_CAST(SignalHandler)(handler)); + return async_initiate( + initiate_async_wait(), handler, this); } + +private: + // Disallow copying and assignment. + basic_signal_set(const basic_signal_set&) ASIO_DELETED; + basic_signal_set& operator=(const basic_signal_set&) ASIO_DELETED; + + struct initiate_async_wait + { + template + void operator()(ASIO_MOVE_ARG(SignalHandler) handler, + basic_signal_set* self) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a SignalHandler. + ASIO_SIGNAL_HANDLER_CHECK(SignalHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_wait( + self->impl_.get_implementation(), handler2.value, + self->impl_.get_implementation_executor()); + } + }; + + detail::io_object_impl impl_; }; } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_BASIC_SIGNAL_SET_HPP diff --git a/source/modules/hylia/link/asio/basic_socket.hpp b/source/modules/hylia/link/asio/basic_socket.hpp index 582982a9b..42efbda43 100644 --- a/source/modules/hylia/link/asio/basic_socket.hpp +++ b/source/modules/hylia/link/asio/basic_socket.hpp @@ -2,7 +2,7 @@ // basic_socket.hpp // ~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,18 +17,42 @@ #include "asio/detail/config.hpp" #include "asio/async_result.hpp" -#include "asio/basic_io_object.hpp" #include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/type_traits.hpp" #include "asio/error.hpp" +#include "asio/execution_context.hpp" +#include "asio/executor.hpp" #include "asio/post.hpp" #include "asio/socket_base.hpp" +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_socket_service.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +#else +# include "asio/detail/reactive_socket_service.hpp" +#endif + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + #include "asio/detail/push_options.hpp" namespace asio { +#if !defined(ASIO_BASIC_SOCKET_FWD_DECL) +#define ASIO_BASIC_SOCKET_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_socket; + +#endif // !defined(ASIO_BASIC_SOCKET_FWD_DECL) + /// Provides socket functionality. /** * The basic_socket class template provides functionality that is common to both @@ -38,14 +62,35 @@ namespace asio { * @e Distinct @e objects: Safe.@n * @e Shared @e objects: Unsafe. */ -template +template class basic_socket - : public basic_io_object, - public socket_base + : public socket_base { public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + + /// Rebinds the socket type to another executor. + template + struct rebind_executor + { + /// The socket type when rebound to the specified executor. + typedef basic_socket other; + }; + /// The native representation of a socket. - typedef typename SocketService::native_handle_type native_handle_type; +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#elif defined(ASIO_WINDOWS_RUNTIME) + typedef typename detail::null_socket_service< + Protocol>::native_handle_type native_handle_type; +#elif defined(ASIO_HAS_IOCP) + typedef typename detail::win_iocp_socket_service< + Protocol>::native_handle_type native_handle_type; +#else + typedef typename detail::reactive_socket_service< + Protocol>::native_handle_type native_handle_type; +#endif /// The protocol type. typedef Protocol protocol_type; @@ -53,18 +98,37 @@ public: /// The endpoint type. typedef typename Protocol::endpoint endpoint_type; +#if !defined(ASIO_NO_EXTENSIONS) /// A basic_socket is always the lowest layer. - typedef basic_socket lowest_layer_type; + typedef basic_socket lowest_layer_type; +#endif // !defined(ASIO_NO_EXTENSIONS) /// Construct a basic_socket without opening it. /** * This constructor creates a socket without opening it. * - * @param io_context The io_context object that the socket will use to + * @param ex The I/O executor that the socket will use, by default, to * dispatch handlers for any asynchronous operations performed on the socket. */ - explicit basic_socket(asio::io_context& io_context) - : basic_io_object(io_context) + explicit basic_socket(const executor_type& ex) + : impl_(ex) + { + } + + /// Construct a basic_socket without opening it. + /** + * This constructor creates a socket without opening it. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + */ + template + explicit basic_socket(ExecutionContext& context, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) { } @@ -72,19 +136,42 @@ public: /** * This constructor creates and opens a socket. * - * @param io_context The io_context object that the socket will use to + * @param ex The I/O executor that the socket will use, by default, to * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. * * @throws asio::system_error Thrown on failure. */ - basic_socket(asio::io_context& io_context, - const protocol_type& protocol) - : basic_io_object(io_context) + basic_socket(const executor_type& ex, const protocol_type& protocol) + : impl_(ex) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct and open a basic_socket. + /** + * This constructor creates and opens a socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_socket(ExecutionContext& context, const protocol_type& protocol, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) { asio::error_code ec; - this->get_service().open(this->get_implementation(), protocol, ec); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); asio::detail::throw_error(ec, "open"); } @@ -95,7 +182,7 @@ public: * specified endpoint on the local machine. The protocol used is the protocol * associated with the given endpoint. * - * @param io_context The io_context object that the socket will use to + * @param ex The I/O executor that the socket will use, by default, to * dispatch handlers for any asynchronous operations performed on the socket. * * @param endpoint An endpoint on the local machine to which the socket will @@ -103,15 +190,45 @@ public: * * @throws asio::system_error Thrown on failure. */ - basic_socket(asio::io_context& io_context, - const endpoint_type& endpoint) - : basic_io_object(io_context) + basic_socket(const executor_type& ex, const endpoint_type& endpoint) + : impl_(ex) { asio::error_code ec; const protocol_type protocol = endpoint.protocol(); - this->get_service().open(this->get_implementation(), protocol, ec); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); asio::detail::throw_error(ec, "open"); - this->get_service().bind(this->get_implementation(), endpoint, ec); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + } + + /// Construct a basic_socket, opening it and binding it to the given local + /// endpoint. + /** + * This constructor creates a socket and automatically opens it bound to the + * specified endpoint on the local machine. The protocol used is the protocol + * associated with the given endpoint. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param endpoint An endpoint on the local machine to which the socket will + * be bound. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_socket(ExecutionContext& context, const endpoint_type& endpoint, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + asio::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); asio::detail::throw_error(ec, "bind"); } @@ -119,7 +236,7 @@ public: /** * This constructor creates a socket object to hold an existing native socket. * - * @param io_context The io_context object that the socket will use to + * @param ex The I/O executor that the socket will use, by default, to * dispatch handlers for any asynchronous operations performed on the socket. * * @param protocol An object specifying protocol parameters to be used. @@ -128,12 +245,40 @@ public: * * @throws asio::system_error Thrown on failure. */ - basic_socket(asio::io_context& io_context, - const protocol_type& protocol, const native_handle_type& native_socket) - : basic_io_object(io_context) + basic_socket(const executor_type& ex, const protocol_type& protocol, + const native_handle_type& native_socket) + : impl_(ex) { asio::error_code ec; - this->get_service().assign(this->get_implementation(), + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_socket, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Construct a basic_socket on an existing native socket. + /** + * This constructor creates a socket object to hold an existing native socket. + * + * @param context An execution context which provides the I/O executor that + * the socket will use, by default, to dispatch handlers for any asynchronous + * operations performed on the socket. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_socket A native socket. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_socket(ExecutionContext& context, const protocol_type& protocol, + const native_handle_type& native_socket, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), protocol, native_socket, ec); asio::detail::throw_error(ec, "assign"); } @@ -147,11 +292,10 @@ public: * occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_socket(io_context&) constructor. + * constructed using the @c basic_socket(const executor_type&) constructor. */ basic_socket(basic_socket&& other) - : basic_io_object( - ASIO_MOVE_CAST(basic_socket)(other)) + : impl_(std::move(other.impl_)) { } @@ -163,17 +307,16 @@ public: * occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_socket(io_context&) constructor. + * constructed using the @c basic_socket(const executor_type&) constructor. */ basic_socket& operator=(basic_socket&& other) { - basic_io_object::operator=( - ASIO_MOVE_CAST(basic_socket)(other)); + impl_ = std::move(other.impl_); return *this; } // All sockets have access to each other's implementations. - template + template friend class basic_socket; /// Move-construct a basic_socket from a socket of another protocol type. @@ -184,15 +327,16 @@ public: * occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_socket(io_context&) constructor. + * constructed using the @c basic_socket(const executor_type&) constructor. */ - template - basic_socket(basic_socket&& other, - typename enable_if::value>::type* = 0) - : basic_io_object(other.get_service().get_io_context()) + template + basic_socket(basic_socket&& other, + typename enable_if< + is_convertible::value + && is_convertible::value + >::type* = 0) + : impl_(std::move(other.impl_)) { - this->get_service().template converting_move_construct( - this->get_implementation(), other.get_implementation()); } /// Move-assign a basic_socket from a socket of another protocol type. @@ -203,21 +347,28 @@ public: * occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_socket(io_context&) constructor. + * constructed using the @c basic_socket(const executor_type&) constructor. */ - template - typename enable_if::value, - basic_socket>::type& operator=( - basic_socket&& other) + template + typename enable_if< + is_convertible::value + && is_convertible::value, + basic_socket& + >::type operator=(basic_socket && other) { - basic_socket tmp(ASIO_MOVE_CAST2(basic_socket< - Protocol1, SocketService1>)(other)); - basic_io_object::operator=( - ASIO_MOVE_CAST(basic_socket)(tmp)); + basic_socket tmp(std::move(other)); + impl_ = std::move(tmp.impl_); return *this; } #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + +#if !defined(ASIO_NO_EXTENSIONS) /// Get a reference to the lowest layer. /** * This function returns a reference to the lowest layer in a stack of @@ -245,6 +396,7 @@ public: { return *this; } +#endif // !defined(ASIO_NO_EXTENSIONS) /// Open the socket using the specified protocol. /** @@ -256,14 +408,14 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * socket.open(asio::ip::tcp::v4()); * @endcode */ void open(const protocol_type& protocol = protocol_type()) { asio::error_code ec; - this->get_service().open(this->get_implementation(), protocol, ec); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); asio::detail::throw_error(ec, "open"); } @@ -277,7 +429,7 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * asio::error_code ec; * socket.open(asio::ip::tcp::v4(), ec); * if (ec) @@ -286,10 +438,11 @@ public: * } * @endcode */ - asio::error_code open(const protocol_type& protocol, + ASIO_SYNC_OP_VOID open(const protocol_type& protocol, asio::error_code& ec) { - return this->get_service().open(this->get_implementation(), protocol, ec); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Assign an existing native socket to the socket. @@ -306,7 +459,7 @@ public: const native_handle_type& native_socket) { asio::error_code ec; - this->get_service().assign(this->get_implementation(), + impl_.get_service().assign(impl_.get_implementation(), protocol, native_socket, ec); asio::detail::throw_error(ec, "assign"); } @@ -321,17 +474,18 @@ public: * * @param ec Set to indicate what error occurred, if any. */ - asio::error_code assign(const protocol_type& protocol, + ASIO_SYNC_OP_VOID assign(const protocol_type& protocol, const native_handle_type& native_socket, asio::error_code& ec) { - return this->get_service().assign(this->get_implementation(), + impl_.get_service().assign(impl_.get_implementation(), protocol, native_socket, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Determine whether the socket is open. bool is_open() const { - return this->get_service().is_open(this->get_implementation()); + return impl_.get_service().is_open(impl_.get_implementation()); } /// Close the socket. @@ -349,7 +503,7 @@ public: void close() { asio::error_code ec; - this->get_service().close(this->get_implementation(), ec); + impl_.get_service().close(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "close"); } @@ -364,7 +518,7 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::error_code ec; * socket.close(ec); @@ -377,9 +531,62 @@ public: * @note For portable behaviour with respect to graceful closure of a * connected socket, call shutdown() before closing the socket. */ - asio::error_code close(asio::error_code& ec) + ASIO_SYNC_OP_VOID close(asio::error_code& ec) { - return this->get_service().close(this->get_implementation(), ec); + impl_.get_service().close(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying native socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. Ownership + * of the native socket is then transferred to the caller. + * + * @throws asio::system_error Thrown on failure. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release() + { + asio::error_code ec; + native_handle_type s = impl_.get_service().release( + impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "release"); + return s; + } + + /// Release ownership of the underlying native socket. + /** + * This function causes all outstanding asynchronous connect, send and receive + * operations to finish immediately, and the handlers for cancelled operations + * will be passed the asio::error::operation_aborted error. Ownership + * of the native socket is then transferred to the caller. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release(asio::error_code& ec) + { + return impl_.get_service().release(impl_.get_implementation(), ec); } /// Get the native socket representation. @@ -390,7 +597,7 @@ public: */ native_handle_type native_handle() { - return this->get_service().native_handle(this->get_implementation()); + return impl_.get_service().native_handle(impl_.get_implementation()); } /// Cancel all asynchronous operations associated with the socket. @@ -437,7 +644,7 @@ public: void cancel() { asio::error_code ec; - this->get_service().cancel(this->get_implementation(), ec); + impl_.get_service().cancel(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "cancel"); } @@ -482,9 +689,10 @@ public: "operation_not_supported when used on Windows XP, Windows Server 2003, " "or earlier. Consult documentation for details.")) #endif - asio::error_code cancel(asio::error_code& ec) + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) { - return this->get_service().cancel(this->get_implementation(), ec); + impl_.get_service().cancel(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Determine whether the socket is at the out-of-band data mark. @@ -500,7 +708,7 @@ public: bool at_mark() const { asio::error_code ec; - bool b = this->get_service().at_mark(this->get_implementation(), ec); + bool b = impl_.get_service().at_mark(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "at_mark"); return b; } @@ -517,7 +725,7 @@ public: */ bool at_mark(asio::error_code& ec) const { - return this->get_service().at_mark(this->get_implementation(), ec); + return impl_.get_service().at_mark(impl_.get_implementation(), ec); } /// Determine the number of bytes available for reading. @@ -533,8 +741,8 @@ public: std::size_t available() const { asio::error_code ec; - std::size_t s = this->get_service().available( - this->get_implementation(), ec); + std::size_t s = impl_.get_service().available( + impl_.get_implementation(), ec); asio::detail::throw_error(ec, "available"); return s; } @@ -551,7 +759,7 @@ public: */ std::size_t available(asio::error_code& ec) const { - return this->get_service().available(this->get_implementation(), ec); + return impl_.get_service().available(impl_.get_implementation(), ec); } /// Bind the socket to the given local endpoint. @@ -566,7 +774,7 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * socket.open(asio::ip::tcp::v4()); * socket.bind(asio::ip::tcp::endpoint( * asio::ip::tcp::v4(), 12345)); @@ -575,7 +783,7 @@ public: void bind(const endpoint_type& endpoint) { asio::error_code ec; - this->get_service().bind(this->get_implementation(), endpoint, ec); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); asio::detail::throw_error(ec, "bind"); } @@ -591,7 +799,7 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * socket.open(asio::ip::tcp::v4()); * asio::error_code ec; * socket.bind(asio::ip::tcp::endpoint( @@ -602,10 +810,11 @@ public: * } * @endcode */ - asio::error_code bind(const endpoint_type& endpoint, + ASIO_SYNC_OP_VOID bind(const endpoint_type& endpoint, asio::error_code& ec) { - return this->get_service().bind(this->get_implementation(), endpoint, ec); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Connect the socket to the specified endpoint. @@ -625,7 +834,7 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * asio::ip::tcp::endpoint endpoint( * asio::ip::address::from_string("1.2.3.4"), 12345); * socket.connect(endpoint); @@ -636,11 +845,11 @@ public: asio::error_code ec; if (!is_open()) { - this->get_service().open(this->get_implementation(), + impl_.get_service().open(impl_.get_implementation(), peer_endpoint.protocol(), ec); asio::detail::throw_error(ec, "connect"); } - this->get_service().connect(this->get_implementation(), peer_endpoint, ec); + impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); asio::detail::throw_error(ec, "connect"); } @@ -661,7 +870,7 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * asio::ip::tcp::endpoint endpoint( * asio::ip::address::from_string("1.2.3.4"), 12345); * asio::error_code ec; @@ -672,20 +881,21 @@ public: * } * @endcode */ - asio::error_code connect(const endpoint_type& peer_endpoint, + ASIO_SYNC_OP_VOID connect(const endpoint_type& peer_endpoint, asio::error_code& ec) { if (!is_open()) { - if (this->get_service().open(this->get_implementation(), - peer_endpoint.protocol(), ec)) + impl_.get_service().open(impl_.get_implementation(), + peer_endpoint.protocol(), ec); + if (ec) { - return ec; + ASIO_SYNC_OP_VOID_RETURN(ec); } } - return this->get_service().connect( - this->get_implementation(), peer_endpoint, ec); + impl_.get_service().connect(impl_.get_implementation(), peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Start an asynchronous connect. @@ -707,9 +917,9 @@ public: * const asio::error_code& error // Result of operation * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * @code @@ -723,7 +933,7 @@ public: * * ... * - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * asio::ip::tcp::endpoint endpoint( * asio::ip::address::from_string("1.2.3.4"), 12345); * socket.async_connect(endpoint, connect_handler); @@ -735,31 +945,15 @@ public: async_connect(const endpoint_type& peer_endpoint, ASIO_MOVE_ARG(ConnectHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a ConnectHandler. - ASIO_CONNECT_HANDLER_CHECK(ConnectHandler, handler) type_check; - + asio::error_code open_ec; if (!is_open()) { - asio::error_code ec; const protocol_type protocol = peer_endpoint.protocol(); - if (this->get_service().open(this->get_implementation(), protocol, ec)) - { - async_completion init(handler); - - asio::post(this->get_executor(), - asio::detail::bind_handler( - ASIO_MOVE_CAST(ASIO_HANDLER_TYPE( - ConnectHandler, void (asio::error_code)))( - init.handler), ec)); - - return init.result.get(); - } + impl_.get_service().open(impl_.get_implementation(), protocol, open_ec); } - return this->get_service().async_connect(this->get_implementation(), - peer_endpoint, ASIO_MOVE_CAST(ConnectHandler)(handler)); + return async_initiate( + initiate_async_connect(), handler, this, peer_endpoint, open_ec); } /// Set an option on the socket. @@ -790,7 +984,7 @@ public: * @par Example * Setting the IPPROTO_TCP/TCP_NODELAY option: * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::ip::tcp::no_delay option(true); * socket.set_option(option); @@ -800,7 +994,7 @@ public: void set_option(const SettableSocketOption& option) { asio::error_code ec; - this->get_service().set_option(this->get_implementation(), option, ec); + impl_.get_service().set_option(impl_.get_implementation(), option, ec); asio::detail::throw_error(ec, "set_option"); } @@ -832,7 +1026,7 @@ public: * @par Example * Setting the IPPROTO_TCP/TCP_NODELAY option: * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::ip::tcp::no_delay option(true); * asio::error_code ec; @@ -844,11 +1038,11 @@ public: * @endcode */ template - asio::error_code set_option(const SettableSocketOption& option, + ASIO_SYNC_OP_VOID set_option(const SettableSocketOption& option, asio::error_code& ec) { - return this->get_service().set_option( - this->get_implementation(), option, ec); + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Get an option from the socket. @@ -879,7 +1073,7 @@ public: * @par Example * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::ip::tcp::socket::keep_alive option; * socket.get_option(option); @@ -890,7 +1084,7 @@ public: void get_option(GettableSocketOption& option) const { asio::error_code ec; - this->get_service().get_option(this->get_implementation(), option, ec); + impl_.get_service().get_option(impl_.get_implementation(), option, ec); asio::detail::throw_error(ec, "get_option"); } @@ -922,7 +1116,7 @@ public: * @par Example * Getting the value of the SOL_SOCKET/SO_KEEPALIVE option: * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::ip::tcp::socket::keep_alive option; * asio::error_code ec; @@ -935,11 +1129,11 @@ public: * @endcode */ template - asio::error_code get_option(GettableSocketOption& option, + ASIO_SYNC_OP_VOID get_option(GettableSocketOption& option, asio::error_code& ec) const { - return this->get_service().get_option( - this->get_implementation(), option, ec); + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Perform an IO control command on the socket. @@ -957,7 +1151,7 @@ public: * @par Example * Getting the number of bytes ready to read: * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::ip::tcp::socket::bytes_readable command; * socket.io_control(command); @@ -968,7 +1162,7 @@ public: void io_control(IoControlCommand& command) { asio::error_code ec; - this->get_service().io_control(this->get_implementation(), command, ec); + impl_.get_service().io_control(impl_.get_implementation(), command, ec); asio::detail::throw_error(ec, "io_control"); } @@ -987,7 +1181,7 @@ public: * @par Example * Getting the number of bytes ready to read: * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::ip::tcp::socket::bytes_readable command; * asio::error_code ec; @@ -1000,11 +1194,11 @@ public: * @endcode */ template - asio::error_code io_control(IoControlCommand& command, + ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, asio::error_code& ec) { - return this->get_service().io_control( - this->get_implementation(), command, ec); + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Gets the non-blocking mode of the socket. @@ -1020,7 +1214,7 @@ public: */ bool non_blocking() const { - return this->get_service().non_blocking(this->get_implementation()); + return impl_.get_service().non_blocking(impl_.get_implementation()); } /// Sets the non-blocking mode of the socket. @@ -1039,7 +1233,7 @@ public: void non_blocking(bool mode) { asio::error_code ec; - this->get_service().non_blocking(this->get_implementation(), mode, ec); + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); asio::detail::throw_error(ec, "non_blocking"); } @@ -1056,11 +1250,11 @@ public: * operations. Asynchronous operations will never fail with the error * asio::error::would_block. */ - asio::error_code non_blocking( + ASIO_SYNC_OP_VOID non_blocking( bool mode, asio::error_code& ec) { - return this->get_service().non_blocking( - this->get_implementation(), mode, ec); + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Gets the non-blocking mode of the native socket implementation. @@ -1149,7 +1343,7 @@ public: */ bool native_non_blocking() const { - return this->get_service().native_non_blocking(this->get_implementation()); + return impl_.get_service().native_non_blocking(impl_.get_implementation()); } /// Sets the non-blocking mode of the native socket implementation. @@ -1240,8 +1434,8 @@ public: void native_non_blocking(bool mode) { asio::error_code ec; - this->get_service().native_non_blocking( - this->get_implementation(), mode, ec); + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); asio::detail::throw_error(ec, "native_non_blocking"); } @@ -1330,11 +1524,12 @@ public: * sock.async_wait(tcp::socket::wait_write, op); * } @endcode */ - asio::error_code native_non_blocking( + ASIO_SYNC_OP_VOID native_non_blocking( bool mode, asio::error_code& ec) { - return this->get_service().native_non_blocking( - this->get_implementation(), mode, ec); + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Get the local endpoint of the socket. @@ -1347,7 +1542,7 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(); * @endcode @@ -1355,8 +1550,8 @@ public: endpoint_type local_endpoint() const { asio::error_code ec; - endpoint_type ep = this->get_service().local_endpoint( - this->get_implementation(), ec); + endpoint_type ep = impl_.get_service().local_endpoint( + impl_.get_implementation(), ec); asio::detail::throw_error(ec, "local_endpoint"); return ep; } @@ -1372,7 +1567,7 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::error_code ec; * asio::ip::tcp::endpoint endpoint = socket.local_endpoint(ec); @@ -1384,7 +1579,7 @@ public: */ endpoint_type local_endpoint(asio::error_code& ec) const { - return this->get_service().local_endpoint(this->get_implementation(), ec); + return impl_.get_service().local_endpoint(impl_.get_implementation(), ec); } /// Get the remote endpoint of the socket. @@ -1397,7 +1592,7 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(); * @endcode @@ -1405,8 +1600,8 @@ public: endpoint_type remote_endpoint() const { asio::error_code ec; - endpoint_type ep = this->get_service().remote_endpoint( - this->get_implementation(), ec); + endpoint_type ep = impl_.get_service().remote_endpoint( + impl_.get_implementation(), ec); asio::detail::throw_error(ec, "remote_endpoint"); return ep; } @@ -1422,7 +1617,7 @@ public: * * @par Example * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::error_code ec; * asio::ip::tcp::endpoint endpoint = socket.remote_endpoint(ec); @@ -1434,7 +1629,7 @@ public: */ endpoint_type remote_endpoint(asio::error_code& ec) const { - return this->get_service().remote_endpoint(this->get_implementation(), ec); + return impl_.get_service().remote_endpoint(impl_.get_implementation(), ec); } /// Disable sends or receives on the socket. @@ -1449,7 +1644,7 @@ public: * @par Example * Shutting down the send side of the socket: * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * socket.shutdown(asio::ip::tcp::socket::shutdown_send); * @endcode @@ -1457,7 +1652,7 @@ public: void shutdown(shutdown_type what) { asio::error_code ec; - this->get_service().shutdown(this->get_implementation(), what, ec); + impl_.get_service().shutdown(impl_.get_implementation(), what, ec); asio::detail::throw_error(ec, "shutdown"); } @@ -1473,7 +1668,7 @@ public: * @par Example * Shutting down the send side of the socket: * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::error_code ec; * socket.shutdown(asio::ip::tcp::socket::shutdown_send, ec); @@ -1483,10 +1678,11 @@ public: * } * @endcode */ - asio::error_code shutdown(shutdown_type what, + ASIO_SYNC_OP_VOID shutdown(shutdown_type what, asio::error_code& ec) { - return this->get_service().shutdown(this->get_implementation(), what, ec); + impl_.get_service().shutdown(impl_.get_implementation(), what, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Wait for the socket to become ready to read, ready to write, or to have @@ -1500,7 +1696,7 @@ public: * @par Example * Waiting for a socket to become readable. * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * socket.wait(asio::ip::tcp::socket::wait_read); * @endcode @@ -1508,7 +1704,7 @@ public: void wait(wait_type w) { asio::error_code ec; - this->get_service().wait(this->get_implementation(), w, ec); + impl_.get_service().wait(impl_.get_implementation(), w, ec); asio::detail::throw_error(ec, "wait"); } @@ -1525,15 +1721,16 @@ public: * @par Example * Waiting for a socket to become readable. * @code - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * asio::error_code ec; * socket.wait(asio::ip::tcp::socket::wait_read, ec); * @endcode */ - asio::error_code wait(wait_type w, asio::error_code& ec) + ASIO_SYNC_OP_VOID wait(wait_type w, asio::error_code& ec) { - return this->get_service().wait(this->get_implementation(), w, ec); + impl_.get_service().wait(impl_.get_implementation(), w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Asynchronously wait for the socket to become ready to read, ready to @@ -1551,9 +1748,9 @@ public: * const asio::error_code& error // Result of operation * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * @code @@ -1567,7 +1764,7 @@ public: * * ... * - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * ... * socket.async_wait(asio::ip::tcp::socket::wait_read, wait_handler); * @endcode @@ -1577,19 +1774,79 @@ public: void (asio::error_code)) async_wait(wait_type w, ASIO_MOVE_ARG(WaitHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WaitHandler. - ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; - - return this->get_service().async_wait(this->get_implementation(), - w, ASIO_MOVE_CAST(WaitHandler)(handler)); + return async_initiate( + initiate_async_wait(), handler, this, w); } protected: /// Protected destructor to prevent deletion through this type. + /** + * This function destroys the socket, cancelling any outstanding asynchronous + * operations associated with the socket as if by calling @c cancel. + */ ~basic_socket() { } + +#if defined(ASIO_WINDOWS_RUNTIME) + detail::io_object_impl< + detail::null_socket_service, Executor> impl_; +#elif defined(ASIO_HAS_IOCP) + detail::io_object_impl< + detail::win_iocp_socket_service, Executor> impl_; +#else + detail::io_object_impl< + detail::reactive_socket_service, Executor> impl_; +#endif + +private: + // Disallow copying and assignment. + basic_socket(const basic_socket&) ASIO_DELETED; + basic_socket& operator=(const basic_socket&) ASIO_DELETED; + + struct initiate_async_connect + { + template + void operator()(ASIO_MOVE_ARG(ConnectHandler) handler, + basic_socket* self, const endpoint_type& peer_endpoint, + const asio::error_code& open_ec) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a ConnectHandler. + ASIO_CONNECT_HANDLER_CHECK(ConnectHandler, handler) type_check; + + if (open_ec) + { + asio::post(self->impl_.get_executor(), + asio::detail::bind_handler( + ASIO_MOVE_CAST(ConnectHandler)(handler), open_ec)); + } + else + { + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_connect( + self->impl_.get_implementation(), peer_endpoint, + handler2.value, self->impl_.get_implementation_executor()); + } + } + }; + + struct initiate_async_wait + { + template + void operator()(ASIO_MOVE_ARG(WaitHandler) handler, + basic_socket* self, wait_type w) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_wait( + self->impl_.get_implementation(), w, handler2.value, + self->impl_.get_implementation_executor()); + } + }; }; } // namespace asio diff --git a/source/modules/hylia/link/asio/basic_socket_acceptor.hpp b/source/modules/hylia/link/asio/basic_socket_acceptor.hpp index 92d2c4b08..87f24c263 100644 --- a/source/modules/hylia/link/asio/basic_socket_acceptor.hpp +++ b/source/modules/hylia/link/asio/basic_socket_acceptor.hpp @@ -2,7 +2,7 @@ // basic_socket_acceptor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,19 +16,42 @@ #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" -#include "asio/basic_io_object.hpp" #include "asio/basic_socket.hpp" #include "asio/detail/handler_type_requirements.hpp" +#include "asio/detail/io_object_impl.hpp" +#include "asio/detail/non_const_lvalue.hpp" #include "asio/detail/throw_error.hpp" #include "asio/detail/type_traits.hpp" #include "asio/error.hpp" -#include "asio/socket_acceptor_service.hpp" +#include "asio/execution_context.hpp" +#include "asio/executor.hpp" #include "asio/socket_base.hpp" +#if defined(ASIO_WINDOWS_RUNTIME) +# include "asio/detail/null_socket_service.hpp" +#elif defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_socket_service.hpp" +#else +# include "asio/detail/reactive_socket_service.hpp" +#endif + +#if defined(ASIO_HAS_MOVE) +# include +#endif // defined(ASIO_HAS_MOVE) + #include "asio/detail/push_options.hpp" namespace asio { +#if !defined(ASIO_BASIC_SOCKET_ACCEPTOR_FWD_DECL) +#define ASIO_BASIC_SOCKET_ACCEPTOR_FWD_DECL + +// Forward declaration with defaulted arguments. +template +class basic_socket_acceptor; + +#endif // !defined(ASIO_BASIC_SOCKET_ACCEPTOR_FWD_DECL) + /// Provides the ability to accept new connections. /** * The basic_socket_acceptor class template is used for accepting new socket @@ -41,7 +64,7 @@ namespace asio { * @par Example * Opening a socket acceptor with the SO_REUSEADDR option enabled: * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port); * acceptor.open(endpoint.protocol()); * acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); @@ -49,15 +72,27 @@ namespace asio { * acceptor.listen(); * @endcode */ -template > +template class basic_socket_acceptor - : public basic_io_object, - public socket_base + : public socket_base { public: + /// The type of the executor associated with the object. + typedef Executor executor_type; + /// The native representation of an acceptor. - typedef typename SocketAcceptorService::native_handle_type native_handle_type; +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#elif defined(ASIO_WINDOWS_RUNTIME) + typedef typename detail::null_socket_service< + Protocol>::native_handle_type native_handle_type; +#elif defined(ASIO_HAS_IOCP) + typedef typename detail::win_iocp_socket_service< + Protocol>::native_handle_type native_handle_type; +#else + typedef typename detail::reactive_socket_service< + Protocol>::native_handle_type native_handle_type; +#endif /// The protocol type. typedef Protocol protocol_type; @@ -71,12 +106,31 @@ public: * connections. The open() function must be called before the acceptor can * accept new socket connections. * - * @param io_context The io_context object that the acceptor will use to + * @param ex The I/O executor that the acceptor will use, by default, to * dispatch handlers for any asynchronous operations performed on the * acceptor. */ - explicit basic_socket_acceptor(asio::io_context& io_context) - : basic_io_object(io_context) + explicit basic_socket_acceptor(const executor_type& ex) + : impl_(ex) + { + } + + /// Construct an acceptor without opening it. + /** + * This constructor creates an acceptor without opening it to listen for new + * connections. The open() function must be called before the acceptor can + * accept new socket connections. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + */ + template + explicit basic_socket_acceptor(ExecutionContext& context, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) { } @@ -84,7 +138,7 @@ public: /** * This constructor creates an acceptor and automatically opens it. * - * @param io_context The io_context object that the acceptor will use to + * @param ex The I/O executor that the acceptor will use, by default, to * dispatch handlers for any asynchronous operations performed on the * acceptor. * @@ -92,12 +146,36 @@ public: * * @throws asio::system_error Thrown on failure. */ - basic_socket_acceptor(asio::io_context& io_context, - const protocol_type& protocol) - : basic_io_object(io_context) + basic_socket_acceptor(const executor_type& ex, const protocol_type& protocol) + : impl_(ex) { asio::error_code ec; - this->get_service().open(this->get_implementation(), protocol, ec); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + } + + /// Construct an open acceptor. + /** + * This constructor creates an acceptor and automatically opens it. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_socket_acceptor(ExecutionContext& context, + const protocol_type& protocol, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + asio::error_code ec; + impl_.get_service().open(impl_.get_implementation(), protocol, ec); asio::detail::throw_error(ec, "open"); } @@ -106,7 +184,7 @@ public: * This constructor creates an acceptor and automatically opens it to listen * for new connections on the specified endpoint. * - * @param io_context The io_context object that the acceptor will use to + * @param ex The I/O executor that the acceptor will use, by default, to * dispatch handlers for any asynchronous operations performed on the * acceptor. * @@ -120,32 +198,84 @@ public: * * @note This constructor is equivalent to the following code: * @code - * basic_socket_acceptor acceptor(io_context); + * basic_socket_acceptor acceptor(my_context); * acceptor.open(endpoint.protocol()); * if (reuse_addr) * acceptor.set_option(socket_base::reuse_address(true)); * acceptor.bind(endpoint); - * acceptor.listen(listen_backlog); + * acceptor.listen(); * @endcode */ - basic_socket_acceptor(asio::io_context& io_context, + basic_socket_acceptor(const executor_type& ex, const endpoint_type& endpoint, bool reuse_addr = true) - : basic_io_object(io_context) + : impl_(ex) + { + asio::error_code ec; + const protocol_type protocol = endpoint.protocol(); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + asio::detail::throw_error(ec, "open"); + if (reuse_addr) + { + impl_.get_service().set_option(impl_.get_implementation(), + socket_base::reuse_address(true), ec); + asio::detail::throw_error(ec, "set_option"); + } + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + asio::detail::throw_error(ec, "bind"); + impl_.get_service().listen(impl_.get_implementation(), + socket_base::max_listen_connections, ec); + asio::detail::throw_error(ec, "listen"); + } + + /// Construct an acceptor opened on the given endpoint. + /** + * This constructor creates an acceptor and automatically opens it to listen + * for new connections on the specified endpoint. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + * + * @param endpoint An endpoint on the local machine on which the acceptor + * will listen for new connections. + * + * @param reuse_addr Whether the constructor should set the socket option + * socket_base::reuse_address. + * + * @throws asio::system_error Thrown on failure. + * + * @note This constructor is equivalent to the following code: + * @code + * basic_socket_acceptor acceptor(my_context); + * acceptor.open(endpoint.protocol()); + * if (reuse_addr) + * acceptor.set_option(socket_base::reuse_address(true)); + * acceptor.bind(endpoint); + * acceptor.listen(); + * @endcode + */ + template + basic_socket_acceptor(ExecutionContext& context, + const endpoint_type& endpoint, bool reuse_addr = true, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) { asio::error_code ec; const protocol_type protocol = endpoint.protocol(); - this->get_service().open(this->get_implementation(), protocol, ec); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); asio::detail::throw_error(ec, "open"); if (reuse_addr) { - this->get_service().set_option(this->get_implementation(), + impl_.get_service().set_option(impl_.get_implementation(), socket_base::reuse_address(true), ec); asio::detail::throw_error(ec, "set_option"); } - this->get_service().bind(this->get_implementation(), endpoint, ec); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); asio::detail::throw_error(ec, "bind"); - this->get_service().listen(this->get_implementation(), - socket_base::max_connections, ec); + impl_.get_service().listen(impl_.get_implementation(), + socket_base::max_listen_connections, ec); asio::detail::throw_error(ec, "listen"); } @@ -154,7 +284,7 @@ public: * This constructor creates an acceptor object to hold an existing native * acceptor. * - * @param io_context The io_context object that the acceptor will use to + * @param ex The I/O executor that the acceptor will use, by default, to * dispatch handlers for any asynchronous operations performed on the * acceptor. * @@ -164,12 +294,41 @@ public: * * @throws asio::system_error Thrown on failure. */ - basic_socket_acceptor(asio::io_context& io_context, + basic_socket_acceptor(const executor_type& ex, const protocol_type& protocol, const native_handle_type& native_acceptor) - : basic_io_object(io_context) + : impl_(ex) { asio::error_code ec; - this->get_service().assign(this->get_implementation(), + impl_.get_service().assign(impl_.get_implementation(), + protocol, native_acceptor, ec); + asio::detail::throw_error(ec, "assign"); + } + + /// Construct a basic_socket_acceptor on an existing native acceptor. + /** + * This constructor creates an acceptor object to hold an existing native + * acceptor. + * + * @param context An execution context which provides the I/O executor that + * the acceptor will use, by default, to dispatch handlers for any + * asynchronous operations performed on the acceptor. + * + * @param protocol An object specifying protocol parameters to be used. + * + * @param native_acceptor A native acceptor. + * + * @throws asio::system_error Thrown on failure. + */ + template + basic_socket_acceptor(ExecutionContext& context, + const protocol_type& protocol, const native_handle_type& native_acceptor, + typename enable_if< + is_convertible::value + >::type* = 0) + : impl_(context) + { + asio::error_code ec; + impl_.get_service().assign(impl_.get_implementation(), protocol, native_acceptor, ec); asio::detail::throw_error(ec, "assign"); } @@ -183,11 +342,11 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_socket_acceptor(io_context&) constructor. + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. */ basic_socket_acceptor(basic_socket_acceptor&& other) - : basic_io_object( - ASIO_MOVE_CAST(basic_socket_acceptor)(other)) + : impl_(std::move(other.impl_)) { } @@ -199,17 +358,17 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_socket_acceptor(io_context&) constructor. + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. */ basic_socket_acceptor& operator=(basic_socket_acceptor&& other) { - basic_io_object::operator=( - ASIO_MOVE_CAST(basic_socket_acceptor)(other)); + impl_ = std::move(other.impl_); return *this; } // All socket acceptors have access to each other's implementations. - template + template friend class basic_socket_acceptor; /// Move-construct a basic_socket_acceptor from an acceptor of another @@ -221,17 +380,17 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_socket(io_context&) constructor. + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. */ - template - basic_socket_acceptor( - basic_socket_acceptor&& other, - typename enable_if::value>::type* = 0) - : basic_io_object( - other.get_service().get_io_context()) + template + basic_socket_acceptor(basic_socket_acceptor&& other, + typename enable_if< + is_convertible::value + && is_convertible::value + >::type* = 0) + : impl_(std::move(other.impl_)) { - this->get_service().template converting_move_construct( - this->get_implementation(), other.get_implementation()); } /// Move-assign a basic_socket_acceptor from an acceptor of another protocol @@ -243,21 +402,38 @@ public: * will occur. * * @note Following the move, the moved-from object is in the same state as if - * constructed using the @c basic_socket(io_context&) constructor. + * constructed using the @c basic_socket_acceptor(const executor_type&) + * constructor. */ - template - typename enable_if::value, - basic_socket_acceptor>::type& operator=( - basic_socket_acceptor&& other) + template + typename enable_if< + is_convertible::value + && is_convertible::value, + basic_socket_acceptor& + >::type operator=(basic_socket_acceptor&& other) { - basic_socket_acceptor tmp(ASIO_MOVE_CAST2(basic_socket_acceptor< - Protocol1, SocketAcceptorService1>)(other)); - basic_io_object::operator=( - ASIO_MOVE_CAST(basic_socket_acceptor)(tmp)); + basic_socket_acceptor tmp(std::move(other)); + impl_ = std::move(tmp.impl_); return *this; } #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + /// Destroys the acceptor. + /** + * This function destroys the acceptor, cancelling any outstanding + * asynchronous operations associated with the acceptor as if by calling + * @c cancel. + */ + ~basic_socket_acceptor() + { + } + + /// Get the executor associated with the object. + executor_type get_executor() ASIO_NOEXCEPT + { + return impl_.get_executor(); + } + /// Open the acceptor using the specified protocol. /** * This function opens the socket acceptor so that it will use the specified @@ -269,14 +445,14 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * acceptor.open(asio::ip::tcp::v4()); * @endcode */ void open(const protocol_type& protocol = protocol_type()) { asio::error_code ec; - this->get_service().open(this->get_implementation(), protocol, ec); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); asio::detail::throw_error(ec, "open"); } @@ -291,7 +467,7 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * asio::error_code ec; * acceptor.open(asio::ip::tcp::v4(), ec); * if (ec) @@ -300,10 +476,11 @@ public: * } * @endcode */ - asio::error_code open(const protocol_type& protocol, + ASIO_SYNC_OP_VOID open(const protocol_type& protocol, asio::error_code& ec) { - return this->get_service().open(this->get_implementation(), protocol, ec); + impl_.get_service().open(impl_.get_implementation(), protocol, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Assigns an existing native acceptor to the acceptor. @@ -320,7 +497,7 @@ public: const native_handle_type& native_acceptor) { asio::error_code ec; - this->get_service().assign(this->get_implementation(), + impl_.get_service().assign(impl_.get_implementation(), protocol, native_acceptor, ec); asio::detail::throw_error(ec, "assign"); } @@ -335,17 +512,18 @@ public: * * @param ec Set to indicate what error occurred, if any. */ - asio::error_code assign(const protocol_type& protocol, + ASIO_SYNC_OP_VOID assign(const protocol_type& protocol, const native_handle_type& native_acceptor, asio::error_code& ec) { - return this->get_service().assign(this->get_implementation(), + impl_.get_service().assign(impl_.get_implementation(), protocol, native_acceptor, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Determine whether the acceptor is open. bool is_open() const { - return this->get_service().is_open(this->get_implementation()); + return impl_.get_service().is_open(impl_.get_implementation()); } /// Bind the acceptor to the given local endpoint. @@ -360,7 +538,7 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), 12345); * acceptor.open(endpoint.protocol()); * acceptor.bind(endpoint); @@ -369,7 +547,7 @@ public: void bind(const endpoint_type& endpoint) { asio::error_code ec; - this->get_service().bind(this->get_implementation(), endpoint, ec); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); asio::detail::throw_error(ec, "bind"); } @@ -385,7 +563,7 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), 12345); * acceptor.open(endpoint.protocol()); * asio::error_code ec; @@ -396,10 +574,11 @@ public: * } * @endcode */ - asio::error_code bind(const endpoint_type& endpoint, + ASIO_SYNC_OP_VOID bind(const endpoint_type& endpoint, asio::error_code& ec) { - return this->get_service().bind(this->get_implementation(), endpoint, ec); + impl_.get_service().bind(impl_.get_implementation(), endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Place the acceptor into the state where it will listen for new @@ -412,10 +591,10 @@ public: * * @throws asio::system_error Thrown on failure. */ - void listen(int backlog = socket_base::max_connections) + void listen(int backlog = socket_base::max_listen_connections) { asio::error_code ec; - this->get_service().listen(this->get_implementation(), backlog, ec); + impl_.get_service().listen(impl_.get_implementation(), backlog, ec); asio::detail::throw_error(ec, "listen"); } @@ -431,19 +610,20 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::error_code ec; - * acceptor.listen(asio::socket_base::max_connections, ec); + * acceptor.listen(asio::socket_base::max_listen_connections, ec); * if (ec) * { * // An error occurred. * } * @endcode */ - asio::error_code listen(int backlog, asio::error_code& ec) + ASIO_SYNC_OP_VOID listen(int backlog, asio::error_code& ec) { - return this->get_service().listen(this->get_implementation(), backlog, ec); + impl_.get_service().listen(impl_.get_implementation(), backlog, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Close the acceptor. @@ -459,7 +639,7 @@ public: void close() { asio::error_code ec; - this->get_service().close(this->get_implementation(), ec); + impl_.get_service().close(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "close"); } @@ -475,7 +655,7 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::error_code ec; * acceptor.close(ec); @@ -485,9 +665,62 @@ public: * } * @endcode */ - asio::error_code close(asio::error_code& ec) + ASIO_SYNC_OP_VOID close(asio::error_code& ec) + { + impl_.get_service().close(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); + } + + /// Release ownership of the underlying native acceptor. + /** + * This function causes all outstanding asynchronous accept operations to + * finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. Ownership of the + * native acceptor is then transferred to the caller. + * + * @throws asio::system_error Thrown on failure. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release() + { + asio::error_code ec; + native_handle_type s = impl_.get_service().release( + impl_.get_implementation(), ec); + asio::detail::throw_error(ec, "release"); + return s; + } + + /// Release ownership of the underlying native acceptor. + /** + * This function causes all outstanding asynchronous accept operations to + * finish immediately, and the handlers for cancelled operations will be + * passed the asio::error::operation_aborted error. Ownership of the + * native acceptor is then transferred to the caller. + * + * @param ec Set to indicate what error occurred, if any. + * + * @note This function is unsupported on Windows versions prior to Windows + * 8.1, and will fail with asio::error::operation_not_supported on + * these platforms. + */ +#if defined(ASIO_MSVC) && (ASIO_MSVC >= 1400) \ + && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603) + __declspec(deprecated("This function always fails with " + "operation_not_supported when used on Windows versions " + "prior to Windows 8.1.")) +#endif + native_handle_type release(asio::error_code& ec) { - return this->get_service().close(this->get_implementation(), ec); + return impl_.get_service().release(impl_.get_implementation(), ec); } /// Get the native acceptor representation. @@ -498,7 +731,7 @@ public: */ native_handle_type native_handle() { - return this->get_service().native_handle(this->get_implementation()); + return impl_.get_service().native_handle(impl_.get_implementation()); } /// Cancel all asynchronous operations associated with the acceptor. @@ -512,7 +745,7 @@ public: void cancel() { asio::error_code ec; - this->get_service().cancel(this->get_implementation(), ec); + impl_.get_service().cancel(impl_.get_implementation(), ec); asio::detail::throw_error(ec, "cancel"); } @@ -524,9 +757,10 @@ public: * * @param ec Set to indicate what error occurred, if any. */ - asio::error_code cancel(asio::error_code& ec) + ASIO_SYNC_OP_VOID cancel(asio::error_code& ec) { - return this->get_service().cancel(this->get_implementation(), ec); + impl_.get_service().cancel(impl_.get_implementation(), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Set an option on the acceptor. @@ -544,7 +778,7 @@ public: * @par Example * Setting the SOL_SOCKET/SO_REUSEADDR option: * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::acceptor::reuse_address option(true); * acceptor.set_option(option); @@ -554,7 +788,7 @@ public: void set_option(const SettableSocketOption& option) { asio::error_code ec; - this->get_service().set_option(this->get_implementation(), option, ec); + impl_.get_service().set_option(impl_.get_implementation(), option, ec); asio::detail::throw_error(ec, "set_option"); } @@ -573,7 +807,7 @@ public: * @par Example * Setting the SOL_SOCKET/SO_REUSEADDR option: * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::acceptor::reuse_address option(true); * asio::error_code ec; @@ -585,11 +819,11 @@ public: * @endcode */ template - asio::error_code set_option(const SettableSocketOption& option, + ASIO_SYNC_OP_VOID set_option(const SettableSocketOption& option, asio::error_code& ec) { - return this->get_service().set_option( - this->get_implementation(), option, ec); + impl_.get_service().set_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Get an option from the acceptor. @@ -607,7 +841,7 @@ public: * @par Example * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::acceptor::reuse_address option; * acceptor.get_option(option); @@ -615,10 +849,10 @@ public: * @endcode */ template - void get_option(GettableSocketOption& option) + void get_option(GettableSocketOption& option) const { asio::error_code ec; - this->get_service().get_option(this->get_implementation(), option, ec); + impl_.get_service().get_option(impl_.get_implementation(), option, ec); asio::detail::throw_error(ec, "get_option"); } @@ -637,7 +871,7 @@ public: * @par Example * Getting the value of the SOL_SOCKET/SO_REUSEADDR option: * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::acceptor::reuse_address option; * asio::error_code ec; @@ -650,11 +884,11 @@ public: * @endcode */ template - asio::error_code get_option(GettableSocketOption& option, - asio::error_code& ec) + ASIO_SYNC_OP_VOID get_option(GettableSocketOption& option, + asio::error_code& ec) const { - return this->get_service().get_option( - this->get_implementation(), option, ec); + impl_.get_service().get_option(impl_.get_implementation(), option, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Perform an IO control command on the acceptor. @@ -671,7 +905,7 @@ public: * @par Example * Getting the number of bytes ready to read: * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::acceptor::non_blocking_io command(true); * socket.io_control(command); @@ -681,7 +915,7 @@ public: void io_control(IoControlCommand& command) { asio::error_code ec; - this->get_service().io_control(this->get_implementation(), command, ec); + impl_.get_service().io_control(impl_.get_implementation(), command, ec); asio::detail::throw_error(ec, "io_control"); } @@ -699,7 +933,7 @@ public: * @par Example * Getting the number of bytes ready to read: * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::acceptor::non_blocking_io command(true); * asio::error_code ec; @@ -711,11 +945,11 @@ public: * @endcode */ template - asio::error_code io_control(IoControlCommand& command, + ASIO_SYNC_OP_VOID io_control(IoControlCommand& command, asio::error_code& ec) { - return this->get_service().io_control( - this->get_implementation(), command, ec); + impl_.get_service().io_control(impl_.get_implementation(), command, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Gets the non-blocking mode of the acceptor. @@ -731,7 +965,7 @@ public: */ bool non_blocking() const { - return this->get_service().non_blocking(this->get_implementation()); + return impl_.get_service().non_blocking(impl_.get_implementation()); } /// Sets the non-blocking mode of the acceptor. @@ -750,7 +984,7 @@ public: void non_blocking(bool mode) { asio::error_code ec; - this->get_service().non_blocking(this->get_implementation(), mode, ec); + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); asio::detail::throw_error(ec, "non_blocking"); } @@ -767,11 +1001,11 @@ public: * operations. Asynchronous operations will never fail with the error * asio::error::would_block. */ - asio::error_code non_blocking( + ASIO_SYNC_OP_VOID non_blocking( bool mode, asio::error_code& ec) { - return this->get_service().non_blocking( - this->get_implementation(), mode, ec); + impl_.get_service().non_blocking(impl_.get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Gets the non-blocking mode of the native acceptor implementation. @@ -790,7 +1024,7 @@ public: */ bool native_non_blocking() const { - return this->get_service().native_non_blocking(this->get_implementation()); + return impl_.get_service().native_non_blocking(impl_.get_implementation()); } /// Sets the non-blocking mode of the native acceptor implementation. @@ -811,8 +1045,8 @@ public: void native_non_blocking(bool mode) { asio::error_code ec; - this->get_service().native_non_blocking( - this->get_implementation(), mode, ec); + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); asio::detail::throw_error(ec, "native_non_blocking"); } @@ -831,11 +1065,12 @@ public: * function fails with asio::error::invalid_argument, as the * combination does not make sense. */ - asio::error_code native_non_blocking( + ASIO_SYNC_OP_VOID native_non_blocking( bool mode, asio::error_code& ec) { - return this->get_service().native_non_blocking( - this->get_implementation(), mode, ec); + impl_.get_service().native_non_blocking( + impl_.get_implementation(), mode, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Get the local endpoint of the acceptor. @@ -848,7 +1083,7 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(); * @endcode @@ -856,8 +1091,8 @@ public: endpoint_type local_endpoint() const { asio::error_code ec; - endpoint_type ep = this->get_service().local_endpoint( - this->get_implementation(), ec); + endpoint_type ep = impl_.get_service().local_endpoint( + impl_.get_implementation(), ec); asio::detail::throw_error(ec, "local_endpoint"); return ep; } @@ -874,7 +1109,7 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::error_code ec; * asio::ip::tcp::endpoint endpoint = acceptor.local_endpoint(ec); @@ -886,7 +1121,7 @@ public: */ endpoint_type local_endpoint(asio::error_code& ec) const { - return this->get_service().local_endpoint(this->get_implementation(), ec); + return impl_.get_service().local_endpoint(impl_.get_implementation(), ec); } /// Wait for the acceptor to become ready to read, ready to write, or to have @@ -900,7 +1135,7 @@ public: * @par Example * Waiting for an acceptor to become readable. * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * acceptor.wait(asio::ip::tcp::acceptor::wait_read); * @endcode @@ -908,7 +1143,7 @@ public: void wait(wait_type w) { asio::error_code ec; - this->get_service().wait(this->get_implementation(), w, ec); + impl_.get_service().wait(impl_.get_implementation(), w, ec); asio::detail::throw_error(ec, "wait"); } @@ -925,15 +1160,16 @@ public: * @par Example * Waiting for an acceptor to become readable. * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::error_code ec; * acceptor.wait(asio::ip::tcp::acceptor::wait_read, ec); * @endcode */ - asio::error_code wait(wait_type w, asio::error_code& ec) + ASIO_SYNC_OP_VOID wait(wait_type w, asio::error_code& ec) { - return this->get_service().wait(this->get_implementation(), w, ec); + impl_.get_service().wait(impl_.get_implementation(), w, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Asynchronously wait for the acceptor to become ready to read, ready to @@ -951,9 +1187,9 @@ public: * const asio::error_code& error // Result of operation * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * @code @@ -967,7 +1203,7 @@ public: * * ... * - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * acceptor.async_wait( * asio::ip::tcp::acceptor::wait_read, @@ -979,14 +1215,11 @@ public: void (asio::error_code)) async_wait(wait_type w, ASIO_MOVE_ARG(WaitHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a WaitHandler. - ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; - - return this->get_service().async_wait(this->get_implementation(), - w, ASIO_MOVE_CAST(WaitHandler)(handler)); + return async_initiate( + initiate_async_wait(), handler, this, w); } +#if !defined(ASIO_NO_EXTENSIONS) /// Accept a new connection. /** * This function is used to accept a new connection from a peer into the @@ -999,18 +1232,20 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * acceptor.accept(socket); * @endcode */ - template - void accept(basic_socket& peer, - typename enable_if::value>::type* = 0) + template + void accept(basic_socket& peer, + typename enable_if< + is_convertible::value + >::type* = 0) { asio::error_code ec; - this->get_service().accept(this->get_implementation(), + impl_.get_service().accept(impl_.get_implementation(), peer, static_cast(0), ec); asio::detail::throw_error(ec, "accept"); } @@ -1027,9 +1262,9 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * asio::error_code ec; * acceptor.accept(socket, ec); * if (ec) @@ -1038,14 +1273,16 @@ public: * } * @endcode */ - template - asio::error_code accept( - basic_socket& peer, - asio::error_code& ec, - typename enable_if::value>::type* = 0) + template + ASIO_SYNC_OP_VOID accept( + basic_socket& peer, asio::error_code& ec, + typename enable_if< + is_convertible::value + >::type* = 0) { - return this->get_service().accept(this->get_implementation(), + impl_.get_service().accept(impl_.get_implementation(), peer, static_cast(0), ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Start an asynchronous accept. @@ -1064,9 +1301,9 @@ public: * const asio::error_code& error // Result of operation. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * @code @@ -1080,26 +1317,24 @@ public: * * ... * - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * acceptor.async_accept(socket, accept_handler); * @endcode */ - template + template ASIO_INITFN_RESULT_TYPE(AcceptHandler, void (asio::error_code)) - async_accept(basic_socket& peer, + async_accept(basic_socket& peer, ASIO_MOVE_ARG(AcceptHandler) handler, - typename enable_if::value>::type* = 0) + typename enable_if< + is_convertible::value + >::type* = 0) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a AcceptHandler. - ASIO_ACCEPT_HANDLER_CHECK(AcceptHandler, handler) type_check; - - return this->get_service().async_accept(this->get_implementation(), - peer, static_cast(0), - ASIO_MOVE_CAST(AcceptHandler)(handler)); + return async_initiate( + initiate_async_accept(), handler, this, + &peer, static_cast(0)); } /// Accept a new connection and obtain the endpoint of the peer @@ -1118,19 +1353,19 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * asio::ip::tcp::endpoint endpoint; * acceptor.accept(socket, endpoint); * @endcode */ - template - void accept(basic_socket& peer, + template + void accept(basic_socket& peer, endpoint_type& peer_endpoint) { asio::error_code ec; - this->get_service().accept(this->get_implementation(), + impl_.get_service().accept(impl_.get_implementation(), peer, &peer_endpoint, ec); asio::detail::throw_error(ec, "accept"); } @@ -1151,9 +1386,9 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... - * asio::ip::tcp::socket socket(io_context); + * asio::ip::tcp::socket socket(my_context); * asio::ip::tcp::endpoint endpoint; * asio::error_code ec; * acceptor.accept(socket, endpoint, ec); @@ -1163,13 +1398,13 @@ public: * } * @endcode */ - template - asio::error_code accept( - basic_socket& peer, + template + ASIO_SYNC_OP_VOID accept(basic_socket& peer, endpoint_type& peer_endpoint, asio::error_code& ec) { - return this->get_service().accept( - this->get_implementation(), peer, &peer_endpoint, ec); + impl_.get_service().accept( + impl_.get_implementation(), peer, &peer_endpoint, ec); + ASIO_SYNC_OP_VOID_RETURN(ec); } /// Start an asynchronous accept. @@ -1194,23 +1429,20 @@ public: * const asio::error_code& error // Result of operation. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). */ - template + template ASIO_INITFN_RESULT_TYPE(AcceptHandler, void (asio::error_code)) - async_accept(basic_socket& peer, + async_accept(basic_socket& peer, endpoint_type& peer_endpoint, ASIO_MOVE_ARG(AcceptHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a AcceptHandler. - ASIO_ACCEPT_HANDLER_CHECK(AcceptHandler, handler) type_check; - - return this->get_service().async_accept(this->get_implementation(), peer, - &peer_endpoint, ASIO_MOVE_CAST(AcceptHandler)(handler)); + return async_initiate( + initiate_async_accept(), handler, this, &peer, &peer_endpoint); } +#endif // !defined(ASIO_NO_EXTENSIONS) #if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) /// Accept a new connection. @@ -1228,7 +1460,7 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::socket socket(acceptor.accept()); * @endcode @@ -1236,9 +1468,8 @@ public: typename Protocol::socket accept() { asio::error_code ec; - typename Protocol::socket peer( - this->get_service().accept( - this->get_implementation(), 0, 0, ec)); + typename Protocol::socket peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); asio::detail::throw_error(ec, "accept"); return peer; } @@ -1259,7 +1490,7 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::socket socket(acceptor.accept(ec)); * if (ec) @@ -1270,7 +1501,9 @@ public: */ typename Protocol::socket accept(asio::error_code& ec) { - return this->get_service().accept(this->get_implementation(), 0, 0, ec); + typename Protocol::socket peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + return peer; } /// Start an asynchronous accept. @@ -1289,9 +1522,9 @@ public: * typename Protocol::socket peer // On success, the newly accepted socket. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * @code @@ -1306,7 +1539,7 @@ public: * * ... * - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * acceptor.async_accept(accept_handler); * @endcode @@ -1316,15 +1549,49 @@ public: void (asio::error_code, typename Protocol::socket)) async_accept(ASIO_MOVE_ARG(MoveAcceptHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a MoveAcceptHandler. - ASIO_MOVE_ACCEPT_HANDLER_CHECK(MoveAcceptHandler, - handler, typename Protocol::socket) type_check; + return async_initiate( + initiate_async_move_accept(), handler, this, + impl_.get_executor(), static_cast(0), + static_cast(0)); + } - return this->get_service().async_accept( - this->get_implementation(), static_cast(0), - static_cast(0), - ASIO_MOVE_CAST(MoveAcceptHandler)(handler)); + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param ex The I/O executor object to be used for the newly + * accepted socket. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept()); + * @endcode + */ + template + typename Protocol::socket::template rebind_executor::other + accept(const Executor1& ex, + typename enable_if< + is_executor::value + >::type* = 0) + { + asio::error_code ec; + typename Protocol::socket::template + rebind_executor::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + asio::detail::throw_error(ec, "accept"); + return peer; } /// Accept a new connection. @@ -1336,8 +1603,8 @@ public: * This overload requires that the Protocol template parameter satisfy the * AcceptableProtocol type requirements. * - * @param io_context The io_context object to be used for the newly accepted - * socket. + * @param context The I/O execution context object to be used for the newly + * accepted socket. * * @returns A socket object representing the newly accepted connection. * @@ -1345,17 +1612,23 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::socket socket(acceptor.accept()); * @endcode */ - typename Protocol::socket accept(asio::io_context& io_context) + template + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, + typename enable_if< + is_convertible::value + >::type* = 0) { asio::error_code ec; - typename Protocol::socket peer( - this->get_service().accept(this->get_implementation(), - &io_context, static_cast(0), ec)); + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); asio::detail::throw_error(ec, "accept"); return peer; } @@ -1369,7 +1642,7 @@ public: * This overload requires that the Protocol template parameter satisfy the * AcceptableProtocol type requirements. * - * @param io_context The io_context object to be used for the newly accepted + * @param ex The I/O executor object to be used for the newly accepted * socket. * * @param ec Set to indicate what error occurred, if any. @@ -1379,20 +1652,68 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... - * asio::ip::tcp::socket socket(acceptor.accept(io_context2, ec)); + * asio::ip::tcp::socket socket(acceptor.accept(my_context2, ec)); * if (ec) * { * // An error occurred. * } * @endcode */ - typename Protocol::socket accept( - asio::io_context& io_context, asio::error_code& ec) + template + typename Protocol::socket::template rebind_executor::other + accept(const Executor1& ex, asio::error_code& ec, + typename enable_if< + is_executor::value + >::type* = 0) { - return this->get_service().accept(this->get_implementation(), - &io_context, static_cast(0), ec); + typename Protocol::socket::template + rebind_executor::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::socket socket(acceptor.accept(my_context2, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, asio::error_code& ec, + typename enable_if< + is_convertible::value + >::type* = 0) + { + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), peer, 0, ec); + return peer; } /// Start an asynchronous accept. @@ -1403,7 +1724,7 @@ public: * This overload requires that the Protocol template parameter satisfy the * AcceptableProtocol type requirements. * - * @param io_context The io_context object to be used for the newly accepted + * @param ex The I/O executor object to be used for the newly accepted * socket. * * @param handler The handler to be called when the accept operation @@ -1411,12 +1732,13 @@ public: * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. - * typename Protocol::socket peer // On success, the newly accepted socket. + * typename Protocol::socket::template rebind_executor< + * Executor1>::other peer // On success, the newly accepted socket. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * @code @@ -1431,25 +1753,94 @@ public: * * ... * - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... - * acceptor.async_accept(io_context2, accept_handler); + * acceptor.async_accept(my_context2, accept_handler); * @endcode */ - template + template ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, - void (asio::error_code, typename Protocol::socket)) - async_accept(asio::io_context& io_context, - ASIO_MOVE_ARG(MoveAcceptHandler) handler) + void (asio::error_code, + typename Protocol::socket::template rebind_executor< + Executor1>::other)) + async_accept(const Executor1& ex, + ASIO_MOVE_ARG(MoveAcceptHandler) handler, + typename enable_if< + is_executor::value + >::type* = 0) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a MoveAcceptHandler. - ASIO_MOVE_ACCEPT_HANDLER_CHECK(MoveAcceptHandler, - handler, typename Protocol::socket) type_check; + typedef typename Protocol::socket::template rebind_executor< + Executor1>::other other_socket_type; + + return async_initiate( + initiate_async_move_accept(), handler, this, + ex, static_cast(0), + static_cast(0)); + } - return this->get_service().async_accept(this->get_implementation(), - &io_context, static_cast(0), - ASIO_MOVE_CAST(MoveAcceptHandler)(handler)); + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * typename Protocol::socket::template rebind_executor< + * typename ExecutionContext::executor_type>::other peer + * // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * acceptor.async_accept(my_context2, accept_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other)) + async_accept(ExecutionContext& context, + ASIO_MOVE_ARG(MoveAcceptHandler) handler, + typename enable_if< + is_convertible::value + >::type* = 0) + { + typedef typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other other_socket_type; + + return async_initiate( + initiate_async_move_accept(), handler, this, + context.get_executor(), static_cast(0), + static_cast(0)); } /// Accept a new connection. @@ -1470,7 +1861,7 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::endpoint endpoint; * asio::ip::tcp::socket socket(acceptor.accept(endpoint)); @@ -1479,9 +1870,9 @@ public: typename Protocol::socket accept(endpoint_type& peer_endpoint) { asio::error_code ec; - typename Protocol::socket peer( - this->get_service().accept(this->get_implementation(), - static_cast(0), &peer_endpoint, ec)); + typename Protocol::socket peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); asio::detail::throw_error(ec, "accept"); return peer; } @@ -1505,7 +1896,7 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::endpoint endpoint; * asio::ip::tcp::socket socket(acceptor.accept(endpoint, ec)); @@ -1518,8 +1909,10 @@ public: typename Protocol::socket accept( endpoint_type& peer_endpoint, asio::error_code& ec) { - return this->get_service().accept(this->get_implementation(), - static_cast(0), &peer_endpoint, ec); + typename Protocol::socket peer(impl_.get_executor()); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + return peer; } /// Start an asynchronous accept. @@ -1543,9 +1936,9 @@ public: * typename Protocol::socket peer // On success, the newly accepted socket. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * @code @@ -1560,7 +1953,7 @@ public: * * ... * - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::endpoint endpoint; * acceptor.async_accept(endpoint, accept_handler); @@ -1572,14 +1965,11 @@ public: async_accept(endpoint_type& peer_endpoint, ASIO_MOVE_ARG(MoveAcceptHandler) handler) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a MoveAcceptHandler. - ASIO_MOVE_ACCEPT_HANDLER_CHECK(MoveAcceptHandler, - handler, typename Protocol::socket) type_check; - - return this->get_service().async_accept(this->get_implementation(), - static_cast(0), &peer_endpoint, - ASIO_MOVE_CAST(MoveAcceptHandler)(handler)); + return async_initiate( + initiate_async_move_accept(), handler, this, + impl_.get_executor(), &peer_endpoint, + static_cast(0)); } /// Accept a new connection. @@ -1591,7 +1981,7 @@ public: * This overload requires that the Protocol template parameter satisfy the * AcceptableProtocol type requirements. * - * @param io_context The io_context object to be used for the newly accepted + * @param ex The I/O executor object to be used for the newly accepted * socket. * * @param peer_endpoint An endpoint object into which the endpoint of the @@ -1603,20 +1993,70 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::endpoint endpoint; * asio::ip::tcp::socket socket( - * acceptor.accept(io_context2, endpoint)); + * acceptor.accept(my_context2, endpoint)); * @endcode */ - typename Protocol::socket accept( - asio::io_context& io_context, endpoint_type& peer_endpoint) + template + typename Protocol::socket::template rebind_executor::other + accept(const Executor1& ex, endpoint_type& peer_endpoint, + typename enable_if< + is_executor::value + >::type* = 0) + { + asio::error_code ec; + typename Protocol::socket::template + rebind_executor::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + asio::detail::throw_error(ec, "accept"); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @returns A socket object representing the newly accepted connection. + * + * @throws asio::system_error Thrown on failure. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket( + * acceptor.accept(my_context2, endpoint)); + * @endcode + */ + template + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, endpoint_type& peer_endpoint, + typename enable_if< + is_convertible::value + >::type* = 0) { asio::error_code ec; - typename Protocol::socket peer( - this->get_service().accept(this->get_implementation(), - &io_context, &peer_endpoint, ec)); + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); asio::detail::throw_error(ec, "accept"); return peer; } @@ -1630,7 +2070,7 @@ public: * This overload requires that the Protocol template parameter satisfy the * AcceptableProtocol type requirements. * - * @param io_context The io_context object to be used for the newly accepted + * @param ex The I/O executor object to be used for the newly accepted * socket. * * @param peer_endpoint An endpoint object into which the endpoint of the @@ -1643,22 +2083,79 @@ public: * * @par Example * @code - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::endpoint endpoint; * asio::ip::tcp::socket socket( - * acceptor.accept(io_context2, endpoint, ec)); + * acceptor.accept(my_context2, endpoint, ec)); * if (ec) * { * // An error occurred. * } * @endcode */ - typename Protocol::socket accept(asio::io_context& io_context, - endpoint_type& peer_endpoint, asio::error_code& ec) + template + typename Protocol::socket::template rebind_executor::other + accept(const executor_type& ex, + endpoint_type& peer_endpoint, asio::error_code& ec, + typename enable_if< + is_executor::value + >::type* = 0) + { + typename Protocol::socket::template + rebind_executor::other peer(ex); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + return peer; + } + + /// Accept a new connection. + /** + * This function is used to accept a new connection from a peer. The function + * call will block until a new connection has been accepted successfully or + * an error occurs. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. + * + * @param ec Set to indicate what error occurred, if any. + * + * @returns On success, a socket object representing the newly accepted + * connection. On error, a socket object where is_open() is false. + * + * @par Example + * @code + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * asio::ip::tcp::socket socket( + * acceptor.accept(my_context2, endpoint, ec)); + * if (ec) + * { + * // An error occurred. + * } + * @endcode + */ + template + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other + accept(ExecutionContext& context, + endpoint_type& peer_endpoint, asio::error_code& ec, + typename enable_if< + is_convertible::value + >::type* = 0) { - return this->get_service().accept(this->get_implementation(), - &io_context, &peer_endpoint, ec); + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other peer(context); + impl_.get_service().accept(impl_.get_implementation(), + peer, &peer_endpoint, ec); + return peer; } /// Start an asynchronous accept. @@ -1669,7 +2166,7 @@ public: * This overload requires that the Protocol template parameter satisfy the * AcceptableProtocol type requirements. * - * @param io_context The io_context object to be used for the newly accepted + * @param ex The I/O executor object to be used for the newly accepted * socket. * * @param peer_endpoint An endpoint object into which the endpoint of the @@ -1682,12 +2179,13 @@ public: * signature of the handler must be: * @code void handler( * const asio::error_code& error, // Result of operation. - * typename Protocol::socket peer // On success, the newly accepted socket. + * typename Protocol::socket::template rebind_executor< + * Executor1>::other peer // On success, the newly accepted socket. * ); @endcode * Regardless of whether the asynchronous operation completes immediately or - * not, the handler will not be invoked from within this function. Invocation - * of the handler will be performed in a manner equivalent to using - * asio::io_context::post(). + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). * * @par Example * @code @@ -1702,29 +2200,175 @@ public: * * ... * - * asio::ip::tcp::acceptor acceptor(io_context); + * asio::ip::tcp::acceptor acceptor(my_context); * ... * asio::ip::tcp::endpoint endpoint; - * acceptor.async_accept(io_context2, endpoint, accept_handler); + * acceptor.async_accept(my_context2, endpoint, accept_handler); * @endcode */ - template + template ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, - void (asio::error_code, typename Protocol::socket)) - async_accept(asio::io_context& io_context, - endpoint_type& peer_endpoint, - ASIO_MOVE_ARG(MoveAcceptHandler) handler) + void (asio::error_code, + typename Protocol::socket::template rebind_executor< + Executor1>::other)) + async_accept(const Executor1& ex, endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(MoveAcceptHandler) handler, + typename enable_if< + is_executor::value + >::type* = 0) { - // If you get an error on the following line it means that your handler does - // not meet the documented type requirements for a MoveAcceptHandler. - ASIO_MOVE_ACCEPT_HANDLER_CHECK(MoveAcceptHandler, - handler, typename Protocol::socket) type_check; + typedef typename Protocol::socket::template rebind_executor< + Executor1>::other other_socket_type; + + return async_initiate( + initiate_async_move_accept(), handler, this, + ex, &peer_endpoint, + static_cast(0)); + } - return this->get_service().async_accept( - this->get_implementation(), &io_context, &peer_endpoint, - ASIO_MOVE_CAST(MoveAcceptHandler)(handler)); + /// Start an asynchronous accept. + /** + * This function is used to asynchronously accept a new connection. The + * function call always returns immediately. + * + * This overload requires that the Protocol template parameter satisfy the + * AcceptableProtocol type requirements. + * + * @param context The I/O execution context object to be used for the newly + * accepted socket. + * + * @param peer_endpoint An endpoint object into which the endpoint of the + * remote peer will be written. Ownership of the peer_endpoint object is + * retained by the caller, which must guarantee that it is valid until the + * handler is called. + * + * @param handler The handler to be called when the accept operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const asio::error_code& error, // Result of operation. + * typename Protocol::socket::template rebind_executor< + * typename ExecutionContext::executor_type>::other peer + * // On success, the newly accepted socket. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. On + * immediate completion, invocation of the handler will be performed in a + * manner equivalent to using asio::post(). + * + * @par Example + * @code + * void accept_handler(const asio::error_code& error, + * asio::ip::tcp::socket peer) + * { + * if (!error) + * { + * // Accept succeeded. + * } + * } + * + * ... + * + * asio::ip::tcp::acceptor acceptor(my_context); + * ... + * asio::ip::tcp::endpoint endpoint; + * acceptor.async_accept(my_context2, endpoint, accept_handler); + * @endcode + */ + template + ASIO_INITFN_RESULT_TYPE(MoveAcceptHandler, + void (asio::error_code, + typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other)) + async_accept(ExecutionContext& context, + endpoint_type& peer_endpoint, + ASIO_MOVE_ARG(MoveAcceptHandler) handler, + typename enable_if< + is_convertible::value + >::type* = 0) + { + typedef typename Protocol::socket::template rebind_executor< + typename ExecutionContext::executor_type>::other other_socket_type; + + return async_initiate( + initiate_async_move_accept(), handler, this, + context.get_executor(), &peer_endpoint, + static_cast(0)); } #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) + +private: + // Disallow copying and assignment. + basic_socket_acceptor(const basic_socket_acceptor&) ASIO_DELETED; + basic_socket_acceptor& operator=( + const basic_socket_acceptor&) ASIO_DELETED; + + struct initiate_async_wait + { + template + void operator()(ASIO_MOVE_ARG(WaitHandler) handler, + basic_socket_acceptor* self, wait_type w) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a WaitHandler. + ASIO_WAIT_HANDLER_CHECK(WaitHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_wait( + self->impl_.get_implementation(), w, handler2.value, + self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_accept + { + template + void operator()(ASIO_MOVE_ARG(AcceptHandler) handler, + basic_socket_acceptor* self, basic_socket* peer, + endpoint_type* peer_endpoint) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a AcceptHandler. + ASIO_ACCEPT_HANDLER_CHECK(AcceptHandler, handler) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_accept( + self->impl_.get_implementation(), *peer, peer_endpoint, + handler2.value, self->impl_.get_implementation_executor()); + } + }; + + struct initiate_async_move_accept + { + template + void operator()(ASIO_MOVE_ARG(MoveAcceptHandler) handler, + basic_socket_acceptor* self, const Executor1& peer_ex, + endpoint_type* peer_endpoint, Socket*) const + { + // If you get an error on the following line it means that your handler + // does not meet the documented type requirements for a MoveAcceptHandler. + ASIO_MOVE_ACCEPT_HANDLER_CHECK( + MoveAcceptHandler, handler, Socket) type_check; + + detail::non_const_lvalue handler2(handler); + self->impl_.get_service().async_move_accept( + self->impl_.get_implementation(), peer_ex, peer_endpoint, + handler2.value, self->impl_.get_implementation_executor()); + } + }; + +#if defined(ASIO_WINDOWS_RUNTIME) + detail::io_object_impl< + detail::null_socket_service, Executor> impl_; +#elif defined(ASIO_HAS_IOCP) + detail::io_object_impl< + detail::win_iocp_socket_service, Executor> impl_; +#else + detail::io_object_impl< + detail::reactive_socket_service, Executor> impl_; +#endif }; } // namespace asio diff --git a/source/modules/hylia/link/asio/basic_socket_iostream.hpp b/source/modules/hylia/link/asio/basic_socket_iostream.hpp index 12a008be4..135f23d7c 100644 --- a/source/modules/hylia/link/asio/basic_socket_iostream.hpp +++ b/source/modules/hylia/link/asio/basic_socket_iostream.hpp @@ -2,7 +2,7 @@ // basic_socket_iostream.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -22,7 +22,6 @@ #include #include #include "asio/basic_socket_streambuf.hpp" -#include "asio/stream_socket_service.hpp" #if !defined(ASIO_HAS_VARIADIC_TEMPLATES) @@ -33,8 +32,7 @@ // explicit basic_socket_iostream(T1 x1, ..., Tn xn) // : std::basic_iostream( // &this->detail::socket_iostream_base< -// Protocol, StreamSocketService, Time, -// TimeTraits, TimerService>::streambuf_) +// Protocol, Clock, WaitTraits>::streambuf_) // { // if (rdbuf()->connect(x1, ..., xn) == 0) // this->setstate(std::ios_base::failbit); @@ -46,8 +44,7 @@ explicit basic_socket_iostream(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ : std::basic_iostream( \ &this->detail::socket_iostream_base< \ - Protocol, StreamSocketService, Time, \ - TimeTraits, TimerService>::streambuf_) \ + Protocol, Clock, WaitTraits>::streambuf_) \ { \ this->setf(std::ios_base::unitbuf); \ if (rdbuf()->connect(ASIO_VARIADIC_BYVAL_ARGS(n)) == 0) \ @@ -82,60 +79,102 @@ namespace detail { // A separate base class is used to ensure that the streambuf is initialised // prior to the basic_socket_iostream's basic_iostream base class. -template +template class socket_iostream_base { protected: - basic_socket_streambuf streambuf_; + socket_iostream_base() + { + } + +#if defined(ASIO_HAS_MOVE) + socket_iostream_base(socket_iostream_base&& other) + : streambuf_(std::move(other.streambuf_)) + { + } + + socket_iostream_base(basic_stream_socket s) + : streambuf_(std::move(s)) + { + } + + socket_iostream_base& operator=(socket_iostream_base&& other) + { + streambuf_ = std::move(other.streambuf_); + return *this; + } +#endif // defined(ASIO_HAS_MOVE) + + basic_socket_streambuf streambuf_; }; -} +} // namespace detail -/// Iostream interface for a socket. +#if !defined(ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL) +#define ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL + +// Forward declaration with defaulted arguments. template , #if defined(ASIO_HAS_BOOST_DATE_TIME) \ - || defined(GENERATING_DOCUMENTATION) - typename Time = boost::posix_time::ptime, - typename TimeTraits = asio::time_traits