| @@ -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 | |||
| @@ -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); | |||
| @@ -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 | |||
| @@ -19,10 +19,6 @@ | |||
| #include "AudioEngine.hpp" | |||
| // Make sure to define this before <cmath> is included for Windows | |||
| #define _USE_MATH_DEFINES | |||
| #include <cmath> | |||
| 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<std::mutex> lock(mEngineDataGuard); | |||
| mSharedEngineData.requestStart = true; | |||
| } | |||
| void AudioEngine::stopPlaying() | |||
| { | |||
| std::lock_guard<std::mutex> 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<std::mutex> lock(mEngineDataGuard); | |||
| std::lock_guard<std::mutex> lock(mEngineDataGuard); | |||
| mSharedEngineData.requestedTempo = tempo; | |||
| } | |||
| @@ -53,26 +68,38 @@ double AudioEngine::quantum() const | |||
| void AudioEngine::setQuantum(double quantum) | |||
| { | |||
| const std::lock_guard<std::mutex> lock(mEngineDataGuard); | |||
| std::lock_guard<std::mutex> 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 | |||
| @@ -19,13 +19,27 @@ | |||
| #pragma once | |||
| // Make sure to define this before <cmath> 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 <cmath> | |||
| #include "ableton/Link.hpp" | |||
| #include <mutex> | |||
| 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; | |||
| }; | |||
| @@ -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 <typename Clock> | |||
| 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<Clock>&) = delete; | |||
| BasicLink& operator=(const BasicLink<Clock>&) = delete; | |||
| BasicLink(BasicLink<Clock>&&) = delete; | |||
| BasicLink& operator=(BasicLink<Clock>&&) = 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 <typename Callback> | |||
| 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 <typename Callback> | |||
| 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<Clock>; | |||
| /*! @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<link::PeerCountCallback, | |||
| link::TempoCallback, | |||
| Clock, | |||
| link::platform::IoContext>; | |||
| std::mutex mCallbackMutex; | |||
| link::PeerCountCallback mPeerCountCallback; | |||
| link::TempoCallback mTempoCallback; | |||
| link::StartStopStateCallback mStartStopCallback; | |||
| Clock mClock; | |||
| Controller mController; | |||
| link::platform::Controller mController; | |||
| }; | |||
| using Link = BasicLink<link::platform::Clock>; | |||
| } // ableton | |||
| } // namespace ableton | |||
| #include <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 <typename Clock> | |||
| inline BasicLink<Clock>::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<std::mutex> lock(mCallbackMutex); | |||
| @@ -37,134 +66,134 @@ inline BasicLink<Clock>::BasicLink(const double bpm) | |||
| std::lock_guard<std::mutex> lock(mCallbackMutex); | |||
| mTempoCallback(tempo); | |||
| }, | |||
| [this](const bool isPlaying) { | |||
| std::lock_guard<std::mutex> lock(mCallbackMutex); | |||
| mStartStopCallback(isPlaying); | |||
| }, | |||
| mClock, | |||
| util::injectVal(link::platform::IoContext{})) | |||
| { | |||
| } | |||
| template <typename Clock> | |||
| inline bool BasicLink<Clock>::isEnabled() const | |||
| inline bool Link::isEnabled() const | |||
| { | |||
| return mController.isEnabled(); | |||
| } | |||
| template <typename Clock> | |||
| inline void BasicLink<Clock>::enable(const bool bEnable) | |||
| inline void Link::enable(const bool bEnable) | |||
| { | |||
| mController.enable(bEnable); | |||
| } | |||
| template <typename Clock> | |||
| inline std::size_t BasicLink<Clock>::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 <typename Clock> | |||
| template <typename Callback> | |||
| void BasicLink<Clock>::setNumPeersCallback(Callback callback) | |||
| void Link::setNumPeersCallback(Callback callback) | |||
| { | |||
| std::lock_guard<std::mutex> lock(mCallbackMutex); | |||
| mPeerCountCallback = [callback](const std::size_t numPeers) { callback(numPeers); }; | |||
| } | |||
| template <typename Clock> | |||
| template <typename Callback> | |||
| void BasicLink<Clock>::setTempoCallback(Callback callback) | |||
| void Link::setTempoCallback(Callback callback) | |||
| { | |||
| std::lock_guard<std::mutex> lock(mCallbackMutex); | |||
| mTempoCallback = [callback](const link::Tempo tempo) { callback(tempo.bpm()); }; | |||
| } | |||
| template <typename Clock> | |||
| inline Clock BasicLink<Clock>::clock() const | |||
| template <typename Callback> | |||
| void Link::setStartStopCallback(Callback callback) | |||
| { | |||
| std::lock_guard<std::mutex> lock(mCallbackMutex); | |||
| mStartStopCallback = callback; | |||
| } | |||
| inline Link::Clock Link::clock() const | |||
| { | |||
| return mClock; | |||
| } | |||
| template <typename Clock> | |||
| inline typename BasicLink<Clock>::Timeline BasicLink<Clock>::captureAudioTimeline() const | |||
| inline Link::SessionState Link::captureAudioSessionState() const | |||
| { | |||
| return BasicLink<Clock>::Timeline{mController.timelineRtSafe(), numPeers() > 0}; | |||
| return detail::toSessionState(mController.clientStateRtSafe(), numPeers() > 0); | |||
| } | |||
| template <typename Clock> | |||
| inline void BasicLink<Clock>::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 <typename Clock> | |||
| inline typename BasicLink<Clock>::Timeline BasicLink<Clock>::captureAppTimeline() const | |||
| inline Link::SessionState Link::captureAppSessionState() const | |||
| { | |||
| return Timeline{mController.timeline(), numPeers() > 0}; | |||
| return detail::toSessionState(mController.clientState(), numPeers() > 0); | |||
| } | |||
| template <typename Clock> | |||
| inline void BasicLink<Clock>::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 <typename Clock> | |||
| inline BasicLink<Clock>::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 <typename Clock> | |||
| inline double BasicLink<Clock>::Timeline::tempo() const | |||
| inline double Link::SessionState::tempo() const | |||
| { | |||
| return mTimeline.tempo.bpm(); | |||
| return mState.timeline.tempo.bpm(); | |||
| } | |||
| template <typename Clock> | |||
| inline void BasicLink<Clock>::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 <typename Clock> | |||
| inline double BasicLink<Clock>::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 <typename Clock> | |||
| inline double BasicLink<Clock>::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 <typename Clock> | |||
| inline std::chrono::microseconds BasicLink<Clock>::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 <typename Clock> | |||
| inline void BasicLink<Clock>::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<Clock>::Timeline::requestBeatAtTime( | |||
| forceBeatAtTime(beat, time, quantum); | |||
| } | |||
| template <typename Clock> | |||
| inline void BasicLink<Clock>::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<Clock>::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 | |||
| @@ -19,7 +19,7 @@ | |||
| #pragma once | |||
| #include <ableton/platforms/asio/AsioService.hpp> | |||
| #include <ableton/platforms/asio/AsioWrapper.hpp> | |||
| #include <ableton/util/Injected.hpp> | |||
| 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 | |||
| @@ -20,9 +20,9 @@ | |||
| #pragma once | |||
| #include <ableton/platforms/asio/AsioWrapper.hpp> | |||
| #if LINK_PLATFORM_MACOSX | |||
| #if defined(LINK_PLATFORM_MACOSX) | |||
| #include <ableton/platforms/darwin/Darwin.hpp> | |||
| #elif LINK_PLATFORM_LINUX | |||
| #elif defined(LINK_PLATFORM_LINUX) | |||
| #include <ableton/platforms/linux/Linux.hpp> | |||
| #endif | |||
| @@ -32,10 +32,10 @@ | |||
| #include <utility> | |||
| #include <vector> | |||
| #if LINK_PLATFORM_WINDOWS | |||
| #include <WS2tcpip.h> | |||
| #include <WinSock2.h> | |||
| #include <Windows.h> | |||
| #if defined(LINK_PLATFORM_WINDOWS) | |||
| #include <ws2tcpip.h> | |||
| #include <winsock2.h> | |||
| #include <windows.h> | |||
| #endif | |||
| namespace ableton | |||
| @@ -80,7 +80,8 @@ struct Deserialize | |||
| // Default size implementation. Works for primitive types. | |||
| template <typename T> | |||
| template <typename T, | |||
| typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr> | |||
| std::uint32_t sizeInByteStream(T) | |||
| { | |||
| return sizeof(T); | |||
| @@ -235,29 +236,53 @@ struct Deserialize<int64_t> | |||
| } | |||
| }; | |||
| // overloads for std::chrono durations | |||
| template <typename Rep, typename Ratio> | |||
| std::uint32_t sizeInByteStream(const std::chrono::duration<Rep, Ratio> dur) | |||
| // bool | |||
| inline std::uint32_t sizeInByteStream(bool) | |||
| { | |||
| return sizeof(uint8_t); | |||
| } | |||
| template <typename It> | |||
| It toNetworkByteStream(bool bl, It out) | |||
| { | |||
| return toNetworkByteStream(static_cast<uint8_t>(bl), std::move(out)); | |||
| } | |||
| template <> | |||
| struct Deserialize<bool> | |||
| { | |||
| template <typename It> | |||
| static std::pair<bool, It> fromNetworkByteStream(It begin, It end) | |||
| { | |||
| auto result = | |||
| Deserialize<uint8_t>::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 <typename Rep, typename Ratio, typename It> | |||
| It toNetworkByteStream(const std::chrono::duration<Rep, Ratio> dur, It out) | |||
| template <typename It> | |||
| 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<int64_t>(micros.count()), std::move(out)); | |||
| } | |||
| template <typename Rep, typename Ratio> | |||
| struct Deserialize<std::chrono::duration<Rep, Ratio>> | |||
| template <> | |||
| struct Deserialize<std::chrono::microseconds> | |||
| { | |||
| template <typename It> | |||
| static std::pair<std::chrono::duration<Rep, Ratio>, It> fromNetworkByteStream( | |||
| It begin, It end) | |||
| static std::pair<std::chrono::microseconds, It> fromNetworkByteStream(It begin, It end) | |||
| { | |||
| using namespace std; | |||
| auto result = Deserialize<Rep>::fromNetworkByteStream(move(begin), move(end)); | |||
| return make_pair(std::chrono::duration<Rep, Ratio>{result.first}, result.second); | |||
| auto result = Deserialize<int64_t>::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<std::array<T, Size>> | |||
| template <typename T, typename Alloc> | |||
| std::uint32_t sizeInByteStream(const std::vector<T, Alloc>& vec) | |||
| { | |||
| return detail::containerSizeInByteStream(vec); | |||
| return sizeof(uint32_t) + detail::containerSizeInByteStream(vec); | |||
| } | |||
| template <typename T, typename Alloc, typename It> | |||
| It toNetworkByteStream(const std::vector<T, Alloc>& vec, It out) | |||
| { | |||
| out = toNetworkByteStream(static_cast<uint32_t>(vec.size()), out); | |||
| return detail::containerToNetworkByteStream(vec, std::move(out)); | |||
| } | |||
| @@ -358,17 +384,42 @@ struct Deserialize<std::vector<T, Alloc>> | |||
| It bytesBegin, It bytesEnd) | |||
| { | |||
| using namespace std; | |||
| auto result_size = | |||
| Deserialize<uint32_t>::fromNetworkByteStream(move(bytesBegin), bytesEnd); | |||
| vector<T, Alloc> 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<T>(move(bytesBegin), move(bytesEnd), | |||
| back_inserter(result), static_cast<uint32_t>(distance(bytesBegin, bytesEnd))); | |||
| auto resultIt = detail::deserializeContainer<T>( | |||
| move(result_size.second), move(bytesEnd), back_inserter(result), result_size.first); | |||
| return make_pair(move(result), move(resultIt)); | |||
| } | |||
| }; | |||
| // 2-tuple | |||
| template <typename X, typename Y> | |||
| std::uint32_t sizeInByteStream(const std::tuple<X, Y>& tup) | |||
| { | |||
| return sizeInByteStream(std::get<0>(tup)) + sizeInByteStream(std::get<1>(tup)); | |||
| } | |||
| template <typename X, typename Y, typename It> | |||
| It toNetworkByteStream(const std::tuple<X, Y>& tup, It out) | |||
| { | |||
| return toNetworkByteStream( | |||
| std::get<1>(tup), toNetworkByteStream(std::get<0>(tup), std::move(out))); | |||
| } | |||
| template <typename X, typename Y> | |||
| struct Deserialize<std::tuple<X, Y>> | |||
| { | |||
| template <typename It> | |||
| static std::pair<std::tuple<X, Y>, It> fromNetworkByteStream(It begin, It end) | |||
| { | |||
| using namespace std; | |||
| auto xres = Deserialize<X>::fromNetworkByteStream(begin, end); | |||
| auto yres = Deserialize<Y>::fromNetworkByteStream(xres.second, end); | |||
| return make_pair(make_tuple(move(xres.first), move(yres.first)), move(yres.second)); | |||
| } | |||
| }; | |||
| // 3-tuple | |||
| template <typename X, typename Y, typename Z> | |||
| std::uint32_t sizeInByteStream(const std::tuple<X, Y, Z>& tup) | |||
| @@ -395,8 +446,7 @@ struct Deserialize<std::tuple<X, Y, Z>> | |||
| auto xres = Deserialize<X>::fromNetworkByteStream(begin, end); | |||
| auto yres = Deserialize<Y>::fromNetworkByteStream(xres.second, end); | |||
| auto zres = Deserialize<Z>::fromNetworkByteStream(yres.second, end); | |||
| return make_pair( | |||
| std::tuple<X, Y, Z>{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)); | |||
| } | |||
| }; | |||
| @@ -21,6 +21,7 @@ | |||
| #include <ableton/discovery/NetworkByteStreamSerializable.hpp> | |||
| #include <functional> | |||
| #include <sstream> | |||
| #include <unordered_map> | |||
| namespace ableton | |||
| @@ -21,7 +21,6 @@ | |||
| #include <ableton/discovery/UdpMessenger.hpp> | |||
| #include <ableton/discovery/v1/Messages.hpp> | |||
| #include <ableton/platforms/asio/AsioService.hpp> | |||
| #include <ableton/util/SafeAsyncHandler.hpp> | |||
| #include <memory> | |||
| @@ -216,11 +215,10 @@ PeerGateway<Messenger, PeerObserver, IoContext> makePeerGateway( | |||
| // IpV4 gateway types | |||
| template <typename StateQuery, typename IoContext> | |||
| using IpV4Messenger = | |||
| UdpMessenger<IpV4Interface<typename util::Injected<IoContext>::type&, | |||
| v1::kMaxMessageSize>, | |||
| StateQuery, | |||
| IoContext>; | |||
| using IpV4Messenger = UdpMessenger< | |||
| IpV4Interface<typename util::Injected<IoContext>::type&, v1::kMaxMessageSize>, | |||
| StateQuery, | |||
| IoContext>; | |||
| template <typename PeerObserver, typename StateQuery, typename IoContext> | |||
| using IpV4Gateway = | |||
| @@ -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 <http://www.gnu.org/licenses/>. | |||
| * | |||
| * If you would like to incorporate Link into a proprietary software application, | |||
| * please contact <link-devs@ableton.com>. | |||
| */ | |||
| #pragma once | |||
| #include <ableton/platforms/asio/AsioService.hpp> | |||
| #include <ableton/platforms/asio/AsioWrapper.hpp> | |||
| #include <ableton/util/SafeAsyncHandler.hpp> | |||
| #include <array> | |||
| #include <cassert> | |||
| namespace ableton | |||
| { | |||
| namespace discovery | |||
| { | |||
| template <std::size_t MaxPacketSize> | |||
| struct Socket | |||
| { | |||
| Socket(platforms::asio::AsioService& io) | |||
| : mpImpl(std::make_shared<Impl>(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 <typename Handler> | |||
| 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<ptrdiff_t>(numBytes)); | |||
| } | |||
| } | |||
| asio::ip::udp::socket mSocket; | |||
| asio::ip::udp::endpoint mSenderEndpoint; | |||
| using Buffer = std::array<uint8_t, MaxPacketSize>; | |||
| Buffer mReceiveBuffer; | |||
| using ByteIt = typename Buffer::const_iterator; | |||
| std::function<void(const asio::ip::udp::endpoint&, ByteIt, ByteIt)> mHandler; | |||
| }; | |||
| std::shared_ptr<Impl> mpImpl; | |||
| }; | |||
| // Configure an asio socket for receiving multicast messages | |||
| template <std::size_t MaxPacketSize> | |||
| void configureMulticastSocket(Socket<MaxPacketSize>& 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 <std::size_t MaxPacketSize> | |||
| void configureUnicastSocket( | |||
| Socket<MaxPacketSize>& 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 | |||
| @@ -169,7 +169,7 @@ private: | |||
| void setReceiveHandler(Handler handler) | |||
| { | |||
| mPeerStateHandler = [handler]( | |||
| PeerState<NodeState> state) { handler(std::move(state)); }; | |||
| PeerState<NodeState> state) { handler(std::move(state)); }; | |||
| mByeByeHandler = [handler](ByeBye<NodeId> byeBye) { handler(std::move(byeBye)); }; | |||
| } | |||
| @@ -42,8 +42,8 @@ public: | |||
| template <typename Callback, typename Tag> | |||
| void receive(Callback callback, Tag tag) | |||
| { | |||
| mCallback = [callback, tag]( | |||
| const asio::ip::udp::endpoint& from, const std::vector<uint8_t>& buffer) { | |||
| mCallback = [callback, tag](const asio::ip::udp::endpoint& from, | |||
| const std::vector<uint8_t>& buffer) { | |||
| callback(tag, from, begin(buffer), end(buffer)); | |||
| }; | |||
| } | |||
| @@ -21,6 +21,7 @@ | |||
| #include <ableton/discovery/NetworkByteStreamSerializable.hpp> | |||
| #include <cstdint> | |||
| #include <tuple> | |||
| #include <utility> | |||
| 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<std::uint64_t> 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<std::uint64_t>; | |||
| using FoobarTuple = std::tuple<FoobarVector, FoobarVector>; | |||
| FoobarVector fooVals; | |||
| FoobarVector barVals; | |||
| friend std::uint32_t sizeInByteStream(const Foobar& foobar) | |||
| { | |||
| return discovery::sizeInByteStream(foobar.asTuple()); | |||
| } | |||
| template <typename It> | |||
| friend It toNetworkByteStream(const Foobar& foobar, It out) | |||
| { | |||
| return discovery::toNetworkByteStream(foobar.asTuple(), out); | |||
| } | |||
| template <typename It> | |||
| static std::pair<Foobar, It> fromNetworkByteStream(It begin, It end) | |||
| { | |||
| const auto result = | |||
| Deserialize<FoobarTuple>::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 | |||
| @@ -53,7 +53,9 @@ public: | |||
| void receive(Handler handler) | |||
| { | |||
| mCallback = [handler](const asio::ip::udp::endpoint& from, | |||
| const std::vector<uint8_t>& buffer) { handler(from, begin(buffer), end(buffer)); }; | |||
| const std::vector<uint8_t>& buffer) { | |||
| handler(from, begin(buffer), end(buffer)); | |||
| }; | |||
| } | |||
| template <typename It> | |||
| @@ -19,6 +19,7 @@ | |||
| #pragma once | |||
| #include <ableton/link/Optional.hpp> | |||
| #include <array> | |||
| #include <atomic> | |||
| #include <cassert> | |||
| @@ -34,12 +35,6 @@ template <typename Type, std::size_t size> | |||
| class CircularFifo | |||
| { | |||
| public: | |||
| struct PoppedItem | |||
| { | |||
| Type item; | |||
| bool valid; | |||
| }; | |||
| CircularFifo() | |||
| : tail(0) | |||
| , head(0) | |||
| @@ -60,32 +55,17 @@ public: | |||
| return false; | |||
| } | |||
| PoppedItem pop() | |||
| Optional<Type> 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<Type>{std::move(item)}; | |||
| } | |||
| bool isEmpty() const | |||
| @@ -109,5 +89,5 @@ private: | |||
| std::array<Type, size + 1> data; | |||
| }; | |||
| } // link | |||
| } // ableton | |||
| } // namespace link | |||
| } // namespace ableton | |||
| @@ -111,5 +111,5 @@ inline Timeline shiftClientTimeline(Timeline client, const Beats shift) | |||
| return client; | |||
| } | |||
| } // link | |||
| } // ableton | |||
| } // namespace link | |||
| } // namespace ableton | |||
| @@ -26,7 +26,9 @@ | |||
| #include <ableton/link/GhostXForm.hpp> | |||
| #include <ableton/link/NodeState.hpp> | |||
| #include <ableton/link/Peers.hpp> | |||
| #include <ableton/link/SessionState.hpp> | |||
| #include <ableton/link/Sessions.hpp> | |||
| #include <ableton/link/StartStopState.hpp> | |||
| #include <mutex> | |||
| namespace ableton | |||
| @@ -44,20 +46,79 @@ GhostXForm initXForm(const Clock& clock) | |||
| return {1.0, -clock.micros()}; | |||
| } | |||
| template <typename Clock> | |||
| 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<void(std::size_t)>; | |||
| using TempoCallback = std::function<void(ableton::link::Tempo)>; | |||
| using StartStopStateCallback = std::function<void(bool)>; | |||
| // The main Link controller | |||
| template <typename PeerCountCallback, | |||
| typename TempoCallback, | |||
| typename StartStopStateCallback, | |||
| typename Clock, | |||
| typename IoContext> | |||
| class Controller | |||
| @@ -66,33 +127,38 @@ public: | |||
| Controller(Tempo tempo, | |||
| PeerCountCallback peerCallback, | |||
| TempoCallback tempoCallback, | |||
| StartStopStateCallback startStopStateCallback, | |||
| Clock clock, | |||
| util::Injected<IoContext> 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<std::mutex> lock(mClientTimelineGuard); | |||
| return mClientTimeline; | |||
| std::lock_guard<std::mutex> 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<std::mutex> lock(mClientTimelineGuard); | |||
| mClientTimeline = newTimeline; | |||
| std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> lock(mSessionTimingGuard); | |||
| mSessionTimeline = newTimeline; | |||
| mGhostXForm = newXForm; | |||
| std::lock_guard<std::mutex> lock(mSessionStateGuard); | |||
| mSessionState.timeline = newTimeline; | |||
| mSessionState.ghostXForm = newXForm; | |||
| } | |||
| // Update the client timeline based on the new session timing data | |||
| { | |||
| std::lock_guard<std::mutex> lock(mClientTimelineGuard); | |||
| mClientTimeline = updateClientTimelineFromSession( | |||
| mClientTimeline, mSessionTimeline, mClock.micros(), mGhostXForm); | |||
| std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(mClientTimelineGuard); | |||
| mClientTimeline = timeline; | |||
| std::lock_guard<std::mutex> 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::function<void()>, | |||
| std::chrono::milliseconds>; | |||
| using RtTimeline = std::pair<Timeline, std::chrono::microseconds>; | |||
| 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<RtTimeline, 16> mFifo; | |||
| std::atomic<bool> mHasPendingTimelines; | |||
| // and the ioService, buffering 16 client states allows to set eight client states | |||
| // per ms. | |||
| static const std::size_t kBufferSize = 16; | |||
| CircularFifo<IncomingClientState, kBufferSize> 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<IoContext>::type; | |||
| using ControllerPeers = | |||
| Peers<IoType&, std::reference_wrapper<SessionPeerCounter>, SessionTimelineCallback>; | |||
| using ControllerPeers = Peers<IoType&, | |||
| std::reference_wrapper<SessionPeerCounter>, | |||
| SessionTimelineCallback, | |||
| SessionStartStopStateCallback>; | |||
| using ControllerGateway = | |||
| Gateway<typename ControllerPeers::GatewayObserver, Clock, IoType&>; | |||
| @@ -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<bool> mHasPendingRtClientStates; | |||
| SessionPeerCounter mSessionPeerCounter; | |||
| std::atomic<bool> mEnabled; | |||
| std::atomic<bool> mStartStopSyncEnabled; | |||
| util::Injected<IoContext> mIo; | |||
| RtTimelineSetter mRtTimelineSetter; | |||
| RtClientStateSetter mRtClientStateSetter; | |||
| ControllerPeers mPeers; | |||
| @@ -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<IoContext> mIo; | |||
| MeasurementService<Clock, typename util::Injected<IoContext>::type::Log> mMeasurement; | |||
| MeasurementService<Clock, typename std::remove_reference<IoContext>::type> mMeasurement; | |||
| discovery:: | |||
| IpV4Gateway<PeerObserver, PeerState, typename util::Injected<IoContext>::type&> | |||
| mPeerGateway; | |||
| @@ -20,13 +20,12 @@ | |||
| #pragma once | |||
| #include <ableton/discovery/Payload.hpp> | |||
| #include <ableton/discovery/Socket.hpp> | |||
| #include <ableton/link/PayloadEntries.hpp> | |||
| #include <ableton/link/PeerState.hpp> | |||
| #include <ableton/link/SessionId.hpp> | |||
| #include <ableton/link/v1/Messages.hpp> | |||
| #include <ableton/platforms/asio/AsioService.hpp> | |||
| #include <ableton/util/Injected.hpp> | |||
| #include <ableton/util/SafeAsyncHandler.hpp> | |||
| #include <chrono> | |||
| #include <memory> | |||
| @@ -35,83 +34,57 @@ namespace ableton | |||
| namespace link | |||
| { | |||
| template <typename IoService, typename Clock, typename Socket, typename Log> | |||
| template <typename Clock, typename IoContext> | |||
| struct Measurement | |||
| { | |||
| using Point = std::pair<double, double>; | |||
| using Callback = std::function<void(std::vector<Point>)>; | |||
| 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> log) | |||
| : mpIo(new IoService{}) | |||
| , mpImpl(std::make_shared<Impl>(*mpIo, | |||
| std::move(state), | |||
| IoContext io) | |||
| : mIo(std::move(io)) | |||
| , mpImpl(std::make_shared<Impl>(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> | |||
| { | |||
| Impl(IoService& io, | |||
| const PeerState& state, | |||
| using Socket = typename IoContext::template Socket<v1::kMaxMessageSize>; | |||
| 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> log) | |||
| : mpSocket(std::make_shared<Socket>(io)) | |||
| IoContext& io) | |||
| : mSocket(io.template openUnicastSocket<v1::kMaxMessageSize>(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<Point>{}); | |||
| mData = {}; | |||
| debug(*mLog) << "Measuring " << mEndpoint << " failed."; | |||
| debug(mLog) << "Measuring " << mEndpoint << " failed."; | |||
| } | |||
| std::shared_ptr<Socket> mpSocket; | |||
| Socket mSocket; | |||
| SessionId mSessionId; | |||
| asio::ip::udp::endpoint mEndpoint; | |||
| std::vector<std::pair<double, double>> mData; | |||
| Callback mCallback; | |||
| Clock mClock; | |||
| util::Injected<typename IoService::Timer> mTimer; | |||
| Timer mTimer; | |||
| std::size_t mMeasurementsStarted; | |||
| util::Injected<Log> mLog; | |||
| Log mLog; | |||
| bool mSuccess; | |||
| }; | |||
| @@ -288,7 +261,7 @@ struct Measurement | |||
| std::shared_ptr<Impl> mpImpl; | |||
| }; | |||
| std::unique_ptr<IoService> mpIo; | |||
| IoContext mIo; | |||
| std::shared_ptr<Impl> mpImpl; | |||
| }; | |||
| @@ -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) | |||
| @@ -19,7 +19,6 @@ | |||
| #pragma once | |||
| #include <ableton/discovery/Socket.hpp> | |||
| #include <ableton/link/GhostXForm.hpp> | |||
| #include <ableton/link/Kalman.hpp> | |||
| #include <ableton/link/LinearRegression.hpp> | |||
| @@ -28,48 +27,34 @@ | |||
| #include <ableton/link/PingResponder.hpp> | |||
| #include <ableton/link/SessionId.hpp> | |||
| #include <ableton/link/v1/Messages.hpp> | |||
| #include <ableton/platforms/asio/AsioService.hpp> | |||
| #include <ableton/util/Log.hpp> | |||
| #include <map> | |||
| #include <memory> | |||
| #include <thread> | |||
| namespace ableton | |||
| { | |||
| namespace link | |||
| { | |||
| template <typename Clock, typename Log> | |||
| template <typename Clock, typename IoContext> | |||
| class MeasurementService | |||
| { | |||
| public: | |||
| using IoType = util::Injected<IoContext>; | |||
| using Point = std::pair<double, double>; | |||
| using MeasurementInstance = Measurement<platforms::asio::AsioService, | |||
| Clock, | |||
| discovery::Socket<v1::kMaxMessageSize>, | |||
| Log>; | |||
| using MeasurementServicePingResponder = PingResponder<platforms::asio::AsioService&, | |||
| Clock, | |||
| discovery::Socket<v1::kMaxMessageSize>, | |||
| Log>; | |||
| static const std::size_t kNumberThreads = 1; | |||
| using MeasurementInstance = Measurement<Clock, IoContext>; | |||
| MeasurementService(asio::ip::address_v4 address, | |||
| SessionId sessionId, | |||
| GhostXForm ghostXForm, | |||
| Clock clock, | |||
| util::Injected<Log> 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<Handler>{*this, nodeId, handler}; | |||
| try | |||
| { | |||
| mMeasurementMap[nodeId] = | |||
| MeasurementInstance{state, move(callback), move(addr), mClock, mLog}; | |||
| std::unique_ptr<MeasurementInstance>(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<NodeId, MeasurementInstance>; | |||
| using MeasurementMap = std::map<NodeId, std::unique_ptr<MeasurementInstance>>; | |||
| MeasurementMap mMeasurementMap; | |||
| Clock mClock; | |||
| util::Injected<Log> mLog; | |||
| platforms::asio::AsioService mIo; | |||
| MeasurementServicePingResponder mPingResponder; | |||
| IoType mIo; | |||
| PingResponder<Clock, IoContext> mPingResponder; | |||
| }; | |||
| } // namespace link | |||
| @@ -22,6 +22,7 @@ | |||
| #include <ableton/discovery/Payload.hpp> | |||
| #include <ableton/link/NodeId.hpp> | |||
| #include <ableton/link/SessionId.hpp> | |||
| #include <ableton/link/StartStopState.hpp> | |||
| #include <ableton/link/Timeline.hpp> | |||
| 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 <typename It> | |||
| 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<Timeline, SessionMembership>(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<Timeline, SessionMembership, StartStopState>(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 | |||
| @@ -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 <http://www.gnu.org/licenses/>. | |||
| * | |||
| * If you would like to incorporate Link into a proprietary software application, | |||
| * please contact <link-devs@ableton.com>. | |||
| */ | |||
| #pragma once | |||
| #include <cassert> | |||
| #include <utility> | |||
| namespace ableton | |||
| { | |||
| namespace link | |||
| { | |||
| // Subset of the C++ 17 std::optional API. T has to be default constructible. | |||
| template <typename T> | |||
| 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 | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -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 IoContext, | |||
| typename SessionMembershipCallback, | |||
| typename SessionTimelineCallback> | |||
| typename SessionTimelineCallback, | |||
| typename SessionStartStopStateCallback> | |||
| class Peers | |||
| { | |||
| // non-movable private implementation type | |||
| @@ -47,9 +51,10 @@ public: | |||
| Peers(util::Injected<IoContext> io, | |||
| SessionMembershipCallback membership, | |||
| SessionTimelineCallback timeline) | |||
| : mpImpl( | |||
| std::make_shared<Impl>(std::move(io), std::move(membership), std::move(timeline))) | |||
| SessionTimelineCallback timeline, | |||
| SessionStartStopStateCallback startStop) | |||
| : mpImpl(std::make_shared<Impl>( | |||
| std::move(io), std::move(membership), std::move(timeline), std::move(startStop))) | |||
| { | |||
| } | |||
| @@ -196,10 +201,12 @@ private: | |||
| { | |||
| Impl(util::Injected<IoContext> 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 <typename Predicate> | |||
| 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<IoContext> mIo; | |||
| SessionMembershipCallback mSessionMembershipCallback; | |||
| SessionTimelineCallback mSessionTimelineCallback; | |||
| SessionStartStopStateCallback mSessionStartStopStateCallback; | |||
| std::vector<Peer> mPeers; // sorted by peerId, unique by (peerId, addr) | |||
| }; | |||
| @@ -342,13 +378,19 @@ private: | |||
| template <typename Io, | |||
| typename SessionMembershipCallback, | |||
| typename SessionTimelineCallback> | |||
| Peers<Io, SessionMembershipCallback, SessionTimelineCallback> makePeers( | |||
| util::Injected<Io> io, | |||
| typename SessionTimelineCallback, | |||
| typename SessionStartStopStateCallback> | |||
| Peers<Io, | |||
| SessionMembershipCallback, | |||
| SessionTimelineCallback, | |||
| SessionStartStopStateCallback> | |||
| makePeers(util::Injected<Io> 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 | |||
| @@ -96,5 +96,5 @@ inline std::chrono::microseconds fromPhaseEncodedBeats( | |||
| return tl.fromBeats(tl.beatOrigin + originOffset + quantum - inversePhaseOffset); | |||
| } | |||
| } // link | |||
| } // ableton | |||
| } // namespace link | |||
| } // namespace ableton | |||
| @@ -23,35 +23,34 @@ | |||
| #include <ableton/link/PayloadEntries.hpp> | |||
| #include <ableton/link/SessionId.hpp> | |||
| #include <ableton/link/v1/Messages.hpp> | |||
| #include <ableton/platforms/asio/AsioWrapper.hpp> | |||
| #include <ableton/util/Injected.hpp> | |||
| #include <ableton/util/SafeAsyncHandler.hpp> | |||
| #include <chrono> | |||
| #include <memory> | |||
| #include <thread> | |||
| namespace ableton | |||
| { | |||
| namespace link | |||
| { | |||
| template <typename Io, typename Clock, typename Socket, typename Log> | |||
| template <typename Clock, typename IoContext> | |||
| class PingResponder | |||
| { | |||
| using IoType = util::Injected<IoContext&>; | |||
| using Socket = typename IoType::type::template Socket<v1::kMaxMessageSize>; | |||
| public: | |||
| PingResponder(asio::ip::address_v4 address, | |||
| SessionId sessionId, | |||
| GhostXForm ghostXForm, | |||
| util::Injected<Io> io, | |||
| Clock clock, | |||
| util::Injected<Log> log) | |||
| : mIo(std::move(io)) | |||
| , mpImpl(std::make_shared<Impl>(*mIo, | |||
| std::move(address), | |||
| IoType io) | |||
| : mIo(io) | |||
| , mpImpl(std::make_shared<Impl>(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> | |||
| { | |||
| Impl(typename util::Injected<Io>::type& io, | |||
| asio::ip::address_v4 address, | |||
| Impl(asio::ip::address_v4 address, | |||
| SessionId sessionId, | |||
| GhostXForm ghostXForm, | |||
| Clock clock, | |||
| util::Injected<Log> 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<v1::kMaxMessageSize>(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<Log> mLog; | |||
| typename IoType::type::Log mLog; | |||
| Socket mSocket; | |||
| }; | |||
| util::Injected<Io> mIo; | |||
| IoType mIo; | |||
| std::shared_ptr<Impl> mpImpl; | |||
| }; | |||
| @@ -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) | |||
| @@ -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 <http://www.gnu.org/licenses/>. | |||
| * | |||
| * If you would like to incorporate Link into a proprietary software application, | |||
| * please contact <link-devs@ableton.com>. | |||
| */ | |||
| #pragma once | |||
| #include <ableton/link/Optional.hpp> | |||
| #include <ableton/link/StartStopState.hpp> | |||
| #include <ableton/link/Timeline.hpp> | |||
| namespace ableton | |||
| { | |||
| namespace link | |||
| { | |||
| using OptionalTimeline = Optional<Timeline>; | |||
| using OptionalStartStopState = Optional<StartStopState>; | |||
| 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 | |||
| @@ -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; | |||
| @@ -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 <http://www.gnu.org/licenses/>. | |||
| * | |||
| * If you would like to incorporate Link into a proprietary software application, | |||
| * please contact <link-devs@ableton.com>. | |||
| */ | |||
| #pragma once | |||
| #include <ableton/discovery/NetworkByteStreamSerializable.hpp> | |||
| #include <ableton/link/Beats.hpp> | |||
| #include <chrono> | |||
| #include <tuple> | |||
| 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<bool, Beats, std::chrono::microseconds>; | |||
| 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 <typename It> | |||
| friend It toNetworkByteStream(const StartStopState& state, It out) | |||
| { | |||
| return discovery::toNetworkByteStream(state.asTuple(), std::move(out)); | |||
| } | |||
| template <typename It> | |||
| static std::pair<StartStopState, It> fromNetworkByteStream(It begin, It end) | |||
| { | |||
| using namespace std; | |||
| using namespace discovery; | |||
| auto result = | |||
| Deserialize<StartStopStateTuple>::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 | |||
| @@ -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 | |||
| { | |||
| @@ -22,15 +22,15 @@ | |||
| #include <ableton/link/Controller.hpp> | |||
| #include <ableton/util/Log.hpp> | |||
| #if LINK_PLATFORM_WINDOWS | |||
| #if defined(LINK_PLATFORM_WINDOWS) | |||
| #include <ableton/platforms/asio/Context.hpp> | |||
| #include <ableton/platforms/windows/Clock.hpp> | |||
| #include <ableton/platforms/windows/ScanIpIfAddrs.hpp> | |||
| #elif LINK_PLATFORM_MACOSX | |||
| #elif defined(LINK_PLATFORM_MACOSX) | |||
| #include <ableton/platforms/asio/Context.hpp> | |||
| #include <ableton/platforms/darwin/Clock.hpp> | |||
| #include <ableton/platforms/posix/ScanIpIfAddrs.hpp> | |||
| #elif LINK_PLATFORM_LINUX | |||
| #elif defined(LINK_PLATFORM_LINUX) | |||
| #include <ableton/platforms/asio/Context.hpp> | |||
| #include <ableton/platforms/linux/Clock.hpp> | |||
| #include <ableton/platforms/posix/ScanIpIfAddrs.hpp> | |||
| @@ -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<platforms::windows::ScanIpIfAddrs, util::NullLog>; | |||
| #elif LINK_PLATFORM_MACOSX | |||
| #elif defined(LINK_PLATFORM_MACOSX) | |||
| using Clock = platforms::darwin::Clock; | |||
| using IoContext = | |||
| platforms::asio::Context<platforms::posix::ScanIpIfAddrs, util::NullLog>; | |||
| #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<platforms::posix::ScanIpIfAddrs, util::NullLog>; | |||
| #endif | |||
| using Controller = Controller<PeerCountCallback, TempoCallback, Clock, IoContext>; | |||
| using Controller = | |||
| Controller<PeerCountCallback, TempoCallback, StartStopStateCallback, Clock, IoContext>; | |||
| } // platform | |||
| } // link | |||
| } // ableton | |||
| } // namespace platform | |||
| } // namespace link | |||
| } // namespace ableton | |||
| @@ -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 <http://www.gnu.org/licenses/>. | |||
| * | |||
| * If you would like to incorporate Link into a proprietary software application, | |||
| * please contact <link-devs@ableton.com>. | |||
| */ | |||
| #pragma once | |||
| #include <ableton/platforms/asio/AsioTimer.hpp> | |||
| #include <ableton/platforms/asio/AsioWrapper.hpp> | |||
| #include <thread> | |||
| namespace ableton | |||
| { | |||
| namespace platforms | |||
| { | |||
| namespace asio | |||
| { | |||
| class AsioService | |||
| { | |||
| public: | |||
| using Timer = AsioTimer; | |||
| AsioService() | |||
| : AsioService(DefaultHandler{}) | |||
| { | |||
| } | |||
| template <typename ExceptionHandler> | |||
| 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 <typename Handler> | |||
| 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 | |||
| @@ -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 <asio.hpp> | |||
| #include <asio/system_timer.hpp> | |||
| #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__) | |||
| @@ -85,5 +85,5 @@ private: | |||
| std::thread mThread; | |||
| }; | |||
| } // platforms | |||
| } // ableton | |||
| } // namespace platforms | |||
| } // namespace ableton | |||
| @@ -38,6 +38,6 @@ AsioAddrType makeAddress(const char* pAddr) | |||
| return AsioAddrType{bytes}; | |||
| } | |||
| } // asio | |||
| } // platforms | |||
| } // ableton | |||
| } // namespace asio | |||
| } // namespace platforms | |||
| } // namespace ableton | |||
| @@ -49,17 +49,7 @@ public: | |||
| }; | |||
| using ClockMonotonic = Clock<CLOCK_MONOTONIC>; | |||
| #ifdef CLOCK_MONOTONIC_RAW | |||
| using ClockMonotonicRaw = Clock<CLOCK_MONOTONIC_RAW>; | |||
| #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<CLOCK_REALTIME>; | |||
| } // namespace linux | |||
| } // namespace platforms | |||
| @@ -67,7 +67,7 @@ private: | |||
| struct ifaddrs* interfaces = NULL; | |||
| }; | |||
| } // detail | |||
| } // namespace detail | |||
| // Posix implementation of ip interface address scanner | |||
| @@ -93,7 +93,7 @@ private: | |||
| IP_ADAPTER_ADDRESSES* adapter; | |||
| }; | |||
| } // detail | |||
| } // namespace detail | |||
| struct ScanIpIfAddrs | |||
| { | |||
| @@ -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 <catch.hpp> | |||
| // Visual Studio | |||
| #if defined(_MSC_VER) | |||
| #pragma warning(pop) | |||
| #endif | |||
| @@ -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 <http://www.gnu.org/licenses/>. | |||
| * | |||
| * If you would like to incorporate Link into a proprietary software application, | |||
| * please contact <link-devs@ableton.com>. | |||
| */ | |||
| #pragma once | |||
| #include <ableton/platforms/AsioWrapper.hpp> | |||
| #include <functional> | |||
| namespace ableton | |||
| { | |||
| namespace test | |||
| { | |||
| namespace serial_io | |||
| { | |||
| struct Socket | |||
| { | |||
| using SendFn = std::function<std::size_t( | |||
| const uint8_t* const, const size_t, const asio::ip::udp::endpoint&)>; | |||
| struct ReceiveHandler | |||
| { | |||
| template <typename It> | |||
| void operator()(const asio::ip::udp::endpoint& from, const It begin, const It end) | |||
| { | |||
| std::vector<uint8_t> buffer{begin, end}; | |||
| mReceive(from, buffer); | |||
| } | |||
| std::function<const asio::ip::udp::endpoint&, const std::vector<uint8_t>&> mReceive; | |||
| }; | |||
| using ReceiveFn = std::function<void(ReceiveHandler)>; | |||
| 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 <typename Handler> | |||
| void receive(Handler handler) | |||
| { | |||
| mReceiveFn(ReceiveHandler{ | |||
| [handler](const asio::ip::udp::endpoint& from, const std::vector<uint8_t>& 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 | |||
| @@ -48,5 +48,5 @@ struct SampleTiming | |||
| double mSampleRate; | |||
| }; | |||
| } // util | |||
| } // ableton | |||
| } // namespace util | |||
| } // namespace ableton | |||
| @@ -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" | |||
| @@ -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<T, Allocator>::get(t, a); | |||
| } | |||
| #if defined(ASIO_HAS_ALIAS_TEMPLATES) | |||
| template <typename T, typename Allocator = std::allocator<void> > | |||
| using associated_allocator_t | |||
| = typename associated_allocator<T, Allocator>::type; | |||
| #endif // defined(ASIO_HAS_ALIAS_TEMPLATES) | |||
| } // namespace asio | |||
| #include "asio/detail/pop_options.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 <typename T, typename Executor = system_executor> | |||
| using associated_executor_t = typename associated_executor<T, Executor>::type; | |||
| #endif // defined(ASIO_HAS_ALIAS_TEMPLATES) | |||
| } // namespace asio | |||
| #include "asio/detail/pop_options.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 <typename Handler> | |||
| template <typename CompletionToken, typename Signature> | |||
| 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 <typename Initiation, typename RawCompletionToken, typename... Args> | |||
| 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 <typename Initiation, typename RawCompletionToken> | |||
| 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 <typename Initiation, typename RawCompletionToken, \ | |||
| ASIO_VARIADIC_TPARAMS(n)> \ | |||
| 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 <typename Handler, typename Signature> | |||
| /// 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 <typename CompletionToken, typename Signature> | |||
| 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<CompletionToken>::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<typename conditional< | |||
| is_same<Handler, handler_type>::value, | |||
| handler_type&, Handler&&>::type>(orig_handler)), | |||
| result(handler) | |||
| explicit async_completion(CompletionToken& token) | |||
| : completion_handler(static_cast<typename conditional< | |||
| is_same<CompletionToken, completion_handler_type>::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<CompletionToken>::type& token) | |||
| : completion_handler(token), | |||
| result(completion_handler) | |||
| { | |||
| } | |||
| explicit async_completion(const typename decay<CompletionToken>::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<Handler, handler_type>::value, | |||
| handler_type&, handler_type>::type handler; | |||
| is_same<CompletionToken, completion_handler_type>::value, | |||
| completion_handler_type&, completion_handler_type>::type completion_handler; | |||
| #else // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | |||
| typename asio::handler_type<Handler, Signature>::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<typename asio::handler_type< | |||
| Handler, Signature>::type> result; | |||
| async_result<typename decay<CompletionToken>::type, Signature> result; | |||
| }; | |||
| namespace detail { | |||
| template <typename Handler, typename Signature> | |||
| struct async_result_type_helper | |||
| template <typename CompletionToken, typename Signature> | |||
| struct async_result_helper | |||
| : async_result<typename decay<CompletionToken>::type, Signature> | |||
| { | |||
| typedef typename async_result< | |||
| typename handler_type<Handler, Signature>::type | |||
| >::type type; | |||
| }; | |||
| } // namespace detail | |||
| } // namespace asio | |||
| struct async_result_memfns_base | |||
| { | |||
| void initiate(); | |||
| }; | |||
| #include "asio/detail/pop_options.hpp" | |||
| template <typename T> | |||
| struct async_result_memfns_derived | |||
| : T, async_result_memfns_base | |||
| { | |||
| }; | |||
| template <typename T, T> | |||
| struct async_result_memfns_check | |||
| { | |||
| }; | |||
| template <typename> | |||
| char (&async_result_initiate_memfn_helper(...))[2]; | |||
| template <typename T> | |||
| char async_result_initiate_memfn_helper( | |||
| async_result_memfns_check< | |||
| void (async_result_memfns_base::*)(), | |||
| &async_result_memfns_derived<T>::initiate>*); | |||
| template <typename CompletionToken, typename Signature> | |||
| struct async_result_has_initiate_memfn | |||
| : integral_constant<bool, sizeof(async_result_initiate_memfn_helper< | |||
| async_result<typename decay<CompletionToken>::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<h, sig>::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<ct>::type, sig>::return_type | |||
| #define ASIO_HANDLER_TYPE(ct, sig) \ | |||
| typename ::asio::async_result< \ | |||
| typename ::asio::handler_type<h, sig>::type>::type | |||
| typename ::asio::decay<ct>::type, sig>::completion_handler_type | |||
| #endif | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| template <typename CompletionToken, typename Signature, | |||
| typename Initiation, typename... Args> | |||
| 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 <typename CompletionToken, typename Signature, | |||
| typename Initiation, typename... Args> | |||
| 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_MOVE_ARG(Args)... args) | |||
| { | |||
| return async_result<typename decay<CompletionToken>::type, | |||
| Signature>::initiate(ASIO_MOVE_CAST(Initiation)(initiation), | |||
| ASIO_MOVE_CAST(CompletionToken)(token), | |||
| ASIO_MOVE_CAST(Args)(args)...); | |||
| } | |||
| template <typename CompletionToken, typename Signature, | |||
| typename Initiation, typename... Args> | |||
| 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_MOVE_ARG(Args)... args) | |||
| { | |||
| async_completion<CompletionToken, Signature> 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 <typename CompletionToken, typename Signature, typename Initiation> | |||
| 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) | |||
| { | |||
| return async_result<typename decay<CompletionToken>::type, | |||
| Signature>::initiate(ASIO_MOVE_CAST(Initiation)(initiation), | |||
| ASIO_MOVE_CAST(CompletionToken)(token)); | |||
| } | |||
| template <typename CompletionToken, typename Signature, typename Initiation> | |||
| 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) | |||
| { | |||
| async_completion<CompletionToken, Signature> 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 <typename CompletionToken, typename Signature, \ | |||
| typename Initiation, ASIO_VARIADIC_TPARAMS(n)> \ | |||
| 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<typename decay<CompletionToken>::type, \ | |||
| Signature>::initiate(ASIO_MOVE_CAST(Initiation)(initiation), \ | |||
| ASIO_MOVE_CAST(CompletionToken)(token), \ | |||
| ASIO_VARIADIC_MOVE_ARGS(n)); \ | |||
| } \ | |||
| \ | |||
| template <typename CompletionToken, typename Signature, \ | |||
| typename Initiation, ASIO_VARIADIC_TPARAMS(n)> \ | |||
| 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<CompletionToken, Signature> 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 | |||
| @@ -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 <experimental/coroutine> | |||
| #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 <typename> class awaitable_thread; | |||
| template <typename, typename> class awaitable_frame; | |||
| } // namespace detail | |||
| /// The return type of a coroutine or asynchronous operation. | |||
| template <typename T, typename Executor = executor> | |||
| 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 <class U> | |||
| void await_suspend( | |||
| detail::coroutine_handle<detail::awaitable_frame<U, Executor>> h) | |||
| { | |||
| frame_->push_frame(&h.promise()); | |||
| } | |||
| // Support for co_await keyword. | |||
| T await_resume() | |||
| { | |||
| return frame_->get(); | |||
| } | |||
| #endif // !defined(GENERATING_DOCUMENTATION) | |||
| private: | |||
| template <typename> friend class detail::awaitable_thread; | |||
| template <typename, typename> 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<T, Executor>* a) | |||
| : frame_(a) | |||
| { | |||
| } | |||
| detail::awaitable_frame<T, Executor>* 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 | |||
| @@ -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 <cstddef> | |||
| #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 <typename Protocol, typename Executor = executor> | |||
| 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 <typename Protocol, | |||
| typename DatagramSocketService = datagram_socket_service<Protocol> > | |||
| template <typename Protocol, typename Executor> | |||
| class basic_datagram_socket | |||
| : public basic_socket<Protocol, DatagramSocketService> | |||
| : public basic_socket<Protocol, Executor> | |||
| { | |||
| public: | |||
| /// The type of the executor associated with the object. | |||
| typedef Executor executor_type; | |||
| /// Rebinds the socket type to another executor. | |||
| template <typename Executor1> | |||
| struct rebind_executor | |||
| { | |||
| /// The socket type when rebound to the specified executor. | |||
| typedef basic_datagram_socket<Protocol, Executor1> 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<Protocol, | |||
| Executor>::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<Protocol, Executor>(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<Protocol, DatagramSocketService>(io_context) | |||
| template <typename ExecutionContext> | |||
| explicit basic_datagram_socket(ExecutionContext& context, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, Executor>(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<Protocol, DatagramSocketService>(io_context, protocol) | |||
| template <typename ExecutionContext> | |||
| basic_datagram_socket(ExecutionContext& context, | |||
| const protocol_type& protocol, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, DatagramSocketService>(io_context, endpoint) | |||
| basic_datagram_socket(const executor_type& ex, const endpoint_type& endpoint) | |||
| : basic_socket<Protocol, Executor>(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 <typename ExecutionContext> | |||
| basic_datagram_socket(ExecutionContext& context, | |||
| const endpoint_type& endpoint, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, DatagramSocketService>( | |||
| io_context, protocol, native_socket) | |||
| : basic_socket<Protocol, Executor>(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 <typename ExecutionContext> | |||
| basic_datagram_socket(ExecutionContext& context, | |||
| const protocol_type& protocol, const native_handle_type& native_socket, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, DatagramSocketService>( | |||
| ASIO_MOVE_CAST(basic_datagram_socket)(other)) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, DatagramSocketService>::operator=( | |||
| ASIO_MOVE_CAST(basic_datagram_socket)(other)); | |||
| basic_socket<Protocol, Executor>::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 <typename Protocol1, typename DatagramSocketService1> | |||
| basic_datagram_socket( | |||
| basic_datagram_socket<Protocol1, DatagramSocketService1>&& other, | |||
| typename enable_if<is_convertible<Protocol1, Protocol>::value>::type* = 0) | |||
| : basic_socket<Protocol, DatagramSocketService>( | |||
| ASIO_MOVE_CAST2(basic_datagram_socket< | |||
| Protocol1, DatagramSocketService1>)(other)) | |||
| template <typename Protocol1, typename Executor1> | |||
| basic_datagram_socket(basic_datagram_socket<Protocol1, Executor1>&& other, | |||
| typename enable_if< | |||
| is_convertible<Protocol1, Protocol>::value | |||
| && is_convertible<Executor1, Executor>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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 Protocol1, typename DatagramSocketService1> | |||
| typename enable_if<is_convertible<Protocol1, Protocol>::value, | |||
| basic_datagram_socket>::type& operator=( | |||
| basic_datagram_socket<Protocol1, DatagramSocketService1>&& other) | |||
| template <typename Protocol1, typename Executor1> | |||
| typename enable_if< | |||
| is_convertible<Protocol1, Protocol>::value | |||
| && is_convertible<Executor1, Executor>::value, | |||
| basic_datagram_socket& | |||
| >::type operator=(basic_datagram_socket<Protocol1, Executor1>&& other) | |||
| { | |||
| basic_socket<Protocol, DatagramSocketService>::operator=( | |||
| ASIO_MOVE_CAST2(basic_datagram_socket< | |||
| Protocol1, DatagramSocketService1>)(other)); | |||
| basic_socket<Protocol, Executor>::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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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 <typename ConstBufferSequence, typename WriteHandler> | |||
| 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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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 <typename MutableBufferSequence, typename ReadHandler> | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| initiate_async_receive_from(), handler, | |||
| this, buffers, &sender_endpoint, flags); | |||
| } | |||
| private: | |||
| struct initiate_async_send | |||
| { | |||
| template <typename WriteHandler, typename ConstBufferSequence> | |||
| 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<WriteHandler> 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 <typename WriteHandler, typename ConstBufferSequence> | |||
| 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<WriteHandler> 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 <typename ReadHandler, typename MutableBufferSequence> | |||
| 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<ReadHandler> 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 <typename ReadHandler, typename MutableBufferSequence> | |||
| 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<ReadHandler> 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 | |||
| @@ -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 <cstddef> | |||
| #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 Time, | |||
| typename TimeTraits = asio::time_traits<Time>, | |||
| typename TimerService = deadline_timer_service<Time, TimeTraits> > | |||
| typename Executor = executor> | |||
| class basic_deadline_timer | |||
| : public basic_io_object<TimerService> | |||
| { | |||
| 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<TimerService>(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 <typename ExecutionContext> | |||
| explicit basic_deadline_timer(ExecutionContext& context, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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<TimerService>(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 <typename ExecutionContext> | |||
| basic_deadline_timer(ExecutionContext& context, const time_type& expiry_time, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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<TimerService>(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 <typename ExecutionContext> | |||
| basic_deadline_timer(ExecutionContext& context, | |||
| const duration_type& expiry_time, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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 <typename WaitHandler> | |||
| 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<WaitHandler, void (asio::error_code)>( | |||
| 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 <typename WaitHandler> | |||
| 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<WaitHandler> 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<TimeTraits>, Executor> impl_; | |||
| }; | |||
| } // namespace asio | |||
| @@ -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 <typename T, typename U> | |||
| 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<service_type*>(0), | |||
| static_cast<implementation_type*>(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 <typename IoObjectService1> | |||
| 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 <typename IoObjectService1> | |||
| basic_io_object(IoObjectService1& other_service, | |||
| typename IoObjectService1::implementation_type& other_implementation) | |||
| : service_(&asio::use_service<IoObjectService>( | |||
| other_service.get_io_context())) | |||
| { | |||
| service_->converting_move_construct(implementation_, | |||
| other_service, other_implementation); | |||
| } | |||
| ~basic_io_object() | |||
| { | |||
| service_->destroy(implementation_); | |||
| @@ -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 <cstddef> | |||
| #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 <typename Protocol, typename Executor = executor> | |||
| 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 <typename Protocol, | |||
| typename RawSocketService = raw_socket_service<Protocol> > | |||
| template <typename Protocol, typename Executor> | |||
| class basic_raw_socket | |||
| : public basic_socket<Protocol, RawSocketService> | |||
| : public basic_socket<Protocol, Executor> | |||
| { | |||
| public: | |||
| /// The type of the executor associated with the object. | |||
| typedef Executor executor_type; | |||
| /// Rebinds the socket type to another executor. | |||
| template <typename Executor1> | |||
| struct rebind_executor | |||
| { | |||
| /// The socket type when rebound to the specified executor. | |||
| typedef basic_raw_socket<Protocol, Executor1> 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<Protocol, | |||
| Executor>::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<Protocol, RawSocketService>(io_context) | |||
| explicit basic_raw_socket(const executor_type& ex) | |||
| : basic_socket<Protocol, Executor>(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 <typename ExecutionContext> | |||
| explicit basic_raw_socket(ExecutionContext& context, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, RawSocketService>(io_context, protocol) | |||
| basic_raw_socket(const executor_type& ex, const protocol_type& protocol) | |||
| : basic_socket<Protocol, Executor>(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 <typename ExecutionContext> | |||
| basic_raw_socket(ExecutionContext& context, const protocol_type& protocol, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, RawSocketService>(io_context, endpoint) | |||
| basic_raw_socket(const executor_type& ex, const endpoint_type& endpoint) | |||
| : basic_socket<Protocol, Executor>(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 <typename ExecutionContext> | |||
| basic_raw_socket(ExecutionContext& context, const endpoint_type& endpoint, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, RawSocketService>( | |||
| io_context, protocol, native_socket) | |||
| : basic_socket<Protocol, Executor>(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 <typename ExecutionContext> | |||
| basic_raw_socket(ExecutionContext& context, | |||
| const protocol_type& protocol, const native_handle_type& native_socket, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, RawSocketService>( | |||
| ASIO_MOVE_CAST(basic_raw_socket)(other)) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, RawSocketService>::operator=( | |||
| ASIO_MOVE_CAST(basic_raw_socket)(other)); | |||
| basic_socket<Protocol, Executor>::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 <typename Protocol1, typename RawSocketService1> | |||
| basic_raw_socket(basic_raw_socket<Protocol1, RawSocketService1>&& other, | |||
| typename enable_if<is_convertible<Protocol1, Protocol>::value>::type* = 0) | |||
| : basic_socket<Protocol, RawSocketService>( | |||
| ASIO_MOVE_CAST2(basic_raw_socket< | |||
| Protocol1, RawSocketService1>)(other)) | |||
| template <typename Protocol1, typename Executor1> | |||
| basic_raw_socket(basic_raw_socket<Protocol1, Executor1>&& other, | |||
| typename enable_if< | |||
| is_convertible<Protocol1, Protocol>::value | |||
| && is_convertible<Executor1, Executor>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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 Protocol1, typename RawSocketService1> | |||
| typename enable_if<is_convertible<Protocol1, Protocol>::value, | |||
| basic_raw_socket>::type& operator=( | |||
| basic_raw_socket<Protocol1, RawSocketService1>&& other) | |||
| template <typename Protocol1, typename Executor1> | |||
| typename enable_if< | |||
| is_convertible<Protocol1, Protocol>::value | |||
| && is_convertible<Executor1, Executor>::value, | |||
| basic_raw_socket& | |||
| >::type operator=(basic_raw_socket<Protocol1, Executor1>&& other) | |||
| { | |||
| basic_socket<Protocol, RawSocketService>::operator=( | |||
| ASIO_MOVE_CAST2(basic_raw_socket< | |||
| Protocol1, RawSocketService1>)(other)); | |||
| basic_socket<Protocol, Executor>::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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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 <typename ConstBufferSequence, typename WriteHandler> | |||
| 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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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 <typename MutableBufferSequence, typename ReadHandler> | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| initiate_async_receive_from(), handler, | |||
| this, buffers, &sender_endpoint, flags); | |||
| } | |||
| private: | |||
| struct initiate_async_send | |||
| { | |||
| template <typename WriteHandler, typename ConstBufferSequence> | |||
| 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<WriteHandler> 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 <typename WriteHandler, typename ConstBufferSequence> | |||
| 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<WriteHandler> 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 <typename ReadHandler, typename MutableBufferSequence> | |||
| 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<ReadHandler> 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 <typename ReadHandler, typename MutableBufferSequence> | |||
| 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<ReadHandler> 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 | |||
| @@ -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 <typename Protocol, typename Executor = executor> | |||
| 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 <typename Protocol, | |||
| typename SeqPacketSocketService = seq_packet_socket_service<Protocol> > | |||
| template <typename Protocol, typename Executor> | |||
| class basic_seq_packet_socket | |||
| : public basic_socket<Protocol, SeqPacketSocketService> | |||
| : public basic_socket<Protocol, Executor> | |||
| { | |||
| public: | |||
| /// The type of the executor associated with the object. | |||
| typedef Executor executor_type; | |||
| /// Rebinds the socket type to another executor. | |||
| template <typename Executor1> | |||
| struct rebind_executor | |||
| { | |||
| /// The socket type when rebound to the specified executor. | |||
| typedef basic_seq_packet_socket<Protocol, Executor1> 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<Protocol, | |||
| Executor>::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<Protocol, SeqPacketSocketService>(io_context) | |||
| explicit basic_seq_packet_socket(const executor_type& ex) | |||
| : basic_socket<Protocol, Executor>(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 <typename ExecutionContext> | |||
| explicit basic_seq_packet_socket(ExecutionContext& context, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, SeqPacketSocketService>(io_context, protocol) | |||
| : basic_socket<Protocol, Executor>(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 <typename ExecutionContext> | |||
| basic_seq_packet_socket(ExecutionContext& context, | |||
| const protocol_type& protocol, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, SeqPacketSocketService>(io_context, endpoint) | |||
| : basic_socket<Protocol, Executor>(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 <typename ExecutionContext> | |||
| basic_seq_packet_socket(ExecutionContext& context, | |||
| const endpoint_type& endpoint, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, SeqPacketSocketService>( | |||
| io_context, protocol, native_socket) | |||
| : basic_socket<Protocol, Executor>(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 <typename ExecutionContext> | |||
| basic_seq_packet_socket(ExecutionContext& context, | |||
| const protocol_type& protocol, const native_handle_type& native_socket, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, SeqPacketSocketService>( | |||
| ASIO_MOVE_CAST(basic_seq_packet_socket)(other)) | |||
| : basic_socket<Protocol, Executor>(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<Protocol, SeqPacketSocketService>::operator=( | |||
| ASIO_MOVE_CAST(basic_seq_packet_socket)(other)); | |||
| basic_socket<Protocol, Executor>::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 <typename Protocol1, typename SeqPacketSocketService1> | |||
| basic_seq_packet_socket( | |||
| basic_seq_packet_socket<Protocol1, SeqPacketSocketService1>&& other, | |||
| typename enable_if<is_convertible<Protocol1, Protocol>::value>::type* = 0) | |||
| : basic_socket<Protocol, SeqPacketSocketService>( | |||
| ASIO_MOVE_CAST2(basic_seq_packet_socket< | |||
| Protocol1, SeqPacketSocketService1>)(other)) | |||
| template <typename Protocol1, typename Executor1> | |||
| basic_seq_packet_socket(basic_seq_packet_socket<Protocol1, Executor1>&& other, | |||
| typename enable_if< | |||
| is_convertible<Protocol1, Protocol>::value | |||
| && is_convertible<Executor1, Executor>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(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 Protocol1, typename SeqPacketSocketService1> | |||
| typename enable_if<is_convertible<Protocol1, Protocol>::value, | |||
| basic_seq_packet_socket>::type& operator=( | |||
| basic_seq_packet_socket<Protocol1, SeqPacketSocketService1>&& other) | |||
| template <typename Protocol1, typename Executor1> | |||
| typename enable_if< | |||
| is_convertible<Protocol1, Protocol>::value | |||
| && is_convertible<Executor1, Executor>::value, | |||
| basic_seq_packet_socket& | |||
| >::type operator=(basic_seq_packet_socket<Protocol1, Executor1>&& other) | |||
| { | |||
| basic_socket<Protocol, SeqPacketSocketService>::operator=( | |||
| ASIO_MOVE_CAST2(basic_seq_packet_socket< | |||
| Protocol1, SeqPacketSocketService1>)(other)); | |||
| basic_socket<Protocol, Executor>::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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| initiate_async_receive_with_flags(), handler, | |||
| this, buffers, in_flags, &out_flags); | |||
| } | |||
| private: | |||
| struct initiate_async_send | |||
| { | |||
| template <typename WriteHandler, typename ConstBufferSequence> | |||
| 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<WriteHandler> 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 <typename ReadHandler, typename MutableBufferSequence> | |||
| 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<ReadHandler> 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 | |||
| @@ -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 <string> | |||
| #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 <utility> | |||
| #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 <typename SerialPortService = serial_port_service> | |||
| template <typename Executor = executor> | |||
| class basic_serial_port | |||
| : public basic_io_object<SerialPortService>, | |||
| 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<SerialPortService> 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 <typename ExecutionContext> | |||
| explicit basic_serial_port(ExecutionContext& context, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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<SerialPortService>(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<SerialPortService>(io_context) | |||
| template <typename ExecutionContext> | |||
| basic_serial_port(ExecutionContext& context, const char* device, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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<SerialPortService>(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 <typename ExecutionContext> | |||
| basic_serial_port(ExecutionContext& context, const std::string& device, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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<SerialPortService>(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 <typename ExecutionContext> | |||
| basic_serial_port(ExecutionContext& context, | |||
| const native_handle_type& native_serial_port, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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<SerialPortService>( | |||
| 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<SerialPortService>::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 <typename SettableSerialPortOption> | |||
| 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 <typename GettableSerialPortOption> | |||
| 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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| 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 <typename WriteHandler, typename ConstBufferSequence> | |||
| 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<WriteHandler> 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 <typename ReadHandler, typename MutableBufferSequence> | |||
| 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<ReadHandler> 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<detail::win_iocp_serial_port_service, Executor> impl_; | |||
| #else | |||
| detail::io_object_impl<detail::reactive_serial_port_service, Executor> impl_; | |||
| #endif | |||
| }; | |||
| } // namespace asio | |||
| @@ -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 <typename SignalSetService = signal_set_service> | |||
| template <typename Executor = executor> | |||
| class basic_signal_set | |||
| : public basic_io_object<SignalSetService> | |||
| { | |||
| 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 <typename ExecutionContext> | |||
| explicit basic_signal_set(ExecutionContext& context, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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<SignalSetService>(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<SignalSetService>(io_context) | |||
| template <typename ExecutionContext> | |||
| basic_signal_set(ExecutionContext& context, int signal_number_1, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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<SignalSetService>(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 <typename ExecutionContext> | |||
| basic_signal_set(ExecutionContext& context, int signal_number_1, | |||
| int signal_number_2, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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<SignalSetService>(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 <typename ExecutionContext> | |||
| basic_signal_set(ExecutionContext& context, int signal_number_1, | |||
| int signal_number_2, int signal_number_3, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::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 <typename SignalHandler> | |||
| 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<SignalHandler, void (asio::error_code, int)>( | |||
| 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 <typename SignalHandler> | |||
| 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<SignalHandler> handler2(handler); | |||
| self->impl_.get_service().async_wait( | |||
| self->impl_.get_implementation(), handler2.value, | |||
| self->impl_.get_implementation_executor()); | |||
| } | |||
| }; | |||
| detail::io_object_impl<detail::signal_set_service, Executor> impl_; | |||
| }; | |||
| } // namespace asio | |||
| #include "asio/detail/pop_options.hpp" | |||
| #endif // ASIO_BASIC_SIGNAL_SET_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 <istream> | |||
| #include <ostream> | |||
| #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<char>( | |||
| // &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<char>( \ | |||
| &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 <typename Protocol, typename StreamSocketService, | |||
| typename Time, typename TimeTraits, typename TimerService> | |||
| template <typename Protocol, typename Clock, typename WaitTraits> | |||
| class socket_iostream_base | |||
| { | |||
| protected: | |||
| basic_socket_streambuf<Protocol, StreamSocketService, | |||
| Time, TimeTraits, TimerService> 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<Protocol> 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<Protocol, Clock, WaitTraits> 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 <typename Protocol, | |||
| typename StreamSocketService = stream_socket_service<Protocol>, | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) \ | |||
| || defined(GENERATING_DOCUMENTATION) | |||
| typename Time = boost::posix_time::ptime, | |||
| typename TimeTraits = asio::time_traits<Time>, | |||
| typename TimerService = deadline_timer_service<Time, TimeTraits> > | |||
| #else | |||
| typename Time = steady_timer::clock_type, | |||
| typename TimeTraits = steady_timer::traits_type, | |||
| typename TimerService = steady_timer::service_type> | |||
| #endif | |||
| && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| typename Clock = boost::posix_time::ptime, | |||
| typename WaitTraits = time_traits<Clock> > | |||
| #else // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| typename Clock = chrono::steady_clock, | |||
| typename WaitTraits = wait_traits<Clock> > | |||
| #endif // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| class basic_socket_iostream; | |||
| #endif // !defined(ASIO_BASIC_SOCKET_IOSTREAM_FWD_DECL) | |||
| /// Iostream interface for a socket. | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| template <typename Protocol, | |||
| typename Clock = chrono::steady_clock, | |||
| typename WaitTraits = wait_traits<Clock> > | |||
| #else // defined(GENERATING_DOCUMENTATION) | |||
| template <typename Protocol, typename Clock, typename WaitTraits> | |||
| #endif // defined(GENERATING_DOCUMENTATION) | |||
| class basic_socket_iostream | |||
| : private detail::socket_iostream_base<Protocol, | |||
| StreamSocketService, Time, TimeTraits, TimerService>, | |||
| : private detail::socket_iostream_base<Protocol, Clock, WaitTraits>, | |||
| public std::basic_iostream<char> | |||
| { | |||
| private: | |||
| // These typedefs are intended keep this class's implementation independent | |||
| // of whether it's using Boost.DateTime, Boost.Chrono or std::chrono. | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| typedef TimeTraits traits_helper; | |||
| #else | |||
| typedef detail::chrono_time_traits<Time, TimeTraits> traits_helper; | |||
| #endif | |||
| // of whether it's using Boost.DateClock, Boost.Chrono or std::chrono. | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) \ | |||
| && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| typedef WaitTraits traits_helper; | |||
| #else // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| typedef detail::chrono_time_traits<Clock, WaitTraits> traits_helper; | |||
| #endif // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| public: | |||
| /// The protocol type. | |||
| typedef Protocol protocol_type; | |||
| /// The endpoint type. | |||
| typedef typename Protocol::endpoint endpoint_type; | |||
| /// The clock type. | |||
| typedef Clock clock_type; | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| /// (Deprecated: Use time_point.) The time type. | |||
| typedef typename TimeTraits::time_type time_type; | |||
| typedef typename WaitTraits::time_type time_type; | |||
| /// The time type. | |||
| typedef typename TimeTraits::time_point time_point; | |||
| typedef typename WaitTraits::time_point time_point; | |||
| /// (Deprecated: Use duration.) The duration type. | |||
| typedef typename TimeTraits::duration_type duration_type; | |||
| typedef typename WaitTraits::duration_type duration_type; | |||
| /// The duration type. | |||
| typedef typename TimeTraits::duration duration; | |||
| typedef typename WaitTraits::duration duration; | |||
| #else | |||
| # if !defined(ASIO_NO_DEPRECATED) | |||
| typedef typename traits_helper::time_type time_type; | |||
| @@ -149,12 +188,47 @@ public: | |||
| basic_socket_iostream() | |||
| : std::basic_iostream<char>( | |||
| &this->detail::socket_iostream_base< | |||
| Protocol, StreamSocketService, Time, | |||
| TimeTraits, TimerService>::streambuf_) | |||
| Protocol, Clock, WaitTraits>::streambuf_) | |||
| { | |||
| this->setf(std::ios_base::unitbuf); | |||
| } | |||
| #if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | |||
| /// Construct a basic_socket_iostream from the supplied socket. | |||
| explicit basic_socket_iostream(basic_stream_socket<protocol_type> s) | |||
| : detail::socket_iostream_base< | |||
| Protocol, Clock, WaitTraits>(std::move(s)), | |||
| std::basic_iostream<char>( | |||
| &this->detail::socket_iostream_base< | |||
| Protocol, Clock, WaitTraits>::streambuf_) | |||
| { | |||
| this->setf(std::ios_base::unitbuf); | |||
| } | |||
| #if defined(ASIO_HAS_STD_IOSTREAM_MOVE) \ | |||
| || defined(GENERATING_DOCUMENTATION) | |||
| /// Move-construct a basic_socket_iostream from another. | |||
| basic_socket_iostream(basic_socket_iostream&& other) | |||
| : detail::socket_iostream_base< | |||
| Protocol, Clock, WaitTraits>(std::move(other)), | |||
| std::basic_iostream<char>(std::move(other)) | |||
| { | |||
| this->set_rdbuf(&this->detail::socket_iostream_base< | |||
| Protocol, Clock, WaitTraits>::streambuf_); | |||
| } | |||
| /// Move-assign a basic_socket_iostream from another. | |||
| basic_socket_iostream& operator=(basic_socket_iostream&& other) | |||
| { | |||
| std::basic_iostream<char>::operator=(std::move(other)); | |||
| detail::socket_iostream_base< | |||
| Protocol, Clock, WaitTraits>::operator=(std::move(other)); | |||
| return *this; | |||
| } | |||
| #endif // defined(ASIO_HAS_STD_IOSTREAM_MOVE) | |||
| // || defined(GENERATING_DOCUMENTATION) | |||
| #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| /// Establish a connection to an endpoint corresponding to a resolver query. | |||
| /** | |||
| @@ -169,8 +243,7 @@ public: | |||
| explicit basic_socket_iostream(T... x) | |||
| : std::basic_iostream<char>( | |||
| &this->detail::socket_iostream_base< | |||
| Protocol, StreamSocketService, Time, | |||
| TimeTraits, TimerService>::streambuf_) | |||
| Protocol, Clock, WaitTraits>::streambuf_) | |||
| { | |||
| this->setf(std::ios_base::unitbuf); | |||
| if (rdbuf()->connect(x...) == 0) | |||
| @@ -208,14 +281,17 @@ public: | |||
| } | |||
| /// Return a pointer to the underlying streambuf. | |||
| basic_socket_streambuf<Protocol, StreamSocketService, | |||
| Time, TimeTraits, TimerService>* rdbuf() const | |||
| basic_socket_streambuf<Protocol, Clock, WaitTraits>* rdbuf() const | |||
| { | |||
| return const_cast<basic_socket_streambuf<Protocol, StreamSocketService, | |||
| Time, TimeTraits, TimerService>*>( | |||
| return const_cast<basic_socket_streambuf<Protocol, Clock, WaitTraits>*>( | |||
| &this->detail::socket_iostream_base< | |||
| Protocol, StreamSocketService, Time, | |||
| TimeTraits, TimerService>::streambuf_); | |||
| Protocol, Clock, WaitTraits>::streambuf_); | |||
| } | |||
| /// Get a reference to the underlying socket. | |||
| basic_socket<Protocol>& socket() | |||
| { | |||
| return rdbuf()->socket(); | |||
| } | |||
| /// Get the last error associated with the stream. | |||
| @@ -232,7 +308,7 @@ public: | |||
| */ | |||
| const asio::error_code& error() const | |||
| { | |||
| return rdbuf()->puberror(); | |||
| return rdbuf()->error(); | |||
| } | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| @@ -309,6 +385,12 @@ public: | |||
| rdbuf()->expires_from_now(expiry_time); | |||
| } | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| private: | |||
| // Disallow copying and assignment. | |||
| basic_socket_iostream(const basic_socket_iostream&) ASIO_DELETED; | |||
| basic_socket_iostream& operator=( | |||
| const basic_socket_iostream&) ASIO_DELETED; | |||
| }; | |||
| } // namespace asio | |||
| @@ -2,7 +2,7 @@ | |||
| // basic_socket_streambuf.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) | |||
| @@ -20,18 +20,22 @@ | |||
| #if !defined(ASIO_NO_IOSTREAM) | |||
| #include <streambuf> | |||
| #include <vector> | |||
| #include "asio/basic_socket.hpp" | |||
| #include "asio/deadline_timer_service.hpp" | |||
| #include "asio/detail/array.hpp" | |||
| #include "asio/basic_stream_socket.hpp" | |||
| #include "asio/detail/buffer_sequence_adapter.hpp" | |||
| #include "asio/detail/memory.hpp" | |||
| #include "asio/detail/throw_error.hpp" | |||
| #include "asio/io_context.hpp" | |||
| #include "asio/stream_socket_service.hpp" | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| # include "asio/deadline_timer.hpp" | |||
| #else | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) \ | |||
| && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| # include "asio/detail/deadline_timer_service.hpp" | |||
| #else // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| # include "asio/steady_timer.hpp" | |||
| #endif | |||
| #endif // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| #if !defined(ASIO_HAS_VARIADIC_TEMPLATES) | |||
| @@ -39,14 +43,11 @@ | |||
| // A macro that should expand to: | |||
| // template <typename T1, ..., typename Tn> | |||
| // basic_socket_streambuf<Protocol, StreamSocketService, | |||
| // Time, TimeTraits, TimerService>* connect( | |||
| // T1 x1, ..., Tn xn) | |||
| // basic_socket_streambuf* connect(T1 x1, ..., Tn xn) | |||
| // { | |||
| // init_buffers(); | |||
| // this->basic_socket<Protocol, StreamSocketService>::close(ec_); | |||
| // typedef typename Protocol::resolver resolver_type; | |||
| // resolver_type resolver(detail::socket_streambuf_base::io_context_); | |||
| // resolver_type resolver(socket().get_executor()); | |||
| // connect_to_endpoints( | |||
| // resolver.resolve(x1, ..., xn, ec_)); | |||
| // return !ec_ ? this : 0; | |||
| @@ -55,14 +56,11 @@ | |||
| # define ASIO_PRIVATE_CONNECT_DEF(n) \ | |||
| template <ASIO_VARIADIC_TPARAMS(n)> \ | |||
| basic_socket_streambuf<Protocol, StreamSocketService, \ | |||
| Time, TimeTraits, TimerService>* connect( \ | |||
| ASIO_VARIADIC_BYVAL_PARAMS(n)) \ | |||
| basic_socket_streambuf* connect(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ | |||
| { \ | |||
| init_buffers(); \ | |||
| this->basic_socket<Protocol, StreamSocketService>::close(ec_); \ | |||
| typedef typename Protocol::resolver resolver_type; \ | |||
| resolver_type resolver(detail::socket_streambuf_base::io_context_); \ | |||
| resolver_type resolver(socket().get_executor()); \ | |||
| connect_to_endpoints( \ | |||
| resolver.resolve(ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \ | |||
| return !ec_ ? this : 0; \ | |||
| @@ -76,59 +74,111 @@ | |||
| namespace asio { | |||
| namespace detail { | |||
| // A separate base class is used to ensure that the io_context is initialised | |||
| // prior to the basic_socket_streambuf's basic_socket base class. | |||
| class socket_streambuf_base | |||
| // A separate base class is used to ensure that the io_context member is | |||
| // initialised prior to the basic_socket_streambuf's basic_socket base class. | |||
| class socket_streambuf_io_context | |||
| { | |||
| protected: | |||
| socket_streambuf_io_context(io_context* ctx) | |||
| : default_io_context_(ctx) | |||
| { | |||
| } | |||
| shared_ptr<io_context> default_io_context_; | |||
| }; | |||
| // A separate base class is used to ensure that the dynamically allocated | |||
| // buffers are constructed prior to the basic_socket_streambuf's basic_socket | |||
| // base class. This makes moving the socket is the last potentially throwing | |||
| // step in the streambuf's move constructor, giving the constructor a strong | |||
| // exception safety guarantee. | |||
| class socket_streambuf_buffers | |||
| { | |||
| protected: | |||
| io_context io_context_; | |||
| socket_streambuf_buffers() | |||
| : get_buffer_(buffer_size), | |||
| put_buffer_(buffer_size) | |||
| { | |||
| } | |||
| enum { buffer_size = 512 }; | |||
| std::vector<char> get_buffer_; | |||
| std::vector<char> put_buffer_; | |||
| }; | |||
| } // namespace detail | |||
| /// Iostream streambuf for a socket. | |||
| #if !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL) | |||
| #define ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL | |||
| // Forward declaration with defaulted arguments. | |||
| template <typename Protocol, | |||
| typename StreamSocketService = stream_socket_service<Protocol>, | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) \ | |||
| || defined(GENERATING_DOCUMENTATION) | |||
| typename Time = boost::posix_time::ptime, | |||
| typename TimeTraits = asio::time_traits<Time>, | |||
| typename TimerService = deadline_timer_service<Time, TimeTraits> > | |||
| #else | |||
| typename Time = steady_timer::clock_type, | |||
| typename TimeTraits = steady_timer::traits_type, | |||
| typename TimerService = steady_timer::service_type> | |||
| #endif | |||
| && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| typename Clock = boost::posix_time::ptime, | |||
| typename WaitTraits = time_traits<Clock> > | |||
| #else // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| typename Clock = chrono::steady_clock, | |||
| typename WaitTraits = wait_traits<Clock> > | |||
| #endif // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| class basic_socket_streambuf; | |||
| #endif // !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL) | |||
| /// Iostream streambuf for a socket. | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| template <typename Protocol, | |||
| typename Clock = chrono::steady_clock, | |||
| typename WaitTraits = wait_traits<Clock> > | |||
| #else // defined(GENERATING_DOCUMENTATION) | |||
| template <typename Protocol, typename Clock, typename WaitTraits> | |||
| #endif // defined(GENERATING_DOCUMENTATION) | |||
| class basic_socket_streambuf | |||
| : public std::streambuf, | |||
| private detail::socket_streambuf_base, | |||
| public basic_socket<Protocol, StreamSocketService> | |||
| private detail::socket_streambuf_io_context, | |||
| private detail::socket_streambuf_buffers, | |||
| #if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) | |||
| private basic_socket<Protocol> | |||
| #else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) | |||
| public basic_socket<Protocol> | |||
| #endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION) | |||
| { | |||
| private: | |||
| // These typedefs are intended keep this class's implementation independent | |||
| // of whether it's using Boost.DateTime, Boost.Chrono or std::chrono. | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| typedef TimeTraits traits_helper; | |||
| #else | |||
| typedef detail::chrono_time_traits<Time, TimeTraits> traits_helper; | |||
| #endif | |||
| // of whether it's using Boost.DateClock, Boost.Chrono or std::chrono. | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) \ | |||
| && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| typedef WaitTraits traits_helper; | |||
| #else // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| typedef detail::chrono_time_traits<Clock, WaitTraits> traits_helper; | |||
| #endif // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| public: | |||
| /// The protocol type. | |||
| typedef Protocol protocol_type; | |||
| /// The endpoint type. | |||
| typedef typename Protocol::endpoint endpoint_type; | |||
| /// The clock type. | |||
| typedef Clock clock_type; | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| /// (Deprecated: Use time_point.) The time type. | |||
| typedef typename TimeTraits::time_type time_type; | |||
| typedef typename WaitTraits::time_type time_type; | |||
| /// The time type. | |||
| typedef typename TimeTraits::time_point time_point; | |||
| typedef typename WaitTraits::time_point time_point; | |||
| /// (Deprecated: Use duration.) The duration type. | |||
| typedef typename TimeTraits::duration_type duration_type; | |||
| typedef typename WaitTraits::duration_type duration_type; | |||
| /// The duration type. | |||
| typedef typename TimeTraits::duration duration; | |||
| typedef typename WaitTraits::duration duration; | |||
| #else | |||
| # if !defined(ASIO_NO_DEPRECATED) | |||
| typedef typename traits_helper::time_type time_type; | |||
| @@ -140,22 +190,64 @@ public: | |||
| /// Construct a basic_socket_streambuf without establishing a connection. | |||
| basic_socket_streambuf() | |||
| : basic_socket<Protocol, StreamSocketService>( | |||
| this->detail::socket_streambuf_base::io_context_), | |||
| unbuffered_(false), | |||
| timer_service_(0), | |||
| timer_state_(no_timer) | |||
| : detail::socket_streambuf_io_context(new io_context), | |||
| basic_socket<Protocol>(*default_io_context_), | |||
| expiry_time_(max_expiry_time()) | |||
| { | |||
| init_buffers(); | |||
| } | |||
| #if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | |||
| /// Construct a basic_socket_streambuf from the supplied socket. | |||
| explicit basic_socket_streambuf(basic_stream_socket<protocol_type> s) | |||
| : detail::socket_streambuf_io_context(0), | |||
| basic_socket<Protocol>(std::move(s)), | |||
| expiry_time_(max_expiry_time()) | |||
| { | |||
| init_buffers(); | |||
| } | |||
| /// Move-construct a basic_socket_streambuf from another. | |||
| basic_socket_streambuf(basic_socket_streambuf&& other) | |||
| : detail::socket_streambuf_io_context(other), | |||
| basic_socket<Protocol>(std::move(other.socket())), | |||
| ec_(other.ec_), | |||
| expiry_time_(other.expiry_time_) | |||
| { | |||
| get_buffer_.swap(other.get_buffer_); | |||
| put_buffer_.swap(other.put_buffer_); | |||
| setg(other.eback(), other.gptr(), other.egptr()); | |||
| setp(other.pptr(), other.epptr()); | |||
| other.ec_ = asio::error_code(); | |||
| other.expiry_time_ = max_expiry_time(); | |||
| other.init_buffers(); | |||
| } | |||
| /// Move-assign a basic_socket_streambuf from another. | |||
| basic_socket_streambuf& operator=(basic_socket_streambuf&& other) | |||
| { | |||
| this->close(); | |||
| socket() = std::move(other.socket()); | |||
| detail::socket_streambuf_io_context::operator=(other); | |||
| ec_ = other.ec_; | |||
| expiry_time_ = other.expiry_time_; | |||
| get_buffer_.swap(other.get_buffer_); | |||
| put_buffer_.swap(other.put_buffer_); | |||
| setg(other.eback(), other.gptr(), other.egptr()); | |||
| setp(other.pptr(), other.epptr()); | |||
| other.ec_ = asio::error_code(); | |||
| other.expiry_time_ = max_expiry_time(); | |||
| other.put_buffer_.resize(buffer_size); | |||
| other.init_buffers(); | |||
| return *this; | |||
| } | |||
| #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | |||
| /// Destructor flushes buffered data. | |||
| virtual ~basic_socket_streambuf() | |||
| { | |||
| if (pptr() != pbase()) | |||
| overflow(traits_type::eof()); | |||
| destroy_timer(); | |||
| } | |||
| /// Establish a connection. | |||
| @@ -165,29 +257,11 @@ public: | |||
| * @return \c this if a connection was successfully established, a null | |||
| * pointer otherwise. | |||
| */ | |||
| basic_socket_streambuf<Protocol, StreamSocketService, | |||
| Time, TimeTraits, TimerService>* connect( | |||
| const endpoint_type& endpoint) | |||
| basic_socket_streambuf* connect(const endpoint_type& endpoint) | |||
| { | |||
| init_buffers(); | |||
| this->basic_socket<Protocol, StreamSocketService>::close(ec_); | |||
| if (timer_state_ == timer_has_expired) | |||
| { | |||
| ec_ = asio::error::operation_aborted; | |||
| return 0; | |||
| } | |||
| io_handler handler = { this }; | |||
| this->basic_socket<Protocol, StreamSocketService>::async_connect( | |||
| endpoint, handler); | |||
| ec_ = asio::error::would_block; | |||
| this->get_service().get_io_context().restart(); | |||
| do this->get_service().get_io_context().run_one(); | |||
| while (ec_ == asio::error::would_block); | |||
| ec_ = asio::error_code(); | |||
| this->connect_to_endpoints(&endpoint, &endpoint + 1); | |||
| return !ec_ ? this : 0; | |||
| } | |||
| @@ -202,17 +276,14 @@ public: | |||
| * pointer otherwise. | |||
| */ | |||
| template <typename T1, ..., typename TN> | |||
| basic_socket_streambuf<Protocol, StreamSocketService>* connect( | |||
| T1 t1, ..., TN tn); | |||
| basic_socket_streambuf* connect(T1 t1, ..., TN tn); | |||
| #elif defined(ASIO_HAS_VARIADIC_TEMPLATES) | |||
| template <typename... T> | |||
| basic_socket_streambuf<Protocol, StreamSocketService, | |||
| Time, TimeTraits, TimerService>* connect(T... x) | |||
| basic_socket_streambuf* connect(T... x) | |||
| { | |||
| init_buffers(); | |||
| this->basic_socket<Protocol, StreamSocketService>::close(ec_); | |||
| typedef typename Protocol::resolver resolver_type; | |||
| resolver_type resolver(detail::socket_streambuf_base::io_context_); | |||
| resolver_type resolver(socket().get_executor()); | |||
| connect_to_endpoints(resolver.resolve(x..., ec_)); | |||
| return !ec_ ? this : 0; | |||
| } | |||
| @@ -225,17 +296,34 @@ public: | |||
| * @return \c this if a connection was successfully established, a null | |||
| * pointer otherwise. | |||
| */ | |||
| basic_socket_streambuf<Protocol, StreamSocketService, | |||
| Time, TimeTraits, TimerService>* close() | |||
| basic_socket_streambuf* close() | |||
| { | |||
| sync(); | |||
| this->basic_socket<Protocol, StreamSocketService>::close(ec_); | |||
| socket().close(ec_); | |||
| if (!ec_) | |||
| init_buffers(); | |||
| return !ec_ ? this : 0; | |||
| } | |||
| /// Get a reference to the underlying socket. | |||
| basic_socket<Protocol>& socket() | |||
| { | |||
| return *this; | |||
| } | |||
| /// Get the last error associated with the stream buffer. | |||
| /** | |||
| * @return An \c error_code corresponding to the last error from the stream | |||
| * buffer. | |||
| */ | |||
| const asio::error_code& error() const | |||
| { | |||
| return ec_; | |||
| } | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated: Use error().) Get the last error associated with the stream | |||
| /// buffer. | |||
| /** | |||
| * @return An \c error_code corresponding to the last error from the stream | |||
| * buffer. | |||
| @@ -245,7 +333,6 @@ public: | |||
| return error(); | |||
| } | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated: Use expiry().) Get the stream buffer's expiry time as an | |||
| /// absolute time. | |||
| /** | |||
| @@ -254,9 +341,7 @@ public: | |||
| */ | |||
| time_point expires_at() const | |||
| { | |||
| return timer_service_ | |||
| ? timer_service_->expires_at(timer_implementation_) | |||
| : time_point(); | |||
| return expiry_time_; | |||
| } | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| @@ -267,13 +352,7 @@ public: | |||
| */ | |||
| time_point expiry() const | |||
| { | |||
| return timer_service_ | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| ? timer_service_->expires_at(timer_implementation_) | |||
| #else // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| ? timer_service_->expiry(timer_implementation_) | |||
| #endif // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| : time_point(); | |||
| return expiry_time_; | |||
| } | |||
| /// Set the stream buffer's expiry time as an absolute time. | |||
| @@ -287,33 +366,7 @@ public: | |||
| */ | |||
| void expires_at(const time_point& expiry_time) | |||
| { | |||
| construct_timer(); | |||
| asio::error_code ec; | |||
| timer_service_->expires_at(timer_implementation_, expiry_time, ec); | |||
| asio::detail::throw_error(ec, "expires_at"); | |||
| start_timer(); | |||
| } | |||
| /// Set the stream buffer's expiry time relative to now. | |||
| /** | |||
| * This function sets the expiry time associated with the stream. Stream | |||
| * operations performed after this time (where the operations cannot be | |||
| * completed using the internal buffers) will fail with the error | |||
| * asio::error::operation_aborted. | |||
| * | |||
| * @param expiry_time The expiry time to be used for the timer. | |||
| */ | |||
| void expires_at(const duration& expiry_time) | |||
| { | |||
| construct_timer(); | |||
| asio::error_code ec; | |||
| timer_service_->expires_from_now(timer_implementation_, expiry_time, ec); | |||
| asio::detail::throw_error(ec, "expires_from_now"); | |||
| start_timer(); | |||
| expiry_time_ = expiry_time; | |||
| } | |||
| /// Set the stream buffer's expiry time relative to now. | |||
| @@ -327,17 +380,7 @@ public: | |||
| */ | |||
| void expires_after(const duration& expiry_time) | |||
| { | |||
| construct_timer(); | |||
| asio::error_code ec; | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| timer_service_->expires_from_now(timer_implementation_, expiry_time, ec); | |||
| #else // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| timer_service_->expires_after(timer_implementation_, expiry_time, ec); | |||
| #endif // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| asio::detail::throw_error(ec, "after"); | |||
| start_timer(); | |||
| expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time); | |||
| } | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| @@ -363,108 +406,124 @@ public: | |||
| */ | |||
| void expires_from_now(const duration& expiry_time) | |||
| { | |||
| construct_timer(); | |||
| asio::error_code ec; | |||
| timer_service_->expires_from_now(timer_implementation_, expiry_time, ec); | |||
| asio::detail::throw_error(ec, "expires_from_now"); | |||
| start_timer(); | |||
| expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time); | |||
| } | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| protected: | |||
| int_type underflow() | |||
| { | |||
| if (gptr() == egptr()) | |||
| #if defined(ASIO_WINDOWS_RUNTIME) | |||
| ec_ = asio::error::operation_not_supported; | |||
| return traits_type::eof(); | |||
| #else // defined(ASIO_WINDOWS_RUNTIME) | |||
| if (gptr() != egptr()) | |||
| return traits_type::eof(); | |||
| for (;;) | |||
| { | |||
| if (timer_state_ == timer_has_expired) | |||
| // Check if we are past the expiry time. | |||
| if (traits_helper::less_than(expiry_time_, traits_helper::now())) | |||
| { | |||
| ec_ = asio::error::operation_aborted; | |||
| ec_ = asio::error::timed_out; | |||
| return traits_type::eof(); | |||
| } | |||
| io_handler handler = { this }; | |||
| this->get_service().async_receive(this->get_implementation(), | |||
| asio::buffer(asio::buffer(get_buffer_) + putback_max), | |||
| 0, handler); | |||
| // Try to complete the operation without blocking. | |||
| if (!socket().native_non_blocking()) | |||
| socket().native_non_blocking(true, ec_); | |||
| detail::buffer_sequence_adapter<mutable_buffer, mutable_buffer> | |||
| bufs(asio::buffer(get_buffer_) + putback_max); | |||
| detail::signed_size_type bytes = detail::socket_ops::recv( | |||
| socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_); | |||
| ec_ = asio::error::would_block; | |||
| this->get_service().get_io_context().restart(); | |||
| do this->get_service().get_io_context().run_one(); | |||
| while (ec_ == asio::error::would_block); | |||
| if (ec_) | |||
| // Check if operation succeeded. | |||
| if (bytes > 0) | |||
| { | |||
| setg(&get_buffer_[0], &get_buffer_[0] + putback_max, | |||
| &get_buffer_[0] + putback_max + bytes); | |||
| return traits_type::to_int_type(*gptr()); | |||
| } | |||
| // Check for EOF. | |||
| if (bytes == 0) | |||
| { | |||
| ec_ = asio::error::eof; | |||
| return traits_type::eof(); | |||
| } | |||
| // Operation failed. | |||
| if (ec_ != asio::error::would_block | |||
| && ec_ != asio::error::try_again) | |||
| return traits_type::eof(); | |||
| setg(&get_buffer_[0], &get_buffer_[0] + putback_max, | |||
| &get_buffer_[0] + putback_max + bytes_transferred_); | |||
| return traits_type::to_int_type(*gptr()); | |||
| } | |||
| else | |||
| { | |||
| return traits_type::eof(); | |||
| // Wait for socket to become ready. | |||
| if (detail::socket_ops::poll_read( | |||
| socket().native_handle(), 0, timeout(), ec_) < 0) | |||
| return traits_type::eof(); | |||
| } | |||
| #endif // defined(ASIO_WINDOWS_RUNTIME) | |||
| } | |||
| int_type overflow(int_type c) | |||
| { | |||
| if (unbuffered_) | |||
| #if defined(ASIO_WINDOWS_RUNTIME) | |||
| ec_ = asio::error::operation_not_supported; | |||
| return traits_type::eof(); | |||
| #else // defined(ASIO_WINDOWS_RUNTIME) | |||
| char_type ch = traits_type::to_char_type(c); | |||
| // Determine what needs to be sent. | |||
| const_buffer output_buffer; | |||
| if (put_buffer_.empty()) | |||
| { | |||
| if (traits_type::eq_int_type(c, traits_type::eof())) | |||
| return traits_type::not_eof(c); // Nothing to do. | |||
| output_buffer = asio::buffer(&ch, sizeof(char_type)); | |||
| } | |||
| else | |||
| { | |||
| output_buffer = asio::buffer(pbase(), | |||
| (pptr() - pbase()) * sizeof(char_type)); | |||
| } | |||
| while (output_buffer.size() > 0) | |||
| { | |||
| // Check if we are past the expiry time. | |||
| if (traits_helper::less_than(expiry_time_, traits_helper::now())) | |||
| { | |||
| // Nothing to do. | |||
| return traits_type::not_eof(c); | |||
| ec_ = asio::error::timed_out; | |||
| return traits_type::eof(); | |||
| } | |||
| else | |||
| // Try to complete the operation without blocking. | |||
| if (!socket().native_non_blocking()) | |||
| socket().native_non_blocking(true, ec_); | |||
| detail::buffer_sequence_adapter< | |||
| const_buffer, const_buffer> bufs(output_buffer); | |||
| detail::signed_size_type bytes = detail::socket_ops::send( | |||
| socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_); | |||
| // Check if operation succeeded. | |||
| if (bytes > 0) | |||
| { | |||
| if (timer_state_ == timer_has_expired) | |||
| { | |||
| ec_ = asio::error::operation_aborted; | |||
| return traits_type::eof(); | |||
| } | |||
| // Send the single character immediately. | |||
| char_type ch = traits_type::to_char_type(c); | |||
| io_handler handler = { this }; | |||
| this->get_service().async_send(this->get_implementation(), | |||
| asio::buffer(&ch, sizeof(char_type)), 0, handler); | |||
| ec_ = asio::error::would_block; | |||
| this->get_service().get_io_context().restart(); | |||
| do this->get_service().get_io_context().run_one(); | |||
| while (ec_ == asio::error::would_block); | |||
| if (ec_) | |||
| return traits_type::eof(); | |||
| return c; | |||
| output_buffer += static_cast<std::size_t>(bytes); | |||
| continue; | |||
| } | |||
| // Operation failed. | |||
| if (ec_ != asio::error::would_block | |||
| && ec_ != asio::error::try_again) | |||
| return traits_type::eof(); | |||
| // Wait for socket to become ready. | |||
| if (detail::socket_ops::poll_write( | |||
| socket().native_handle(), 0, timeout(), ec_) < 0) | |||
| return traits_type::eof(); | |||
| } | |||
| else | |||
| if (!put_buffer_.empty()) | |||
| { | |||
| // Send all data in the output buffer. | |||
| asio::const_buffer buffer = | |||
| asio::buffer(pbase(), pptr() - pbase()); | |||
| while (buffer.size() > 0) | |||
| { | |||
| if (timer_state_ == timer_has_expired) | |||
| { | |||
| ec_ = asio::error::operation_aborted; | |||
| return traits_type::eof(); | |||
| } | |||
| io_handler handler = { this }; | |||
| this->get_service().async_send(this->get_implementation(), | |||
| asio::buffer(buffer), 0, handler); | |||
| ec_ = asio::error::would_block; | |||
| this->get_service().get_io_context().restart(); | |||
| do this->get_service().get_io_context().run_one(); | |||
| while (ec_ == asio::error::would_block); | |||
| if (ec_) | |||
| return traits_type::eof(); | |||
| buffer = buffer + bytes_transferred_; | |||
| } | |||
| setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); | |||
| // If the new character is eof then our work here is done. | |||
| @@ -472,10 +531,12 @@ protected: | |||
| return traits_type::not_eof(c); | |||
| // Add the new character to the output buffer. | |||
| *pptr() = traits_type::to_char_type(c); | |||
| *pptr() = ch; | |||
| pbump(1); | |||
| return c; | |||
| } | |||
| return c; | |||
| #endif // defined(ASIO_WINDOWS_RUNTIME) | |||
| } | |||
| int sync() | |||
| @@ -487,150 +548,130 @@ protected: | |||
| { | |||
| if (pptr() == pbase() && s == 0 && n == 0) | |||
| { | |||
| unbuffered_ = true; | |||
| put_buffer_.clear(); | |||
| setp(0, 0); | |||
| sync(); | |||
| return this; | |||
| } | |||
| return 0; | |||
| } | |||
| /// Get the last error associated with the stream buffer. | |||
| /** | |||
| * @return An \c error_code corresponding to the last error from the stream | |||
| * buffer. | |||
| */ | |||
| virtual const asio::error_code& error() const | |||
| { | |||
| return ec_; | |||
| } | |||
| private: | |||
| // Disallow copying and assignment. | |||
| basic_socket_streambuf(const basic_socket_streambuf&) ASIO_DELETED; | |||
| basic_socket_streambuf& operator=( | |||
| const basic_socket_streambuf&) ASIO_DELETED; | |||
| void init_buffers() | |||
| { | |||
| setg(&get_buffer_[0], | |||
| &get_buffer_[0] + putback_max, | |||
| &get_buffer_[0] + putback_max); | |||
| if (unbuffered_) | |||
| if (put_buffer_.empty()) | |||
| setp(0, 0); | |||
| else | |||
| setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); | |||
| } | |||
| template <typename EndpointSequence> | |||
| void connect_to_endpoints(const EndpointSequence& endpoints) | |||
| int timeout() const | |||
| { | |||
| if (!ec_) | |||
| { | |||
| typename EndpointSequence::iterator i = endpoints.begin(); | |||
| typename EndpointSequence::iterator end = endpoints.end(); | |||
| ec_ = asio::error::host_not_found; | |||
| while (ec_ && i != end) | |||
| { | |||
| this->basic_socket<Protocol, StreamSocketService>::close(ec_); | |||
| if (timer_state_ == timer_has_expired) | |||
| { | |||
| ec_ = asio::error::operation_aborted; | |||
| return; | |||
| } | |||
| io_handler handler = { this }; | |||
| this->basic_socket<Protocol, StreamSocketService>::async_connect( | |||
| *i, handler); | |||
| ec_ = asio::error::would_block; | |||
| this->get_service().get_io_context().restart(); | |||
| do this->get_service().get_io_context().run_one(); | |||
| while (ec_ == asio::error::would_block); | |||
| ++i; | |||
| } | |||
| } | |||
| int64_t msec = traits_helper::to_posix_duration( | |||
| traits_helper::subtract(expiry_time_, | |||
| traits_helper::now())).total_milliseconds(); | |||
| if (msec > (std::numeric_limits<int>::max)()) | |||
| msec = (std::numeric_limits<int>::max)(); | |||
| else if (msec < 0) | |||
| msec = 0; | |||
| return static_cast<int>(msec); | |||
| } | |||
| struct io_handler; | |||
| friend struct io_handler; | |||
| struct io_handler | |||
| template <typename EndpointSequence> | |||
| void connect_to_endpoints(const EndpointSequence& endpoints) | |||
| { | |||
| basic_socket_streambuf* this_; | |||
| void operator()(const asio::error_code& ec, | |||
| std::size_t bytes_transferred = 0) | |||
| { | |||
| this_->ec_ = ec; | |||
| this_->bytes_transferred_ = bytes_transferred; | |||
| } | |||
| }; | |||
| this->connect_to_endpoints(endpoints.begin(), endpoints.end()); | |||
| } | |||
| struct timer_handler; | |||
| friend struct timer_handler; | |||
| struct timer_handler | |||
| template <typename EndpointIterator> | |||
| void connect_to_endpoints(EndpointIterator begin, EndpointIterator end) | |||
| { | |||
| basic_socket_streambuf* this_; | |||
| void operator()(const asio::error_code&) | |||
| #if defined(ASIO_WINDOWS_RUNTIME) | |||
| ec_ = asio::error::operation_not_supported; | |||
| #else // defined(ASIO_WINDOWS_RUNTIME) | |||
| if (ec_) | |||
| return; | |||
| ec_ = asio::error::not_found; | |||
| for (EndpointIterator i = begin; i != end; ++i) | |||
| { | |||
| time_point now = traits_helper::now(); | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| time_point expiry_time = this_->timer_service_->expires_at( | |||
| this_->timer_implementation_); | |||
| #else // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| time_point expiry_time = this_->timer_service_->expiry( | |||
| this_->timer_implementation_); | |||
| #endif // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| if (traits_helper::less_than(now, expiry_time)) | |||
| // Check if we are past the expiry time. | |||
| if (traits_helper::less_than(expiry_time_, traits_helper::now())) | |||
| { | |||
| this_->timer_state_ = timer_is_pending; | |||
| this_->timer_service_->async_wait(this_->timer_implementation_, *this); | |||
| ec_ = asio::error::timed_out; | |||
| return; | |||
| } | |||
| else | |||
| { | |||
| this_->timer_state_ = timer_has_expired; | |||
| asio::error_code ec; | |||
| this_->basic_socket<Protocol, StreamSocketService>::close(ec); | |||
| } | |||
| } | |||
| }; | |||
| void construct_timer() | |||
| { | |||
| if (timer_service_ == 0) | |||
| { | |||
| TimerService& timer_service = use_service<TimerService>( | |||
| detail::socket_streambuf_base::io_context_); | |||
| timer_service.construct(timer_implementation_); | |||
| timer_service_ = &timer_service; | |||
| // Close and reopen the socket. | |||
| typename Protocol::endpoint ep(*i); | |||
| socket().close(ec_); | |||
| socket().open(ep.protocol(), ec_); | |||
| if (ec_) | |||
| continue; | |||
| // Try to complete the operation without blocking. | |||
| if (!socket().native_non_blocking()) | |||
| socket().native_non_blocking(true, ec_); | |||
| detail::socket_ops::connect(socket().native_handle(), | |||
| ep.data(), ep.size(), ec_); | |||
| // Check if operation succeeded. | |||
| if (!ec_) | |||
| return; | |||
| // Operation failed. | |||
| if (ec_ != asio::error::in_progress | |||
| && ec_ != asio::error::would_block) | |||
| continue; | |||
| // Wait for socket to become ready. | |||
| if (detail::socket_ops::poll_connect( | |||
| socket().native_handle(), timeout(), ec_) < 0) | |||
| continue; | |||
| // Get the error code from the connect operation. | |||
| int connect_error = 0; | |||
| size_t connect_error_len = sizeof(connect_error); | |||
| if (detail::socket_ops::getsockopt(socket().native_handle(), 0, | |||
| SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec_) | |||
| == detail::socket_error_retval) | |||
| return; | |||
| // Check the result of the connect operation. | |||
| ec_ = asio::error_code(connect_error, | |||
| asio::error::get_system_category()); | |||
| if (!ec_) | |||
| return; | |||
| } | |||
| #endif // defined(ASIO_WINDOWS_RUNTIME) | |||
| } | |||
| void destroy_timer() | |||
| // Helper function to get the maximum expiry time. | |||
| static time_point max_expiry_time() | |||
| { | |||
| if (timer_service_) | |||
| timer_service_->destroy(timer_implementation_); | |||
| } | |||
| void start_timer() | |||
| { | |||
| if (timer_state_ != timer_is_pending) | |||
| { | |||
| timer_handler handler = { this }; | |||
| handler(asio::error_code()); | |||
| } | |||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) \ | |||
| && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| return boost::posix_time::pos_infin; | |||
| #else // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| return (time_point::max)(); | |||
| #endif // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM) | |||
| } | |||
| enum { putback_max = 8 }; | |||
| enum { buffer_size = 512 }; | |||
| asio::detail::array<char, buffer_size> get_buffer_; | |||
| asio::detail::array<char, buffer_size> put_buffer_; | |||
| bool unbuffered_; | |||
| asio::error_code ec_; | |||
| std::size_t bytes_transferred_; | |||
| TimerService* timer_service_; | |||
| typename TimerService::implementation_type timer_implementation_; | |||
| enum state { no_timer, timer_is_pending, timer_has_expired } timer_state_; | |||
| time_point expiry_time_; | |||
| }; | |||
| } // namespace asio | |||
| @@ -2,7 +2,7 @@ | |||
| // basic_stream_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) | |||
| @@ -20,14 +20,23 @@ | |||
| #include "asio/async_result.hpp" | |||
| #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/error.hpp" | |||
| #include "asio/stream_socket_service.hpp" | |||
| #include "asio/detail/push_options.hpp" | |||
| namespace asio { | |||
| #if !defined(ASIO_BASIC_STREAM_SOCKET_FWD_DECL) | |||
| #define ASIO_BASIC_STREAM_SOCKET_FWD_DECL | |||
| // Forward declaration with defaulted arguments. | |||
| template <typename Protocol, typename Executor = executor> | |||
| class basic_stream_socket; | |||
| #endif // !defined(ASIO_BASIC_STREAM_SOCKET_FWD_DECL) | |||
| /// Provides stream-oriented socket functionality. | |||
| /** | |||
| * The basic_stream_socket class template provides asynchronous and blocking | |||
| @@ -40,14 +49,29 @@ namespace asio { | |||
| * @par Concepts: | |||
| * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. | |||
| */ | |||
| template <typename Protocol, | |||
| typename StreamSocketService = stream_socket_service<Protocol> > | |||
| template <typename Protocol, typename Executor> | |||
| class basic_stream_socket | |||
| : public basic_socket<Protocol, StreamSocketService> | |||
| : public basic_socket<Protocol, Executor> | |||
| { | |||
| public: | |||
| /// The type of the executor associated with the object. | |||
| typedef Executor executor_type; | |||
| /// Rebinds the socket type to another executor. | |||
| template <typename Executor1> | |||
| struct rebind_executor | |||
| { | |||
| /// The socket type when rebound to the specified executor. | |||
| typedef basic_stream_socket<Protocol, Executor1> other; | |||
| }; | |||
| /// The native representation of a socket. | |||
| typedef typename StreamSocketService::native_handle_type native_handle_type; | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| typedef implementation_defined native_handle_type; | |||
| #else | |||
| typedef typename basic_socket<Protocol, | |||
| Executor>::native_handle_type native_handle_type; | |||
| #endif | |||
| /// The protocol type. | |||
| typedef Protocol protocol_type; | |||
| @@ -61,11 +85,30 @@ public: | |||
| * 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 stream 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_stream_socket(asio::io_context& io_context) | |||
| : basic_socket<Protocol, StreamSocketService>(io_context) | |||
| explicit basic_stream_socket(const executor_type& ex) | |||
| : basic_socket<Protocol, Executor>(ex) | |||
| { | |||
| } | |||
| /// Construct a basic_stream_socket without opening it. | |||
| /** | |||
| * This constructor creates a stream 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 <typename ExecutionContext> | |||
| explicit basic_stream_socket(ExecutionContext& context, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(context) | |||
| { | |||
| } | |||
| @@ -74,16 +117,37 @@ public: | |||
| * This constructor creates and opens a stream socket. The socket needs to be | |||
| * connected or accepted before data can be sent or received on it. | |||
| * | |||
| * @param io_context The io_context object that the stream 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_stream_socket(asio::io_context& io_context, | |||
| const protocol_type& protocol) | |||
| : basic_socket<Protocol, StreamSocketService>(io_context, protocol) | |||
| basic_stream_socket(const executor_type& ex, const protocol_type& protocol) | |||
| : basic_socket<Protocol, Executor>(ex, protocol) | |||
| { | |||
| } | |||
| /// Construct and open a basic_stream_socket. | |||
| /** | |||
| * This constructor creates and opens a stream 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 <typename ExecutionContext> | |||
| basic_stream_socket(ExecutionContext& context, const protocol_type& protocol, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(context, protocol) | |||
| { | |||
| } | |||
| @@ -94,7 +158,7 @@ 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 stream 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 stream | |||
| @@ -102,9 +166,33 @@ public: | |||
| * | |||
| * @throws asio::system_error Thrown on failure. | |||
| */ | |||
| basic_stream_socket(asio::io_context& io_context, | |||
| const endpoint_type& endpoint) | |||
| : basic_socket<Protocol, StreamSocketService>(io_context, endpoint) | |||
| basic_stream_socket(const executor_type& ex, const endpoint_type& endpoint) | |||
| : basic_socket<Protocol, Executor>(ex, endpoint) | |||
| { | |||
| } | |||
| /// Construct a basic_stream_socket, opening it and binding it to the given | |||
| /// local endpoint. | |||
| /** | |||
| * This constructor creates a stream 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 stream | |||
| * socket will be bound. | |||
| * | |||
| * @throws asio::system_error Thrown on failure. | |||
| */ | |||
| template <typename ExecutionContext> | |||
| basic_stream_socket(ExecutionContext& context, const endpoint_type& endpoint, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(context, endpoint) | |||
| { | |||
| } | |||
| @@ -113,7 +201,7 @@ public: | |||
| * This constructor creates a stream socket object to hold an existing native | |||
| * socket. | |||
| * | |||
| * @param io_context The io_context object that the stream 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. | |||
| @@ -122,10 +210,34 @@ public: | |||
| * | |||
| * @throws asio::system_error Thrown on failure. | |||
| */ | |||
| basic_stream_socket(asio::io_context& io_context, | |||
| basic_stream_socket(const executor_type& ex, | |||
| const protocol_type& protocol, const native_handle_type& native_socket) | |||
| : basic_socket<Protocol, StreamSocketService>( | |||
| io_context, protocol, native_socket) | |||
| : basic_socket<Protocol, Executor>(ex, protocol, native_socket) | |||
| { | |||
| } | |||
| /// Construct a basic_stream_socket on an existing native socket. | |||
| /** | |||
| * This constructor creates a stream 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 <typename ExecutionContext> | |||
| basic_stream_socket(ExecutionContext& context, | |||
| const protocol_type& protocol, const native_handle_type& native_socket, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(context, protocol, native_socket) | |||
| { | |||
| } | |||
| @@ -138,11 +250,11 @@ public: | |||
| * will occur. | |||
| * | |||
| * @note Following the move, the moved-from object is in the same state as if | |||
| * constructed using the @c basic_stream_socket(io_context&) constructor. | |||
| * constructed using the @c basic_stream_socket(const executor_type&) | |||
| * constructor. | |||
| */ | |||
| basic_stream_socket(basic_stream_socket&& other) | |||
| : basic_socket<Protocol, StreamSocketService>( | |||
| ASIO_MOVE_CAST(basic_stream_socket)(other)) | |||
| : basic_socket<Protocol, Executor>(std::move(other)) | |||
| { | |||
| } | |||
| @@ -154,12 +266,12 @@ public: | |||
| * will occur. | |||
| * | |||
| * @note Following the move, the moved-from object is in the same state as if | |||
| * constructed using the @c basic_stream_socket(io_context&) constructor. | |||
| * constructed using the @c basic_stream_socket(const executor_type&) | |||
| * constructor. | |||
| */ | |||
| basic_stream_socket& operator=(basic_stream_socket&& other) | |||
| { | |||
| basic_socket<Protocol, StreamSocketService>::operator=( | |||
| ASIO_MOVE_CAST(basic_stream_socket)(other)); | |||
| basic_socket<Protocol, Executor>::operator=(std::move(other)); | |||
| return *this; | |||
| } | |||
| @@ -172,15 +284,16 @@ public: | |||
| * will occur. | |||
| * | |||
| * @note Following the move, the moved-from object is in the same state as if | |||
| * constructed using the @c basic_stream_socket(io_context&) constructor. | |||
| * constructed using the @c basic_stream_socket(const executor_type&) | |||
| * constructor. | |||
| */ | |||
| template <typename Protocol1, typename StreamSocketService1> | |||
| basic_stream_socket( | |||
| basic_stream_socket<Protocol1, StreamSocketService1>&& other, | |||
| typename enable_if<is_convertible<Protocol1, Protocol>::value>::type* = 0) | |||
| : basic_socket<Protocol, StreamSocketService>( | |||
| ASIO_MOVE_CAST2(basic_stream_socket< | |||
| Protocol1, StreamSocketService1>)(other)) | |||
| template <typename Protocol1, typename Executor1> | |||
| basic_stream_socket(basic_stream_socket<Protocol1, Executor1>&& other, | |||
| typename enable_if< | |||
| is_convertible<Protocol1, Protocol>::value | |||
| && is_convertible<Executor1, Executor>::value | |||
| >::type* = 0) | |||
| : basic_socket<Protocol, Executor>(std::move(other)) | |||
| { | |||
| } | |||
| @@ -192,20 +305,30 @@ public: | |||
| * will occur. | |||
| * | |||
| * @note Following the move, the moved-from object is in the same state as if | |||
| * constructed using the @c basic_stream_socket(io_context&) constructor. | |||
| * constructed using the @c basic_stream_socket(const executor_type&) | |||
| * constructor. | |||
| */ | |||
| template <typename Protocol1, typename StreamSocketService1> | |||
| typename enable_if<is_convertible<Protocol1, Protocol>::value, | |||
| basic_stream_socket>::type& operator=( | |||
| basic_stream_socket<Protocol1, StreamSocketService1>&& other) | |||
| { | |||
| basic_socket<Protocol, StreamSocketService>::operator=( | |||
| ASIO_MOVE_CAST2(basic_stream_socket< | |||
| Protocol1, StreamSocketService1>)(other)); | |||
| template <typename Protocol1, typename Executor1> | |||
| typename enable_if< | |||
| is_convertible<Protocol1, Protocol>::value | |||
| && is_convertible<Executor1, Executor>::value, | |||
| basic_stream_socket& | |||
| >::type operator=(basic_stream_socket<Protocol1, Executor1>&& other) | |||
| { | |||
| basic_socket<Protocol, Executor>::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_stream_socket() | |||
| { | |||
| } | |||
| /// Send some data on the socket. | |||
| /** | |||
| * This function is used to send data on the stream socket. The function | |||
| @@ -235,8 +358,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; | |||
| } | |||
| @@ -273,8 +396,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; | |||
| } | |||
| @@ -301,8 +424,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. | |||
| @@ -323,9 +446,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 send 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 | |||
| @@ -346,13 +469,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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| initiate_async_send(), handler, this, | |||
| buffers, socket_base::message_flags(0)); | |||
| } | |||
| /// Start an asynchronous send. | |||
| @@ -375,9 +495,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 send 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 | |||
| @@ -399,13 +519,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<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| initiate_async_send(), handler, this, buffers, flags); | |||
| } | |||
| /// Receive some data on the socket. | |||
| @@ -440,8 +556,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; | |||
| } | |||
| @@ -481,8 +597,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; | |||
| } | |||
| @@ -509,8 +625,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. | |||
| @@ -531,9 +647,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 receive operation may not receive all of the requested number of | |||
| * bytes. Consider using the @ref async_read function if you need to ensure | |||
| @@ -556,12 +672,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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| initiate_async_receive(), handler, this, | |||
| buffers, socket_base::message_flags(0)); | |||
| } | |||
| /// Start an asynchronous receive. | |||
| @@ -584,9 +698,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 receive operation may not receive all of the requested number of | |||
| * bytes. Consider using the @ref async_read function if you need to ensure | |||
| @@ -610,12 +724,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<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| initiate_async_receive(), handler, this, buffers, flags); | |||
| } | |||
| /// Write some data to the socket. | |||
| @@ -649,8 +760,8 @@ public: | |||
| std::size_t write_some(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, "write_some"); | |||
| return s; | |||
| } | |||
| @@ -675,7 +786,8 @@ public: | |||
| std::size_t write_some(const ConstBufferSequence& buffers, | |||
| asio::error_code& ec) | |||
| { | |||
| return this->get_service().send(this->get_implementation(), buffers, 0, ec); | |||
| return this->impl_.get_service().send( | |||
| this->impl_.get_implementation(), buffers, 0, ec); | |||
| } | |||
| /// Start an asynchronous write. | |||
| @@ -696,9 +808,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 | |||
| @@ -719,12 +831,10 @@ 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_send(this->get_implementation(), | |||
| buffers, 0, ASIO_MOVE_CAST(WriteHandler)(handler)); | |||
| return async_initiate<WriteHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| initiate_async_send(), handler, this, | |||
| buffers, socket_base::message_flags(0)); | |||
| } | |||
| /// Read some data from the socket. | |||
| @@ -759,8 +869,8 @@ public: | |||
| std::size_t read_some(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, "read_some"); | |||
| return s; | |||
| } | |||
| @@ -786,8 +896,8 @@ public: | |||
| std::size_t read_some(const MutableBufferSequence& buffers, | |||
| asio::error_code& ec) | |||
| { | |||
| return this->get_service().receive( | |||
| this->get_implementation(), buffers, 0, ec); | |||
| return this->impl_.get_service().receive( | |||
| this->impl_.get_implementation(), buffers, 0, ec); | |||
| } | |||
| /// Start an asynchronous read. | |||
| @@ -808,9 +918,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 | |||
| @@ -832,13 +942,48 @@ 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_receive(this->get_implementation(), | |||
| buffers, 0, ASIO_MOVE_CAST(ReadHandler)(handler)); | |||
| return async_initiate<ReadHandler, | |||
| void (asio::error_code, std::size_t)>( | |||
| initiate_async_receive(), handler, this, | |||
| buffers, socket_base::message_flags(0)); | |||
| } | |||
| private: | |||
| struct initiate_async_send | |||
| { | |||
| template <typename WriteHandler, typename ConstBufferSequence> | |||
| void operator()(ASIO_MOVE_ARG(WriteHandler) handler, | |||
| basic_stream_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<WriteHandler> 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 | |||
| { | |||
| template <typename ReadHandler, typename MutableBufferSequence> | |||
| void operator()(ASIO_MOVE_ARG(ReadHandler) handler, | |||
| basic_stream_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<ReadHandler> handler2(handler); | |||
| self->impl_.get_service().async_receive( | |||
| self->impl_.get_implementation(), buffers, flags, | |||
| handler2.value, self->impl_.get_implementation_executor()); | |||
| } | |||
| }; | |||
| }; | |||
| } // namespace asio | |||
| @@ -2,7 +2,7 @@ | |||
| // basic_streambuf.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) | |||
| @@ -119,8 +119,8 @@ public: | |||
| /// The type used to represent the output sequence as a list of buffers. | |||
| typedef implementation_defined mutable_buffers_type; | |||
| #else | |||
| typedef asio::const_buffers_1 const_buffers_type; | |||
| typedef asio::mutable_buffers_1 mutable_buffers_type; | |||
| typedef ASIO_CONST_BUFFER const_buffers_type; | |||
| typedef ASIO_MUTABLE_BUFFER mutable_buffers_type; | |||
| #endif | |||
| /// Construct a basic_streambuf object. | |||
| @@ -232,8 +232,7 @@ public: | |||
| */ | |||
| void commit(std::size_t n) | |||
| { | |||
| if (pptr() + n > epptr()) | |||
| n = epptr() - pptr(); | |||
| n = std::min<std::size_t>(n, epptr() - pptr()); | |||
| pbump(static_cast<int>(n)); | |||
| setg(eback(), gptr(), pptr()); | |||
| } | |||
| @@ -2,7 +2,7 @@ | |||
| // basic_streambuf_fwd.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // basic_waitable_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) | |||
| @@ -17,17 +17,35 @@ | |||
| #include "asio/detail/config.hpp" | |||
| #include <cstddef> | |||
| #include "asio/basic_io_object.hpp" | |||
| #include "asio/detail/chrono_time_traits.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/executor.hpp" | |||
| #include "asio/wait_traits.hpp" | |||
| #include "asio/waitable_timer_service.hpp" | |||
| #if defined(ASIO_HAS_MOVE) | |||
| # include <utility> | |||
| #endif // defined(ASIO_HAS_MOVE) | |||
| #include "asio/detail/push_options.hpp" | |||
| namespace asio { | |||
| #if !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL) | |||
| #define ASIO_BASIC_WAITABLE_TIMER_FWD_DECL | |||
| // Forward declaration with defaulted arguments. | |||
| template <typename Clock, | |||
| typename WaitTraits = asio::wait_traits<Clock>, | |||
| typename Executor = executor> | |||
| class basic_waitable_timer; | |||
| #endif // !defined(ASIO_BASIC_WAITABLE_TIMER_FWD_DECL) | |||
| /// Provides waitable timer functionality. | |||
| /** | |||
| * The basic_waitable_timer class template provides the ability to perform a | |||
| @@ -51,7 +69,7 @@ namespace asio { | |||
| * Performing a blocking wait (C++11): | |||
| * @code | |||
| * // Construct a timer without setting an expiry time. | |||
| * asio::steady_timer timer(io_context); | |||
| * asio::steady_timer timer(my_context); | |||
| * | |||
| * // Set an expiry time relative to now. | |||
| * timer.expires_after(std::chrono::seconds(5)); | |||
| @@ -74,7 +92,7 @@ namespace asio { | |||
| * ... | |||
| * | |||
| * // Construct a timer with an absolute expiry time. | |||
| * asio::steady_timer timer(io_context, | |||
| * asio::steady_timer timer(my_context, | |||
| * std::chrono::steady_clock::now() + std::chrono::seconds(60)); | |||
| * | |||
| * // Start an asynchronous wait. | |||
| @@ -120,13 +138,13 @@ namespace asio { | |||
| * @li If a wait handler is cancelled, the asio::error_code passed to | |||
| * it contains the value asio::error::operation_aborted. | |||
| */ | |||
| template <typename Clock, | |||
| typename WaitTraits = asio::wait_traits<Clock>, | |||
| typename WaitableTimerService = waitable_timer_service<Clock, WaitTraits> > | |||
| template <typename Clock, typename WaitTraits, typename Executor> | |||
| class basic_waitable_timer | |||
| : public basic_io_object<WaitableTimerService> | |||
| { | |||
| public: | |||
| /// The type of the executor associated with the object. | |||
| typedef Executor executor_type; | |||
| /// The clock type. | |||
| typedef Clock clock_type; | |||
| @@ -145,30 +163,72 @@ public: | |||
| * expires_at() or expires_after() 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_waitable_timer(const executor_type& ex) | |||
| : impl_(ex) | |||
| { | |||
| } | |||
| /// Constructor. | |||
| /** | |||
| * This constructor creates a timer without setting an expiry time. The | |||
| * expires_at() or expires_after() 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 <typename ExecutionContext> | |||
| explicit basic_waitable_timer(ExecutionContext& context, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : impl_(context) | |||
| { | |||
| } | |||
| /// Constructor to set a particular expiry time as an absolute time. | |||
| /** | |||
| * This constructor creates a timer and sets the expiry time. | |||
| * | |||
| * @param ex The I/O executor object 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. | |||
| */ | |||
| explicit basic_waitable_timer(asio::io_context& io_context) | |||
| : basic_io_object<WaitableTimerService>(io_context) | |||
| basic_waitable_timer(const executor_type& ex, const time_point& expiry_time) | |||
| : impl_(ex) | |||
| { | |||
| asio::error_code 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 io_context The io_context object that the timer will use to dispatch | |||
| * handlers for any asynchronous operations performed on the timer. | |||
| * @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. | |||
| */ | |||
| basic_waitable_timer(asio::io_context& io_context, | |||
| const time_point& expiry_time) | |||
| : basic_io_object<WaitableTimerService>(io_context) | |||
| template <typename ExecutionContext> | |||
| explicit basic_waitable_timer(ExecutionContext& context, | |||
| const time_point& expiry_time, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : impl_(context) | |||
| { | |||
| 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"); | |||
| } | |||
| @@ -176,19 +236,43 @@ 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_waitable_timer(asio::io_context& io_context, | |||
| const duration& expiry_time) | |||
| : basic_io_object<WaitableTimerService>(io_context) | |||
| basic_waitable_timer(const executor_type& ex, const duration& expiry_time) | |||
| : impl_(ex) | |||
| { | |||
| asio::error_code ec; | |||
| this->get_service().expires_after( | |||
| this->get_implementation(), expiry_time, ec); | |||
| impl_.get_service().expires_after( | |||
| impl_.get_implementation(), expiry_time, ec); | |||
| asio::detail::throw_error(ec, "expires_after"); | |||
| } | |||
| /// 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 <typename ExecutionContext> | |||
| explicit basic_waitable_timer(ExecutionContext& context, | |||
| const duration& expiry_time, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0) | |||
| : impl_(context) | |||
| { | |||
| asio::error_code ec; | |||
| impl_.get_service().expires_after( | |||
| impl_.get_implementation(), expiry_time, ec); | |||
| asio::detail::throw_error(ec, "expires_after"); | |||
| } | |||
| @@ -201,32 +285,48 @@ public: | |||
| * occur. | |||
| * | |||
| * @note Following the move, the moved-from object is in the same state as if | |||
| * constructed using the @c basic_waitable_timer(io_context&) constructor. | |||
| * constructed using the @c basic_waitable_timer(const executor_type&) | |||
| * constructor. | |||
| */ | |||
| basic_waitable_timer(basic_waitable_timer&& other) | |||
| : basic_io_object<WaitableTimerService>( | |||
| ASIO_MOVE_CAST(basic_waitable_timer)(other)) | |||
| : impl_(std::move(other.impl_)) | |||
| { | |||
| } | |||
| /// Move-assign a basic_waitable_timer from another. | |||
| /** | |||
| * This assignment operator moves a timer from one object to 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_waitable_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_waitable_timer(io_context&) constructor. | |||
| * constructed using the @c basic_waitable_timer(const executor_type&) | |||
| * constructor. | |||
| */ | |||
| basic_waitable_timer& operator=(basic_waitable_timer&& other) | |||
| { | |||
| basic_io_object<WaitableTimerService>::operator=( | |||
| ASIO_MOVE_CAST(basic_waitable_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_waitable_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 | |||
| @@ -252,12 +352,14 @@ 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; | |||
| } | |||
| /// Cancel any asynchronous operations that are waiting on the timer. | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated: Use non-error_code overload.) Cancel any asynchronous | |||
| /// operations that are waiting on the timer. | |||
| /** | |||
| * This function forces the completion of any pending asynchronous wait | |||
| * operations against the timer. The handler for each cancelled operation will | |||
| @@ -281,8 +383,9 @@ 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); | |||
| } | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| /// Cancels one asynchronous operation that is waiting on the timer. | |||
| /** | |||
| @@ -311,13 +414,15 @@ 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; | |||
| } | |||
| /// Cancels one asynchronous operation that is waiting on the timer. | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated: Use non-error_code overload.) Cancels one asynchronous | |||
| /// operation that is waiting on the timer. | |||
| /** | |||
| * This function forces the completion of one pending asynchronous wait | |||
| * operation against the timer. Handlers are cancelled in FIFO order. The | |||
| @@ -343,10 +448,9 @@ 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); | |||
| } | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated: Use expiry().) Get the timer's expiry time as an absolute | |||
| /// time. | |||
| /** | |||
| @@ -355,7 +459,7 @@ public: | |||
| */ | |||
| time_point expires_at() const | |||
| { | |||
| return this->get_service().expires_at(this->get_implementation()); | |||
| return impl_.get_service().expires_at(impl_.get_implementation()); | |||
| } | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| @@ -366,7 +470,7 @@ public: | |||
| */ | |||
| time_point expiry() const | |||
| { | |||
| return this->get_service().expiry(this->get_implementation()); | |||
| return impl_.get_service().expiry(impl_.get_implementation()); | |||
| } | |||
| /// Set the timer's expiry time as an absolute time. | |||
| @@ -394,13 +498,15 @@ public: | |||
| std::size_t expires_at(const time_point& 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; | |||
| } | |||
| /// Set the timer's expiry time as an absolute time. | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated: Use non-error_code overload.) Set the timer's expiry time as | |||
| /// an absolute time. | |||
| /** | |||
| * This function sets the expiry time. Any pending asynchronous wait | |||
| * operations will be cancelled. The handler for each cancelled operation will | |||
| @@ -425,9 +531,10 @@ public: | |||
| std::size_t expires_at(const time_point& 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); | |||
| } | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| /// Set the timer's expiry time relative to now. | |||
| /** | |||
| @@ -454,41 +561,12 @@ public: | |||
| std::size_t expires_after(const duration& expiry_time) | |||
| { | |||
| asio::error_code ec; | |||
| std::size_t s = this->get_service().expires_after( | |||
| this->get_implementation(), expiry_time, ec); | |||
| std::size_t s = impl_.get_service().expires_after( | |||
| impl_.get_implementation(), expiry_time, ec); | |||
| asio::detail::throw_error(ec, "expires_after"); | |||
| return s; | |||
| } | |||
| /// Set the timer's expiry time relative to now. | |||
| /** | |||
| * This function sets the expiry time. Any pending asynchronous wait | |||
| * operations will be cancelled. The handler for each cancelled operation will | |||
| * be invoked with the asio::error::operation_aborted error code. | |||
| * | |||
| * @param expiry_time The expiry time to be used for the timer. | |||
| * | |||
| * @param ec Set to indicate what error occurred, if any. | |||
| * | |||
| * @return The number of asynchronous operations that were cancelled. | |||
| * | |||
| * @note If the timer has already expired when expires_after() is called, | |||
| * then the handlers for asynchronous wait operations will: | |||
| * | |||
| * @li have already been invoked; or | |||
| * | |||
| * @li have been queued for invocation in the near future. | |||
| * | |||
| * These handlers can no longer be cancelled, and therefore are passed an | |||
| * error code that indicates the successful completion of the wait operation. | |||
| */ | |||
| std::size_t expires_after(const duration& expiry_time, | |||
| asio::error_code& ec) | |||
| { | |||
| return this->get_service().expires_after( | |||
| this->get_implementation(), expiry_time, ec); | |||
| } | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated: Use expiry().) Get the timer's expiry time relative to now. | |||
| /** | |||
| @@ -497,7 +575,7 @@ public: | |||
| */ | |||
| duration expires_from_now() const | |||
| { | |||
| return this->get_service().expires_from_now(this->get_implementation()); | |||
| return impl_.get_service().expires_from_now(impl_.get_implementation()); | |||
| } | |||
| /// (Deprecated: Use expires_after().) Set the timer's expiry time relative | |||
| @@ -526,8 +604,8 @@ public: | |||
| std::size_t expires_from_now(const duration& 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; | |||
| } | |||
| @@ -558,8 +636,8 @@ public: | |||
| std::size_t expires_from_now(const duration& 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); | |||
| } | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| @@ -573,7 +651,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"); | |||
| } | |||
| @@ -586,7 +664,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. | |||
| @@ -609,22 +687,46 @@ 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 <typename WaitHandler> | |||
| 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<WaitHandler, void (asio::error_code)>( | |||
| initiate_async_wait(), handler, this); | |||
| } | |||
| private: | |||
| // Disallow copying and assignment. | |||
| basic_waitable_timer(const basic_waitable_timer&) ASIO_DELETED; | |||
| basic_waitable_timer& operator=( | |||
| const basic_waitable_timer&) ASIO_DELETED; | |||
| struct initiate_async_wait | |||
| { | |||
| template <typename WaitHandler> | |||
| void operator()(ASIO_MOVE_ARG(WaitHandler) handler, | |||
| basic_waitable_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<WaitHandler> 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< | |||
| detail::chrono_time_traits<Clock, WaitTraits> >, | |||
| executor_type > impl_; | |||
| }; | |||
| } // namespace asio | |||
| @@ -2,7 +2,7 @@ | |||
| // bind_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) | |||
| @@ -18,9 +18,11 @@ | |||
| #include "asio/detail/config.hpp" | |||
| #include "asio/detail/type_traits.hpp" | |||
| #include "asio/detail/variadic_templates.hpp" | |||
| #include "asio/associated_executor.hpp" | |||
| #include "asio/associated_allocator.hpp" | |||
| #include "asio/async_result.hpp" | |||
| #include "asio/handler_type.hpp" | |||
| #include "asio/execution_context.hpp" | |||
| #include "asio/is_executor.hpp" | |||
| #include "asio/uses_executor.hpp" | |||
| #include "asio/detail/push_options.hpp" | |||
| @@ -166,27 +168,27 @@ class executor_binder_base<T, Executor, true> | |||
| protected: | |||
| template <typename E, typename U> | |||
| executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) u) | |||
| : Executor(ASIO_MOVE_CAST(E)(e)), | |||
| target_(executor_arg_t(), static_cast<const Executor&>(*this), | |||
| ASIO_MOVE_CAST(U)(u)) | |||
| : executor_(ASIO_MOVE_CAST(E)(e)), | |||
| target_(executor_arg_t(), executor_, ASIO_MOVE_CAST(U)(u)) | |||
| { | |||
| } | |||
| Executor executor_; | |||
| T target_; | |||
| }; | |||
| template <typename T, typename Executor> | |||
| class executor_binder_base<T, Executor, false> | |||
| : protected Executor | |||
| { | |||
| protected: | |||
| template <typename E, typename U> | |||
| executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) u) | |||
| : Executor(ASIO_MOVE_CAST(E)(e)), | |||
| : executor_(ASIO_MOVE_CAST(E)(e)), | |||
| target_(ASIO_MOVE_CAST(U)(u)) | |||
| { | |||
| } | |||
| Executor executor_; | |||
| T target_; | |||
| }; | |||
| @@ -393,7 +395,7 @@ public: | |||
| /// Obtain the associated executor. | |||
| executor_type get_executor() const ASIO_NOEXCEPT | |||
| { | |||
| return static_cast<const Executor&>(*this); | |||
| return this->executor_; | |||
| } | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| @@ -519,30 +521,30 @@ struct uses_executor<executor_binder<T, Executor>, Executor> | |||
| : true_type {}; | |||
| template <typename T, typename Executor, typename Signature> | |||
| struct handler_type<executor_binder<T, Executor>, Signature> | |||
| class async_result<executor_binder<T, Executor>, Signature> | |||
| { | |||
| public: | |||
| typedef executor_binder< | |||
| typename handler_type<T, Signature>::type, Executor> type; | |||
| }; | |||
| typename async_result<T, Signature>::completion_handler_type, Executor> | |||
| completion_handler_type; | |||
| template <typename T, typename Executor> | |||
| class async_result<executor_binder<T, Executor> > | |||
| { | |||
| public: | |||
| typedef typename async_result<T>::type type; | |||
| typedef typename async_result<T, Signature>::return_type return_type; | |||
| explicit async_result(executor_binder<T, Executor>& b) | |||
| : target_(b.get()) | |||
| { | |||
| } | |||
| type get() | |||
| return_type get() | |||
| { | |||
| return target_.get(); | |||
| } | |||
| private: | |||
| async_result<T> target_; | |||
| async_result(const async_result&) ASIO_DELETED; | |||
| async_result& operator=(const async_result&) ASIO_DELETED; | |||
| async_result<T, Signature> target_; | |||
| }; | |||
| template <typename T, typename Executor, typename Allocator> | |||
| @@ -2,7 +2,7 @@ | |||
| // buffered_read_stream.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) | |||
| @@ -26,7 +26,6 @@ | |||
| #include "asio/detail/noncopyable.hpp" | |||
| #include "asio/detail/type_traits.hpp" | |||
| #include "asio/error.hpp" | |||
| #include "asio/io_context.hpp" | |||
| #include "asio/detail/push_options.hpp" | |||
| @@ -105,22 +104,6 @@ public: | |||
| return next_layer_.lowest_layer().get_executor(); | |||
| } | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated: Use get_executor().) Get the io_context associated with the | |||
| /// object. | |||
| asio::io_context& get_io_context() | |||
| { | |||
| return next_layer_.get_io_context(); | |||
| } | |||
| /// (Deprecated: Use get_executor().) Get the io_context associated with the | |||
| /// object. | |||
| asio::io_context& get_io_service() | |||
| { | |||
| return next_layer_.get_io_service(); | |||
| } | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| /// Close the stream. | |||
| void close() | |||
| { | |||
| @@ -128,9 +111,10 @@ public: | |||
| } | |||
| /// Close the stream. | |||
| asio::error_code close(asio::error_code& ec) | |||
| ASIO_SYNC_OP_VOID close(asio::error_code& ec) | |||
| { | |||
| return next_layer_.close(ec); | |||
| next_layer_.close(ec); | |||
| ASIO_SYNC_OP_VOID_RETURN(ec); | |||
| } | |||
| /// Write the given data to the stream. Returns the number of bytes written. | |||
| @@ -158,14 +142,8 @@ public: | |||
| async_write_some(const ConstBufferSequence& buffers, | |||
| ASIO_MOVE_ARG(WriteHandler) handler) | |||
| { | |||
| async_completion<WriteHandler, | |||
| void (asio::error_code, std::size_t)> init(handler); | |||
| next_layer_.async_write_some(buffers, | |||
| ASIO_MOVE_CAST(ASIO_HANDLER_TYPE(WriteHandler, | |||
| void (asio::error_code, std::size_t)))(init.handler)); | |||
| return init.result.get(); | |||
| return next_layer_.async_write_some(buffers, | |||
| ASIO_MOVE_CAST(WriteHandler)(handler)); | |||
| } | |||
| /// Fill the buffer with some data. Returns the number of bytes placed in the | |||
| @@ -2,7 +2,7 @@ | |||
| // buffered_read_stream_fwd.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // buffered_stream.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) | |||
| @@ -23,7 +23,6 @@ | |||
| #include "asio/buffered_stream_fwd.hpp" | |||
| #include "asio/detail/noncopyable.hpp" | |||
| #include "asio/error.hpp" | |||
| #include "asio/io_context.hpp" | |||
| #include "asio/detail/push_options.hpp" | |||
| @@ -96,22 +95,6 @@ public: | |||
| return stream_impl_.lowest_layer().get_executor(); | |||
| } | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated: Use get_executor().) Get the io_context associated with the | |||
| /// object. | |||
| asio::io_context& get_io_context() | |||
| { | |||
| return stream_impl_.get_io_context(); | |||
| } | |||
| /// (Deprecated: Use get_executor().) Get the io_context associated with the | |||
| /// object. | |||
| asio::io_context& get_io_service() | |||
| { | |||
| return stream_impl_.get_io_service(); | |||
| } | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| /// Close the stream. | |||
| void close() | |||
| { | |||
| @@ -119,9 +102,10 @@ public: | |||
| } | |||
| /// Close the stream. | |||
| asio::error_code close(asio::error_code& ec) | |||
| ASIO_SYNC_OP_VOID close(asio::error_code& ec) | |||
| { | |||
| return stream_impl_.close(ec); | |||
| stream_impl_.close(ec); | |||
| ASIO_SYNC_OP_VOID_RETURN(ec); | |||
| } | |||
| /// Flush all data from the buffer to the next layer. Returns the number of | |||
| @@ -2,7 +2,7 @@ | |||
| // buffered_stream_fwd.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // buffered_write_stream.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) | |||
| @@ -25,7 +25,6 @@ | |||
| #include "asio/detail/noncopyable.hpp" | |||
| #include "asio/detail/type_traits.hpp" | |||
| #include "asio/error.hpp" | |||
| #include "asio/io_context.hpp" | |||
| #include "asio/write.hpp" | |||
| #include "asio/detail/push_options.hpp" | |||
| @@ -105,22 +104,6 @@ public: | |||
| return next_layer_.lowest_layer().get_executor(); | |||
| } | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated: Use get_executor().) Get the io_context associated with the | |||
| /// object. | |||
| asio::io_context& get_io_context() | |||
| { | |||
| return next_layer_.get_io_context(); | |||
| } | |||
| /// (Deprecated: Use get_executor().) Get the io_context associated with the | |||
| /// object. | |||
| asio::io_context& get_io_service() | |||
| { | |||
| return next_layer_.get_io_service(); | |||
| } | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| /// Close the stream. | |||
| void close() | |||
| { | |||
| @@ -128,9 +111,10 @@ public: | |||
| } | |||
| /// Close the stream. | |||
| asio::error_code close(asio::error_code& ec) | |||
| ASIO_SYNC_OP_VOID close(asio::error_code& ec) | |||
| { | |||
| return next_layer_.close(ec); | |||
| next_layer_.close(ec); | |||
| ASIO_SYNC_OP_VOID_RETURN(ec); | |||
| } | |||
| /// Flush all data from the buffer to the next layer. Returns the number of | |||
| @@ -193,14 +177,8 @@ public: | |||
| async_read_some(const MutableBufferSequence& buffers, | |||
| ASIO_MOVE_ARG(ReadHandler) handler) | |||
| { | |||
| async_completion<ReadHandler, | |||
| void (asio::error_code, std::size_t)> init(handler); | |||
| next_layer_.async_read_some(buffers, | |||
| ASIO_MOVE_CAST(ASIO_HANDLER_TYPE(ReadHandler, | |||
| void (asio::error_code, std::size_t)))(init.handler)); | |||
| return init.result.get(); | |||
| return next_layer_.async_read_some(buffers, | |||
| ASIO_MOVE_CAST(ReadHandler)(handler)); | |||
| } | |||
| /// Peek at the incoming data on the stream. Returns the number of bytes read. | |||
| @@ -2,7 +2,7 @@ | |||
| // buffered_write_stream_fwd.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // buffers_iterator.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) | |||
| @@ -65,7 +65,44 @@ namespace detail | |||
| typedef buffers_iterator_types_helper<is_mutable> helper; | |||
| typedef typename helper::buffer_type buffer_type; | |||
| typedef typename helper::template byte_type<ByteType>::type byte_type; | |||
| typedef typename BufferSequence::const_iterator const_iterator; | |||
| }; | |||
| template <typename ByteType> | |||
| struct buffers_iterator_types<mutable_buffer, ByteType> | |||
| { | |||
| typedef mutable_buffer buffer_type; | |||
| typedef ByteType byte_type; | |||
| typedef const mutable_buffer* const_iterator; | |||
| }; | |||
| template <typename ByteType> | |||
| struct buffers_iterator_types<const_buffer, ByteType> | |||
| { | |||
| typedef const_buffer buffer_type; | |||
| typedef typename add_const<ByteType>::type byte_type; | |||
| typedef const const_buffer* const_iterator; | |||
| }; | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| template <typename ByteType> | |||
| struct buffers_iterator_types<mutable_buffers_1, ByteType> | |||
| { | |||
| typedef mutable_buffer buffer_type; | |||
| typedef ByteType byte_type; | |||
| typedef const mutable_buffer* const_iterator; | |||
| }; | |||
| template <typename ByteType> | |||
| struct buffers_iterator_types<const_buffers_1, ByteType> | |||
| { | |||
| typedef const_buffer buffer_type; | |||
| typedef typename add_const<ByteType>::type byte_type; | |||
| typedef const const_buffer* const_iterator; | |||
| }; | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| } | |||
| /// A random access iterator over the bytes in a buffer sequence. | |||
| @@ -76,6 +113,9 @@ private: | |||
| typedef typename detail::buffers_iterator_types< | |||
| BufferSequence, ByteType>::buffer_type buffer_type; | |||
| typedef typename detail::buffers_iterator_types<BufferSequence, | |||
| ByteType>::const_iterator buffer_sequence_iterator_type; | |||
| public: | |||
| /// The type used for the distance between two iterators. | |||
| typedef std::ptrdiff_t difference_type; | |||
| @@ -130,9 +170,9 @@ public: | |||
| #endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) | |||
| { | |||
| buffers_iterator new_iter; | |||
| new_iter.begin_ = buffers.begin(); | |||
| new_iter.current_ = buffers.begin(); | |||
| new_iter.end_ = buffers.end(); | |||
| new_iter.begin_ = asio::buffer_sequence_begin(buffers); | |||
| new_iter.current_ = asio::buffer_sequence_begin(buffers); | |||
| new_iter.end_ = asio::buffer_sequence_end(buffers); | |||
| while (new_iter.current_ != new_iter.end_) | |||
| { | |||
| new_iter.current_buffer_ = *new_iter.current_; | |||
| @@ -150,9 +190,9 @@ public: | |||
| #endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) | |||
| { | |||
| buffers_iterator new_iter; | |||
| new_iter.begin_ = buffers.begin(); | |||
| new_iter.current_ = buffers.begin(); | |||
| new_iter.end_ = buffers.end(); | |||
| new_iter.begin_ = asio::buffer_sequence_begin(buffers); | |||
| new_iter.current_ = asio::buffer_sequence_begin(buffers); | |||
| new_iter.end_ = asio::buffer_sequence_end(buffers); | |||
| while (new_iter.current_ != new_iter.end_) | |||
| { | |||
| buffer_type buffer = *new_iter.current_; | |||
| @@ -347,7 +387,7 @@ private: | |||
| } | |||
| // Find the previous non-empty buffer. | |||
| typename BufferSequence::const_iterator iter = current_; | |||
| buffer_sequence_iterator_type iter = current_; | |||
| while (iter != begin_) | |||
| { | |||
| --iter; | |||
| @@ -426,7 +466,7 @@ private: | |||
| } | |||
| // Find the previous non-empty buffer. | |||
| typename BufferSequence::const_iterator iter = current_; | |||
| buffer_sequence_iterator_type iter = current_; | |||
| while (iter != begin_) | |||
| { | |||
| --iter; | |||
| @@ -452,9 +492,9 @@ private: | |||
| buffer_type current_buffer_; | |||
| std::size_t current_buffer_position_; | |||
| typename BufferSequence::const_iterator begin_; | |||
| typename BufferSequence::const_iterator current_; | |||
| typename BufferSequence::const_iterator end_; | |||
| buffer_sequence_iterator_type begin_; | |||
| buffer_sequence_iterator_type current_; | |||
| buffer_sequence_iterator_type end_; | |||
| std::size_t position_; | |||
| }; | |||
| @@ -0,0 +1,88 @@ | |||
| // | |||
| // co_spawn.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_CO_SPAWN_HPP | |||
| #define ASIO_CO_SPAWN_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 "asio/awaitable.hpp" | |||
| #include "asio/execution_context.hpp" | |||
| #include "asio/is_executor.hpp" | |||
| #include "asio/detail/push_options.hpp" | |||
| namespace asio { | |||
| namespace detail { | |||
| template <typename T> | |||
| struct awaitable_signature; | |||
| template <typename T, typename Executor> | |||
| struct awaitable_signature<awaitable<T, Executor>> | |||
| { | |||
| typedef void type(std::exception_ptr, T); | |||
| }; | |||
| template <typename Executor> | |||
| struct awaitable_signature<awaitable<void, Executor>> | |||
| { | |||
| typedef void type(std::exception_ptr); | |||
| }; | |||
| } // namespace detail | |||
| /// Spawn a new thread of execution. | |||
| /** | |||
| * The entry point function object @c f must have the signature: | |||
| * | |||
| * @code awaitable<void, E> f(); @endcode | |||
| * | |||
| * where @c E is convertible from @c Executor. | |||
| */ | |||
| template <typename Executor, typename F, typename CompletionToken> | |||
| ASIO_INITFN_RESULT_TYPE(CompletionToken, | |||
| typename detail::awaitable_signature<typename result_of<F()>::type>::type) | |||
| co_spawn(const Executor& ex, F&& f, CompletionToken&& token, | |||
| typename enable_if< | |||
| is_executor<Executor>::value | |||
| >::type* = 0); | |||
| /// Spawn a new thread of execution. | |||
| /** | |||
| * The entry point function object @c f must have the signature: | |||
| * | |||
| * @code awaitable<void, E> f(); @endcode | |||
| * | |||
| * where @c E is convertible from @c ExecutionContext::executor_type. | |||
| */ | |||
| template <typename ExecutionContext, typename F, typename CompletionToken> | |||
| ASIO_INITFN_RESULT_TYPE(CompletionToken, | |||
| typename detail::awaitable_signature<typename result_of<F()>::type>::type) | |||
| co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token, | |||
| typename enable_if< | |||
| is_convertible<ExecutionContext&, execution_context&>::value | |||
| >::type* = 0); | |||
| } // namespace asio | |||
| #include "asio/detail/pop_options.hpp" | |||
| #include "asio/impl/co_spawn.hpp" | |||
| #endif // defined(ASIO_HAS_CO_AWAIT) || defined(GENERATING_DOCUMENTATION) | |||
| #endif // ASIO_CO_SPAWN_HPP | |||
| @@ -2,7 +2,7 @@ | |||
| // completion_condition.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) | |||
| @@ -0,0 +1,136 @@ | |||
| // | |||
| // compose.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_COMPOSE_HPP | |||
| #define ASIO_COMPOSE_HPP | |||
| #if defined(_MSC_VER) && (_MSC_VER >= 1200) | |||
| # pragma once | |||
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |||
| #include "asio/detail/config.hpp" | |||
| #include "asio/async_result.hpp" | |||
| #include "asio/detail/push_options.hpp" | |||
| namespace asio { | |||
| #if defined(ASIO_HAS_VARIADIC_TEMPLATES) \ | |||
| || defined(GENERATING_DOCUMENTATION) | |||
| /// Launch an asynchronous operation with a stateful implementation. | |||
| /** | |||
| * The async_compose function simplifies the implementation of composed | |||
| * asynchronous operations automatically wrapping a stateful function object | |||
| * with a conforming intermediate completion handler. | |||
| * | |||
| * @param implementation A function object that contains the implementation of | |||
| * the composed asynchronous operation. The first argument to the function | |||
| * object is a non-const reference to the enclosing intermediate completion | |||
| * handler. The remaining arguments are any arguments that originate from the | |||
| * completion handlers of any asynchronous operations performed by the | |||
| * implementation. | |||
| * @param token The completion token. | |||
| * | |||
| * @param io_objects_or_executors Zero or more I/O objects or I/O executors for | |||
| * which outstanding work must be maintained. | |||
| * | |||
| * @par Example: | |||
| * | |||
| * @code struct async_echo_implementation | |||
| * { | |||
| * tcp::socket& socket_; | |||
| * asio::mutable_buffer buffer_; | |||
| * enum { starting, reading, writing } state_; | |||
| * | |||
| * template <typename Self> | |||
| * void operator()(Self& self, | |||
| * asio::error_code error = {}, | |||
| * std::size_t n = 0) | |||
| * { | |||
| * switch (state_) | |||
| * { | |||
| * case starting: | |||
| * state_ = reading; | |||
| * socket_.async_read_some( | |||
| * buffer_, std::move(self)); | |||
| * break; | |||
| * case reading: | |||
| * if (error) | |||
| * { | |||
| * self.complete(error, 0); | |||
| * } | |||
| * else | |||
| * { | |||
| * state_ = writing; | |||
| * asio::async_write(socket_, buffer_, | |||
| * asio::transfer_exactly(n), | |||
| * std::move(self)); | |||
| * } | |||
| * break; | |||
| * case writing: | |||
| * self.complete(error, n); | |||
| * break; | |||
| * } | |||
| * } | |||
| * }; | |||
| * | |||
| * template <typename CompletionToken> | |||
| * auto async_echo(tcp::socket& socket, | |||
| * asio::mutable_buffer buffer, | |||
| * CompletionToken&& token) -> | |||
| * typename asio::async_result< | |||
| * typename std::decay<CompletionToken>::type, | |||
| * void(asio::error_code, std::size_t)>::return_type | |||
| * { | |||
| * return asio::async_compose<CompletionToken, | |||
| * void(asio::error_code, std::size_t)>( | |||
| * async_echo_implementation{socket, buffer, | |||
| * async_echo_implementation::starting}, | |||
| * token, socket); | |||
| * } @endcode | |||
| */ | |||
| template <typename CompletionToken, typename Signature, | |||
| typename Implementation, typename... IoObjectsOrExecutors> | |||
| ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature) | |||
| async_compose(ASIO_MOVE_ARG(Implementation) implementation, | |||
| ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, | |||
| ASIO_MOVE_ARG(IoObjectsOrExecutors)... io_objects_or_executors); | |||
| #else // defined(ASIO_HAS_VARIADIC_TEMPLATES) | |||
| // || defined(GENERATING_DOCUMENTATION) | |||
| template <typename CompletionToken, typename Signature, typename Implementation> | |||
| ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature) | |||
| async_compose(ASIO_MOVE_ARG(Implementation) implementation, | |||
| ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token); | |||
| #define ASIO_PRIVATE_ASYNC_COMPOSE_DEF(n) \ | |||
| template <typename CompletionToken, typename Signature, \ | |||
| typename Implementation, ASIO_VARIADIC_TPARAMS(n)> \ | |||
| ASIO_INITFN_RESULT_TYPE(CompletionToken, Signature) \ | |||
| async_compose(ASIO_MOVE_ARG(Implementation) implementation, \ | |||
| ASIO_NONDEDUCED_MOVE_ARG(CompletionToken) token, \ | |||
| ASIO_VARIADIC_MOVE_PARAMS(n)); | |||
| /**/ | |||
| ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_ASYNC_COMPOSE_DEF) | |||
| #undef ASIO_PRIVATE_ASYNC_COMPOSE_DEF | |||
| #endif // defined(ASIO_HAS_VARIADIC_TEMPLATES) | |||
| // || defined(GENERATING_DOCUMENTATION) | |||
| } // namespace asio | |||
| #include "asio/detail/pop_options.hpp" | |||
| #include "asio/impl/compose.hpp" | |||
| #endif // ASIO_COMPOSE_HPP | |||
| @@ -2,7 +2,7 @@ | |||
| // connect.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) | |||
| @@ -58,7 +58,8 @@ struct is_endpoint_sequence | |||
| /** | |||
| * @defgroup connect asio::connect | |||
| * | |||
| * @brief Establishes a socket connection by trying each endpoint in a sequence. | |||
| * @brief The @c connect function is a composed operation that establishes a | |||
| * socket connection by trying each endpoint in a sequence. | |||
| */ | |||
| /*@{*/ | |||
| @@ -81,13 +82,13 @@ struct is_endpoint_sequence | |||
| * Otherwise, contains the error from the last connection attempt. | |||
| * | |||
| * @par Example | |||
| * @code tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * asio::connect(s, r.resolve(q)); @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, typename EndpointSequence> | |||
| typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||
| template <typename Protocol, typename Executor, typename EndpointSequence> | |||
| typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, | |||
| const EndpointSequence& endpoints, | |||
| typename enable_if<is_endpoint_sequence< | |||
| EndpointSequence>::value>::type* = 0); | |||
| @@ -112,9 +113,9 @@ typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||
| * default-constructed endpoint. | |||
| * | |||
| * @par Example | |||
| * @code tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * asio::error_code ec; | |||
| * asio::connect(s, r.resolve(q), ec); | |||
| * if (ec) | |||
| @@ -122,15 +123,15 @@ typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||
| * // An error occurred. | |||
| * } @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, typename EndpointSequence> | |||
| typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||
| template <typename Protocol, typename Executor, typename EndpointSequence> | |||
| typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, | |||
| const EndpointSequence& endpoints, asio::error_code& ec, | |||
| typename enable_if<is_endpoint_sequence< | |||
| EndpointSequence>::value>::type* = 0); | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated.) Establishes a socket connection by trying each endpoint in a | |||
| /// sequence. | |||
| /// (Deprecated: Use range overload.) Establishes a socket connection by trying | |||
| /// each endpoint in a sequence. | |||
| /** | |||
| * This function attempts to connect a socket to one of a sequence of | |||
| * endpoints. It does this by repeated calls to the socket's @c connect member | |||
| @@ -153,12 +154,12 @@ typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||
| * Iterator represents the end of the sequence. This is a valid assumption for | |||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | |||
| */ | |||
| template <typename Protocol, typename SocketService, typename Iterator> | |||
| Iterator connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| template <typename Protocol, typename Executor, typename Iterator> | |||
| Iterator connect(basic_socket<Protocol, Executor>& s, Iterator begin, | |||
| typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | |||
| /// (Deprecated.) Establishes a socket connection by trying each endpoint in a | |||
| /// sequence. | |||
| /// (Deprecated: Use range overload.) Establishes a socket connection by trying | |||
| /// each endpoint in a sequence. | |||
| /** | |||
| * This function attempts to connect a socket to one of a sequence of | |||
| * endpoints. It does this by repeated calls to the socket's @c connect member | |||
| @@ -181,8 +182,8 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| * Iterator represents the end of the sequence. This is a valid assumption for | |||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | |||
| */ | |||
| template <typename Protocol, typename SocketService, typename Iterator> | |||
| Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| template <typename Protocol, typename Executor, typename Iterator> | |||
| Iterator connect(basic_socket<Protocol, Executor>& s, | |||
| Iterator begin, asio::error_code& ec, | |||
| typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| @@ -208,14 +209,14 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| * Otherwise, contains the error from the last connection attempt. | |||
| * | |||
| * @par Example | |||
| * @code tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::resolver::results_type e = r.resolve(q); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * asio::connect(s, e.begin(), e.end()); @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, typename Iterator> | |||
| Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| template <typename Protocol, typename Executor, typename Iterator> | |||
| Iterator connect(basic_socket<Protocol, Executor>& s, | |||
| Iterator begin, Iterator end); | |||
| /// Establishes a socket connection by trying each endpoint in a sequence. | |||
| @@ -240,10 +241,10 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| * endpoint. Otherwise, the end iterator. | |||
| * | |||
| * @par Example | |||
| * @code tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::resolver::results_type e = r.resolve(q); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * asio::error_code ec; | |||
| * asio::connect(s, e.begin(), e.end(), ec); | |||
| * if (ec) | |||
| @@ -251,8 +252,8 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| * // An error occurred. | |||
| * } @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, typename Iterator> | |||
| Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| template <typename Protocol, typename Executor, typename Iterator> | |||
| Iterator connect(basic_socket<Protocol, Executor>& s, | |||
| Iterator begin, Iterator end, asio::error_code& ec); | |||
| /// Establishes a socket connection by trying each endpoint in a sequence. | |||
| @@ -299,16 +300,16 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| * } | |||
| * }; @endcode | |||
| * It would be used with the asio::connect function as follows: | |||
| * @code tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * tcp::endpoint e = asio::connect(s, | |||
| * r.resolve(q), my_connect_condition()); | |||
| * std::cout << "Connected to: " << e << std::endl; @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, | |||
| template <typename Protocol, typename Executor, | |||
| typename EndpointSequence, typename ConnectCondition> | |||
| typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||
| typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, | |||
| const EndpointSequence& endpoints, ConnectCondition connect_condition, | |||
| typename enable_if<is_endpoint_sequence< | |||
| EndpointSequence>::value>::type* = 0); | |||
| @@ -358,9 +359,9 @@ typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||
| * } | |||
| * }; @endcode | |||
| * It would be used with the asio::connect function as follows: | |||
| * @code tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * asio::error_code ec; | |||
| * tcp::endpoint e = asio::connect(s, | |||
| * r.resolve(q), my_connect_condition(), ec); | |||
| @@ -373,17 +374,17 @@ typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||
| * std::cout << "Connected to: " << e << std::endl; | |||
| * } @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, | |||
| template <typename Protocol, typename Executor, | |||
| typename EndpointSequence, typename ConnectCondition> | |||
| typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||
| typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s, | |||
| const EndpointSequence& endpoints, ConnectCondition connect_condition, | |||
| asio::error_code& ec, | |||
| typename enable_if<is_endpoint_sequence< | |||
| EndpointSequence>::value>::type* = 0); | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated.) Establishes a socket connection by trying each endpoint in a | |||
| /// sequence. | |||
| /// (Deprecated: Use range overload.) Establishes a socket connection by trying | |||
| /// each endpoint in a sequence. | |||
| /** | |||
| * This function attempts to connect a socket to one of a sequence of | |||
| * endpoints. It does this by repeated calls to the socket's @c connect member | |||
| @@ -417,14 +418,14 @@ typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||
| * Iterator represents the end of the sequence. This is a valid assumption for | |||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | |||
| */ | |||
| template <typename Protocol, typename SocketService, | |||
| template <typename Protocol, typename Executor, | |||
| typename Iterator, typename ConnectCondition> | |||
| Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| Iterator connect(basic_socket<Protocol, Executor>& s, | |||
| Iterator begin, ConnectCondition connect_condition, | |||
| typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | |||
| /// (Deprecated.) Establishes a socket connection by trying each endpoint in a | |||
| /// sequence. | |||
| /// (Deprecated: Use range overload.) Establishes a socket connection by trying | |||
| /// each endpoint in a sequence. | |||
| /** | |||
| * This function attempts to connect a socket to one of a sequence of | |||
| * endpoints. It does this by repeated calls to the socket's @c connect member | |||
| @@ -458,9 +459,9 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| * Iterator represents the end of the sequence. This is a valid assumption for | |||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | |||
| */ | |||
| template <typename Protocol, typename SocketService, | |||
| template <typename Protocol, typename Executor, | |||
| typename Iterator, typename ConnectCondition> | |||
| Iterator connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| Iterator connect(basic_socket<Protocol, Executor>& s, Iterator begin, | |||
| ConnectCondition connect_condition, asio::error_code& ec, | |||
| typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| @@ -511,17 +512,17 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| * } | |||
| * }; @endcode | |||
| * It would be used with the asio::connect function as follows: | |||
| * @code tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::resolver::results_type e = r.resolve(q); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * tcp::resolver::results_type::iterator i = asio::connect( | |||
| * s, e.begin(), e.end(), my_connect_condition()); | |||
| * std::cout << "Connected to: " << i->endpoint() << std::endl; @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, | |||
| template <typename Protocol, typename Executor, | |||
| typename Iterator, typename ConnectCondition> | |||
| Iterator connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| Iterator connect(basic_socket<Protocol, Executor>& s, Iterator begin, | |||
| Iterator end, ConnectCondition connect_condition); | |||
| /// Establishes a socket connection by trying each endpoint in a sequence. | |||
| @@ -571,10 +572,10 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| * } | |||
| * }; @endcode | |||
| * It would be used with the asio::connect function as follows: | |||
| * @code tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::resolver::results_type e = r.resolve(q); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * asio::error_code ec; | |||
| * tcp::resolver::results_type::iterator i = asio::connect( | |||
| * s, e.begin(), e.end(), my_connect_condition()); | |||
| @@ -587,9 +588,9 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| * std::cout << "Connected to: " << i->endpoint() << std::endl; | |||
| * } @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, | |||
| template <typename Protocol, typename Executor, | |||
| typename Iterator, typename ConnectCondition> | |||
| Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| Iterator connect(basic_socket<Protocol, Executor>& s, | |||
| Iterator begin, Iterator end, ConnectCondition connect_condition, | |||
| asio::error_code& ec); | |||
| @@ -598,8 +599,8 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| /** | |||
| * @defgroup async_connect asio::async_connect | |||
| * | |||
| * @brief Asynchronously establishes a socket connection by trying each | |||
| * endpoint in a sequence. | |||
| * @brief The @c async_connect function is a composed asynchronous operation | |||
| * that establishes a socket connection by trying each endpoint in a sequence. | |||
| */ | |||
| /*@{*/ | |||
| @@ -630,14 +631,14 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| * const typename Protocol::endpoint& endpoint | |||
| * ); @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 tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * | |||
| * // ... | |||
| * | |||
| @@ -664,19 +665,19 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||
| * // ... | |||
| * } @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, | |||
| template <typename Protocol, typename Executor, | |||
| typename EndpointSequence, typename RangeConnectHandler> | |||
| ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, | |||
| void (asio::error_code, typename Protocol::endpoint)) | |||
| async_connect(basic_socket<Protocol, SocketService>& s, | |||
| async_connect(basic_socket<Protocol, Executor>& s, | |||
| const EndpointSequence& endpoints, | |||
| ASIO_MOVE_ARG(RangeConnectHandler) handler, | |||
| typename enable_if<is_endpoint_sequence< | |||
| EndpointSequence>::value>::type* = 0); | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated.) Asynchronously establishes a socket connection by trying each | |||
| /// endpoint in a sequence. | |||
| /// (Deprecated: Use range overload.) Asynchronously establishes a socket | |||
| /// connection by trying each endpoint in a sequence. | |||
| /** | |||
| * This function attempts to connect a socket to one of a sequence of | |||
| * endpoints. It does this by repeated calls to the socket's @c async_connect | |||
| @@ -702,20 +703,20 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||
| * Iterator iterator | |||
| * ); @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 This overload assumes that a default constructed object of type @c | |||
| * Iterator represents the end of the sequence. This is a valid assumption for | |||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | |||
| */ | |||
| template <typename Protocol, typename SocketService, | |||
| template <typename Protocol, typename Executor, | |||
| typename Iterator, typename IteratorConnectHandler> | |||
| ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | |||
| void (asio::error_code, Iterator)) | |||
| async_connect(basic_socket<Protocol, SocketService>& s, | |||
| Iterator begin, ASIO_MOVE_ARG(IteratorConnectHandler) handler, | |||
| async_connect(basic_socket<Protocol, Executor>& s, Iterator begin, | |||
| ASIO_MOVE_ARG(IteratorConnectHandler) handler, | |||
| typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| @@ -748,13 +749,13 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||
| * Iterator iterator | |||
| * ); @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 std::vector<tcp::endpoint> endpoints = ...; | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * asio::async_connect(s, | |||
| * endpoints.begin(), endpoints.end(), | |||
| * connect_handler); | |||
| @@ -768,12 +769,11 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||
| * // ... | |||
| * } @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, | |||
| template <typename Protocol, typename Executor, | |||
| typename Iterator, typename IteratorConnectHandler> | |||
| ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | |||
| void (asio::error_code, Iterator)) | |||
| async_connect(basic_socket<Protocol, SocketService>& s, | |||
| Iterator begin, Iterator end, | |||
| async_connect(basic_socket<Protocol, Executor>& s, Iterator begin, Iterator end, | |||
| ASIO_MOVE_ARG(IteratorConnectHandler) handler); | |||
| /// Asynchronously establishes a socket connection by trying each endpoint in a | |||
| @@ -814,9 +814,9 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||
| * Iterator iterator | |||
| * ); @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 | |||
| * The following connect condition function object can be used to output | |||
| @@ -833,9 +833,9 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||
| * } | |||
| * }; @endcode | |||
| * It would be used with the asio::connect function as follows: | |||
| * @code tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * | |||
| * // ... | |||
| * | |||
| @@ -871,19 +871,19 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||
| * } | |||
| * } @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, typename EndpointSequence, | |||
| template <typename Protocol, typename Executor, typename EndpointSequence, | |||
| typename ConnectCondition, typename RangeConnectHandler> | |||
| ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, | |||
| void (asio::error_code, typename Protocol::endpoint)) | |||
| async_connect(basic_socket<Protocol, SocketService>& s, | |||
| async_connect(basic_socket<Protocol, Executor>& s, | |||
| const EndpointSequence& endpoints, ConnectCondition connect_condition, | |||
| ASIO_MOVE_ARG(RangeConnectHandler) handler, | |||
| typename enable_if<is_endpoint_sequence< | |||
| EndpointSequence>::value>::type* = 0); | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| /// (Deprecated.) Asynchronously establishes a socket connection by trying each | |||
| /// endpoint in a sequence. | |||
| /// (Deprecated: Use range overload.) Asynchronously establishes a socket | |||
| /// connection by trying each endpoint in a sequence. | |||
| /** | |||
| * This function attempts to connect a socket to one of a sequence of | |||
| * endpoints. It does this by repeated calls to the socket's @c async_connect | |||
| @@ -920,19 +920,19 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||
| * Iterator iterator | |||
| * ); @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 This overload assumes that a default constructed object of type @c | |||
| * Iterator represents the end of the sequence. This is a valid assumption for | |||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | |||
| */ | |||
| template <typename Protocol, typename SocketService, typename Iterator, | |||
| template <typename Protocol, typename Executor, typename Iterator, | |||
| typename ConnectCondition, typename IteratorConnectHandler> | |||
| ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | |||
| void (asio::error_code, Iterator)) | |||
| async_connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| async_connect(basic_socket<Protocol, Executor>& s, Iterator begin, | |||
| ConnectCondition connect_condition, | |||
| ASIO_MOVE_ARG(IteratorConnectHandler) handler, | |||
| typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | |||
| @@ -978,9 +978,9 @@ async_connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| * Iterator iterator | |||
| * ); @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 | |||
| * The following connect condition function object can be used to output | |||
| @@ -997,9 +997,9 @@ async_connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| * } | |||
| * }; @endcode | |||
| * It would be used with the asio::connect function as follows: | |||
| * @code tcp::resolver r(io_context); | |||
| * @code tcp::resolver r(my_context); | |||
| * tcp::resolver::query q("host", "service"); | |||
| * tcp::socket s(io_context); | |||
| * tcp::socket s(my_context); | |||
| * | |||
| * // ... | |||
| * | |||
| @@ -1036,12 +1036,12 @@ async_connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||
| * } | |||
| * } @endcode | |||
| */ | |||
| template <typename Protocol, typename SocketService, typename Iterator, | |||
| template <typename Protocol, typename Executor, typename Iterator, | |||
| typename ConnectCondition, typename IteratorConnectHandler> | |||
| ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | |||
| void (asio::error_code, Iterator)) | |||
| async_connect(basic_socket<Protocol, SocketService>& s, | |||
| Iterator begin, Iterator end, ConnectCondition connect_condition, | |||
| async_connect(basic_socket<Protocol, Executor>& s, Iterator begin, | |||
| Iterator end, ConnectCondition connect_condition, | |||
| ASIO_MOVE_ARG(IteratorConnectHandler) handler); | |||
| /*@}*/ | |||
| @@ -2,7 +2,7 @@ | |||
| // coroutine.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) | |||
| @@ -205,7 +205,7 @@ class coroutine_ref; | |||
| * { | |||
| * do | |||
| * { | |||
| * socket_.reset(new tcp::socket(io_context_)); | |||
| * socket_.reset(new tcp::socket(my_context_)); | |||
| * yield acceptor->async_accept(*socket_, *this); | |||
| * fork server(*this)(); | |||
| * } while (is_parent()); | |||
| @@ -227,7 +227,7 @@ class coroutine_ref; | |||
| * Note that @c fork doesn't do the actual forking by itself. It is the | |||
| * application's responsibility to create a clone of the coroutine and call it. | |||
| * The clone can be called immediately, as above, or scheduled for delayed | |||
| * execution using something like io_context::post(). | |||
| * execution using something like asio::post(). | |||
| * | |||
| * @par Alternate macro names | |||
| * | |||
| @@ -289,7 +289,7 @@ private: | |||
| bail_out_of_coroutine: \ | |||
| break; \ | |||
| } \ | |||
| else case 0: | |||
| else /* fall-through */ case 0: | |||
| #define ASIO_CORO_YIELD_IMPL(n) \ | |||
| for (_coro_value = (n);;) \ | |||
| @@ -301,12 +301,12 @@ private: | |||
| else \ | |||
| switch (_coro_value ? 0 : 1) \ | |||
| for (;;) \ | |||
| case -1: if (_coro_value) \ | |||
| /* fall-through */ case -1: if (_coro_value) \ | |||
| goto terminate_coroutine; \ | |||
| else for (;;) \ | |||
| case 1: if (_coro_value) \ | |||
| /* fall-through */ case 1: if (_coro_value) \ | |||
| goto bail_out_of_coroutine; \ | |||
| else case 0: | |||
| else /* fall-through */ case 0: | |||
| #define ASIO_CORO_FORK_IMPL(n) \ | |||
| for (_coro_value = -(n);; _coro_value = (n)) \ | |||
| @@ -1,438 +0,0 @@ | |||
| // | |||
| // datagram_socket_service.hpp | |||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
| // | |||
| // Copyright (c) 2003-2015 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_DATAGRAM_SOCKET_SERVICE_HPP | |||
| #define ASIO_DATAGRAM_SOCKET_SERVICE_HPP | |||
| #if defined(_MSC_VER) && (_MSC_VER >= 1200) | |||
| # pragma once | |||
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |||
| #include "asio/detail/config.hpp" | |||
| #include <cstddef> | |||
| #include "asio/async_result.hpp" | |||
| #include "asio/detail/type_traits.hpp" | |||
| #include "asio/error.hpp" | |||
| #include "asio/io_context.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 | |||
| #include "asio/detail/push_options.hpp" | |||
| namespace asio { | |||
| /// Default service implementation for a datagram socket. | |||
| template <typename Protocol> | |||
| class datagram_socket_service | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| : public asio::io_context::service | |||
| #else | |||
| : public asio::detail::service_base<datagram_socket_service<Protocol> > | |||
| #endif | |||
| { | |||
| public: | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| /// The unique service identifier. | |||
| static asio::io_context::id id; | |||
| #endif | |||
| /// The protocol type. | |||
| typedef Protocol protocol_type; | |||
| /// The endpoint type. | |||
| typedef typename Protocol::endpoint endpoint_type; | |||
| private: | |||
| // The type of the platform-specific implementation. | |||
| #if defined(ASIO_WINDOWS_RUNTIME) | |||
| typedef detail::null_socket_service<Protocol> service_impl_type; | |||
| #elif defined(ASIO_HAS_IOCP) | |||
| typedef detail::win_iocp_socket_service<Protocol> service_impl_type; | |||
| #else | |||
| typedef detail::reactive_socket_service<Protocol> service_impl_type; | |||
| #endif | |||
| public: | |||
| /// The type of a datagram socket. | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| typedef implementation_defined implementation_type; | |||
| #else | |||
| typedef typename service_impl_type::implementation_type implementation_type; | |||
| #endif | |||
| /// The native socket type. | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| typedef implementation_defined native_handle_type; | |||
| #else | |||
| typedef typename service_impl_type::native_handle_type native_handle_type; | |||
| #endif | |||
| /// Construct a new datagram socket service for the specified io_context. | |||
| explicit datagram_socket_service(asio::io_context& io_context) | |||
| : asio::detail::service_base< | |||
| datagram_socket_service<Protocol> >(io_context), | |||
| service_impl_(io_context) | |||
| { | |||
| } | |||
| /// Construct a new datagram socket implementation. | |||
| void construct(implementation_type& impl) | |||
| { | |||
| service_impl_.construct(impl); | |||
| } | |||
| #if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | |||
| /// Move-construct a new datagram socket implementation. | |||
| void move_construct(implementation_type& impl, | |||
| implementation_type& other_impl) | |||
| { | |||
| service_impl_.move_construct(impl, other_impl); | |||
| } | |||
| /// Move-assign from another datagram socket implementation. | |||
| void move_assign(implementation_type& impl, | |||
| datagram_socket_service& other_service, | |||
| implementation_type& other_impl) | |||
| { | |||
| service_impl_.move_assign(impl, other_service.service_impl_, other_impl); | |||
| } | |||
| /// Move-construct a new datagram socket implementation from another protocol | |||
| /// type. | |||
| template <typename Protocol1> | |||
| void converting_move_construct(implementation_type& impl, | |||
| typename datagram_socket_service< | |||
| Protocol1>::implementation_type& other_impl, | |||
| typename enable_if<is_convertible< | |||
| Protocol1, Protocol>::value>::type* = 0) | |||
| { | |||
| service_impl_.template converting_move_construct<Protocol1>( | |||
| impl, other_impl); | |||
| } | |||
| #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | |||
| /// Destroy a datagram socket implementation. | |||
| void destroy(implementation_type& impl) | |||
| { | |||
| service_impl_.destroy(impl); | |||
| } | |||
| // Open a new datagram socket implementation. | |||
| asio::error_code open(implementation_type& impl, | |||
| const protocol_type& protocol, asio::error_code& ec) | |||
| { | |||
| if (protocol.type() == ASIO_OS_DEF(SOCK_DGRAM)) | |||
| service_impl_.open(impl, protocol, ec); | |||
| else | |||
| ec = asio::error::invalid_argument; | |||
| return ec; | |||
| } | |||
| /// Assign an existing native socket to a datagram socket. | |||
| asio::error_code assign(implementation_type& impl, | |||
| const protocol_type& protocol, const native_handle_type& native_socket, | |||
| asio::error_code& ec) | |||
| { | |||
| return service_impl_.assign(impl, protocol, native_socket, ec); | |||
| } | |||
| /// Determine whether the socket is open. | |||
| bool is_open(const implementation_type& impl) const | |||
| { | |||
| return service_impl_.is_open(impl); | |||
| } | |||
| /// Close a datagram socket implementation. | |||
| asio::error_code close(implementation_type& impl, | |||
| asio::error_code& ec) | |||
| { | |||
| return service_impl_.close(impl, ec); | |||
| } | |||
| /// Get the native socket implementation. | |||
| native_handle_type native_handle(implementation_type& impl) | |||
| { | |||
| return service_impl_.native_handle(impl); | |||
| } | |||
| /// Cancel all asynchronous operations associated with the socket. | |||
| asio::error_code cancel(implementation_type& impl, | |||
| asio::error_code& ec) | |||
| { | |||
| return service_impl_.cancel(impl, ec); | |||
| } | |||
| /// Determine whether the socket is at the out-of-band data mark. | |||
| bool at_mark(const implementation_type& impl, | |||
| asio::error_code& ec) const | |||
| { | |||
| return service_impl_.at_mark(impl, ec); | |||
| } | |||
| /// Determine the number of bytes available for reading. | |||
| std::size_t available(const implementation_type& impl, | |||
| asio::error_code& ec) const | |||
| { | |||
| return service_impl_.available(impl, ec); | |||
| } | |||
| // Bind the datagram socket to the specified local endpoint. | |||
| asio::error_code bind(implementation_type& impl, | |||
| const endpoint_type& endpoint, asio::error_code& ec) | |||
| { | |||
| return service_impl_.bind(impl, endpoint, ec); | |||
| } | |||
| /// Connect the datagram socket to the specified endpoint. | |||
| asio::error_code connect(implementation_type& impl, | |||
| const endpoint_type& peer_endpoint, asio::error_code& ec) | |||
| { | |||
| return service_impl_.connect(impl, peer_endpoint, ec); | |||
| } | |||
| /// Start an asynchronous connect. | |||
| template <typename ConnectHandler> | |||
| ASIO_INITFN_RESULT_TYPE(ConnectHandler, | |||
| void (asio::error_code)) | |||
| async_connect(implementation_type& impl, | |||
| const endpoint_type& peer_endpoint, | |||
| ASIO_MOVE_ARG(ConnectHandler) handler) | |||
| { | |||
| async_completion<ConnectHandler, | |||
| void (asio::error_code)> init(handler); | |||
| service_impl_.async_connect(impl, peer_endpoint, init.handler); | |||
| return init.result.get(); | |||
| } | |||
| /// Set a socket option. | |||
| template <typename SettableSocketOption> | |||
| asio::error_code set_option(implementation_type& impl, | |||
| const SettableSocketOption& option, asio::error_code& ec) | |||
| { | |||
| return service_impl_.set_option(impl, option, ec); | |||
| } | |||
| /// Get a socket option. | |||
| template <typename GettableSocketOption> | |||
| asio::error_code get_option(const implementation_type& impl, | |||
| GettableSocketOption& option, asio::error_code& ec) const | |||
| { | |||
| return service_impl_.get_option(impl, option, ec); | |||
| } | |||
| /// Perform an IO control command on the socket. | |||
| template <typename IoControlCommand> | |||
| asio::error_code io_control(implementation_type& impl, | |||
| IoControlCommand& command, asio::error_code& ec) | |||
| { | |||
| return service_impl_.io_control(impl, command, ec); | |||
| } | |||
| /// Gets the non-blocking mode of the socket. | |||
| bool non_blocking(const implementation_type& impl) const | |||
| { | |||
| return service_impl_.non_blocking(impl); | |||
| } | |||
| /// Sets the non-blocking mode of the socket. | |||
| asio::error_code non_blocking(implementation_type& impl, | |||
| bool mode, asio::error_code& ec) | |||
| { | |||
| return service_impl_.non_blocking(impl, mode, ec); | |||
| } | |||
| /// Gets the non-blocking mode of the native socket implementation. | |||
| bool native_non_blocking(const implementation_type& impl) const | |||
| { | |||
| return service_impl_.native_non_blocking(impl); | |||
| } | |||
| /// Sets the non-blocking mode of the native socket implementation. | |||
| asio::error_code native_non_blocking(implementation_type& impl, | |||
| bool mode, asio::error_code& ec) | |||
| { | |||
| return service_impl_.native_non_blocking(impl, mode, ec); | |||
| } | |||
| /// Get the local endpoint. | |||
| endpoint_type local_endpoint(const implementation_type& impl, | |||
| asio::error_code& ec) const | |||
| { | |||
| return service_impl_.local_endpoint(impl, ec); | |||
| } | |||
| /// Get the remote endpoint. | |||
| endpoint_type remote_endpoint(const implementation_type& impl, | |||
| asio::error_code& ec) const | |||
| { | |||
| return service_impl_.remote_endpoint(impl, ec); | |||
| } | |||
| /// Disable sends or receives on the socket. | |||
| asio::error_code shutdown(implementation_type& impl, | |||
| socket_base::shutdown_type what, asio::error_code& ec) | |||
| { | |||
| return service_impl_.shutdown(impl, what, ec); | |||
| } | |||
| /// Wait for the socket to become ready to read, ready to write, or to have | |||
| /// pending error conditions. | |||
| asio::error_code wait(implementation_type& impl, | |||
| socket_base::wait_type w, asio::error_code& ec) | |||
| { | |||
| return service_impl_.wait(impl, w, ec); | |||
| } | |||
| /// Asynchronously wait for the socket to become ready to read, ready to | |||
| /// write, or to have pending error conditions. | |||
| template <typename WaitHandler> | |||
| ASIO_INITFN_RESULT_TYPE(WaitHandler, | |||
| void (asio::error_code)) | |||
| async_wait(implementation_type& impl, socket_base::wait_type w, | |||
| ASIO_MOVE_ARG(WaitHandler) handler) | |||
| { | |||
| async_completion<WaitHandler, | |||
| void (asio::error_code)> init(handler); | |||
| service_impl_.async_wait(impl, w, init.handler); | |||
| return init.result.get(); | |||
| } | |||
| /// Send the given data to the peer. | |||
| template <typename ConstBufferSequence> | |||
| std::size_t send(implementation_type& impl, | |||
| const ConstBufferSequence& buffers, | |||
| socket_base::message_flags flags, asio::error_code& ec) | |||
| { | |||
| return service_impl_.send(impl, buffers, flags, ec); | |||
| } | |||
| /// Start an asynchronous send. | |||
| template <typename ConstBufferSequence, typename WriteHandler> | |||
| ASIO_INITFN_RESULT_TYPE(WriteHandler, | |||
| void (asio::error_code, std::size_t)) | |||
| async_send(implementation_type& impl, const ConstBufferSequence& buffers, | |||
| socket_base::message_flags flags, | |||
| ASIO_MOVE_ARG(WriteHandler) handler) | |||
| { | |||
| async_completion<WriteHandler, | |||
| void (asio::error_code, std::size_t)> init(handler); | |||
| service_impl_.async_send(impl, buffers, flags, init.handler); | |||
| return init.result.get(); | |||
| } | |||
| /// Send a datagram to the specified endpoint. | |||
| template <typename ConstBufferSequence> | |||
| std::size_t send_to(implementation_type& impl, | |||
| const ConstBufferSequence& buffers, const endpoint_type& destination, | |||
| socket_base::message_flags flags, asio::error_code& ec) | |||
| { | |||
| return service_impl_.send_to(impl, buffers, destination, flags, ec); | |||
| } | |||
| /// Start an asynchronous send. | |||
| template <typename ConstBufferSequence, typename WriteHandler> | |||
| ASIO_INITFN_RESULT_TYPE(WriteHandler, | |||
| void (asio::error_code, std::size_t)) | |||
| async_send_to(implementation_type& impl, | |||
| const ConstBufferSequence& buffers, const endpoint_type& destination, | |||
| socket_base::message_flags flags, | |||
| ASIO_MOVE_ARG(WriteHandler) handler) | |||
| { | |||
| async_completion<WriteHandler, | |||
| void (asio::error_code, std::size_t)> init(handler); | |||
| service_impl_.async_send_to(impl, buffers, | |||
| destination, flags, init.handler); | |||
| return init.result.get(); | |||
| } | |||
| /// Receive some data from the peer. | |||
| template <typename MutableBufferSequence> | |||
| std::size_t receive(implementation_type& impl, | |||
| const MutableBufferSequence& buffers, | |||
| socket_base::message_flags flags, asio::error_code& ec) | |||
| { | |||
| return service_impl_.receive(impl, buffers, flags, ec); | |||
| } | |||
| /// Start an asynchronous receive. | |||
| template <typename MutableBufferSequence, typename ReadHandler> | |||
| ASIO_INITFN_RESULT_TYPE(ReadHandler, | |||
| void (asio::error_code, std::size_t)) | |||
| async_receive(implementation_type& impl, | |||
| const MutableBufferSequence& buffers, | |||
| socket_base::message_flags flags, | |||
| ASIO_MOVE_ARG(ReadHandler) handler) | |||
| { | |||
| async_completion<ReadHandler, | |||
| void (asio::error_code, std::size_t)> init(handler); | |||
| service_impl_.async_receive(impl, buffers, flags, init.handler); | |||
| return init.result.get(); | |||
| } | |||
| /// Receive a datagram with the endpoint of the sender. | |||
| template <typename MutableBufferSequence> | |||
| std::size_t receive_from(implementation_type& impl, | |||
| const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, | |||
| socket_base::message_flags flags, asio::error_code& ec) | |||
| { | |||
| return service_impl_.receive_from(impl, buffers, sender_endpoint, flags, | |||
| ec); | |||
| } | |||
| /// Start an asynchronous receive that will get the endpoint of the sender. | |||
| template <typename MutableBufferSequence, typename ReadHandler> | |||
| ASIO_INITFN_RESULT_TYPE(ReadHandler, | |||
| void (asio::error_code, std::size_t)) | |||
| async_receive_from(implementation_type& impl, | |||
| const MutableBufferSequence& buffers, endpoint_type& sender_endpoint, | |||
| socket_base::message_flags flags, | |||
| ASIO_MOVE_ARG(ReadHandler) handler) | |||
| { | |||
| async_completion<ReadHandler, | |||
| void (asio::error_code, std::size_t)> init(handler); | |||
| service_impl_.async_receive_from(impl, buffers, | |||
| sender_endpoint, flags, init.handler); | |||
| return init.result.get(); | |||
| } | |||
| private: | |||
| // Destroy all user-defined handler objects owned by the service. | |||
| void shutdown() | |||
| { | |||
| service_impl_.shutdown(); | |||
| } | |||
| // The platform-specific implementation. | |||
| service_impl_type service_impl_; | |||
| }; | |||
| } // namespace asio | |||
| #include "asio/detail/pop_options.hpp" | |||
| #endif // ASIO_DATAGRAM_SOCKET_SERVICE_HPP | |||
| @@ -2,7 +2,7 @@ | |||
| // 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) | |||
| @@ -1,170 +0,0 @@ | |||
| // | |||
| // deadline_timer_service.hpp | |||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
| // | |||
| // Copyright (c) 2003-2015 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_DEADLINE_TIMER_SERVICE_HPP | |||
| #define ASIO_DEADLINE_TIMER_SERVICE_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_BOOST_DATE_TIME) \ | |||
| || defined(GENERATING_DOCUMENTATION) | |||
| #include <cstddef> | |||
| #include "asio/async_result.hpp" | |||
| #include "asio/detail/deadline_timer_service.hpp" | |||
| #include "asio/io_context.hpp" | |||
| #include "asio/time_traits.hpp" | |||
| #include "asio/detail/timer_queue_ptime.hpp" | |||
| #include "asio/detail/push_options.hpp" | |||
| namespace asio { | |||
| /// Default service implementation for a timer. | |||
| template <typename TimeType, | |||
| typename TimeTraits = asio::time_traits<TimeType> > | |||
| class deadline_timer_service | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| : public asio::io_context::service | |||
| #else | |||
| : public asio::detail::service_base< | |||
| deadline_timer_service<TimeType, TimeTraits> > | |||
| #endif | |||
| { | |||
| public: | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| /// The unique service identifier. | |||
| static asio::io_context::id id; | |||
| #endif | |||
| /// The time traits type. | |||
| typedef TimeTraits traits_type; | |||
| /// The time type. | |||
| typedef typename traits_type::time_type time_type; | |||
| /// The duration type. | |||
| typedef typename traits_type::duration_type duration_type; | |||
| private: | |||
| // The type of the platform-specific implementation. | |||
| typedef detail::deadline_timer_service<traits_type> service_impl_type; | |||
| public: | |||
| /// The implementation type of the deadline timer. | |||
| #if defined(GENERATING_DOCUMENTATION) | |||
| typedef implementation_defined implementation_type; | |||
| #else | |||
| typedef typename service_impl_type::implementation_type implementation_type; | |||
| #endif | |||
| /// Construct a new timer service for the specified io_context. | |||
| explicit deadline_timer_service(asio::io_context& io_context) | |||
| : asio::detail::service_base< | |||
| deadline_timer_service<TimeType, TimeTraits> >(io_context), | |||
| service_impl_(io_context) | |||
| { | |||
| } | |||
| /// Construct a new timer implementation. | |||
| void construct(implementation_type& impl) | |||
| { | |||
| service_impl_.construct(impl); | |||
| } | |||
| /// Destroy a timer implementation. | |||
| void destroy(implementation_type& impl) | |||
| { | |||
| service_impl_.destroy(impl); | |||
| } | |||
| /// Cancel any asynchronous wait operations associated with the timer. | |||
| std::size_t cancel(implementation_type& impl, asio::error_code& ec) | |||
| { | |||
| return service_impl_.cancel(impl, ec); | |||
| } | |||
| /// Cancels one asynchronous wait operation associated with the timer. | |||
| std::size_t cancel_one(implementation_type& impl, | |||
| asio::error_code& ec) | |||
| { | |||
| return service_impl_.cancel_one(impl, ec); | |||
| } | |||
| /// Get the expiry time for the timer as an absolute time. | |||
| time_type expires_at(const implementation_type& impl) const | |||
| { | |||
| return service_impl_.expiry(impl); | |||
| } | |||
| /// Set the expiry time for the timer as an absolute time. | |||
| std::size_t expires_at(implementation_type& impl, | |||
| const time_type& expiry_time, asio::error_code& ec) | |||
| { | |||
| return service_impl_.expires_at(impl, expiry_time, ec); | |||
| } | |||
| /// Get the expiry time for the timer relative to now. | |||
| duration_type expires_from_now(const implementation_type& impl) const | |||
| { | |||
| return TimeTraits::subtract(service_impl_.expiry(impl), TimeTraits::now()); | |||
| } | |||
| /// Set the expiry time for the timer relative to now. | |||
| std::size_t expires_from_now(implementation_type& impl, | |||
| const duration_type& expiry_time, asio::error_code& ec) | |||
| { | |||
| return service_impl_.expires_after(impl, expiry_time, ec); | |||
| } | |||
| // Perform a blocking wait on the timer. | |||
| void wait(implementation_type& impl, asio::error_code& ec) | |||
| { | |||
| service_impl_.wait(impl, ec); | |||
| } | |||
| // Start an asynchronous wait on the timer. | |||
| template <typename WaitHandler> | |||
| ASIO_INITFN_RESULT_TYPE(WaitHandler, | |||
| void (asio::error_code)) | |||
| async_wait(implementation_type& impl, | |||
| ASIO_MOVE_ARG(WaitHandler) handler) | |||
| { | |||
| async_completion<WaitHandler, | |||
| void (asio::error_code)> init(handler); | |||
| service_impl_.async_wait(impl, init.handler); | |||
| return init.result.get(); | |||
| } | |||
| private: | |||
| // Destroy all user-defined handler objects owned by the service. | |||
| void shutdown() | |||
| { | |||
| service_impl_.shutdown(); | |||
| } | |||
| // The platform-specific implementation. | |||
| service_impl_type service_impl_; | |||
| }; | |||
| } // namespace asio | |||
| #include "asio/detail/pop_options.hpp" | |||
| #endif // defined(ASIO_HAS_BOOST_DATE_TIME) | |||
| // || defined(GENERATING_DOCUMENTATION) | |||
| #endif // ASIO_DEADLINE_TIMER_SERVICE_HPP | |||
| @@ -2,7 +2,7 @@ | |||
| // defer.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) | |||
| @@ -31,6 +31,11 @@ namespace asio { | |||
| * executor. The function object is queued for execution, and is never called | |||
| * from the current thread prior to returning from <tt>defer()</tt>. | |||
| * | |||
| * The use of @c defer(), rather than @ref post(), indicates the caller's | |||
| * preference that the executor defer the queueing of the function object. This | |||
| * may allow the executor to optimise queueing for cases when the function | |||
| * object represents a continuation of the current call context. | |||
| * | |||
| * This function has the following effects: | |||
| * | |||
| * @li Constructs a function object handler of type @c Handler, initialized | |||
| @@ -59,6 +64,11 @@ ASIO_INITFN_RESULT_TYPE(CompletionToken, void()) defer( | |||
| * The function object is queued for execution, and is never called from the | |||
| * current thread prior to returning from <tt>defer()</tt>. | |||
| * | |||
| * The use of @c defer(), rather than @ref post(), indicates the caller's | |||
| * preference that the executor defer the queueing of the function object. This | |||
| * may allow the executor to optimise queueing for cases when the function | |||
| * object represents a continuation of the current call context. | |||
| * | |||
| * This function has the following effects: | |||
| * | |||
| * @li Constructs a function object handler of type @c Handler, initialized | |||
| @@ -0,0 +1,62 @@ | |||
| // | |||
| // detached.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_DETACHED_HPP | |||
| #define ASIO_DETACHED_HPP | |||
| #if defined(_MSC_VER) && (_MSC_VER >= 1200) | |||
| # pragma once | |||
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |||
| #include "asio/detail/config.hpp" | |||
| #include <memory> | |||
| #include "asio/detail/push_options.hpp" | |||
| namespace asio { | |||
| /// Class used to specify that an asynchronous operation is detached. | |||
| /** | |||
| * The detached_t class is used to indicate that an asynchronous operation is | |||
| * detached. That is, there is no completion handler waiting for the | |||
| * operation's result. A detached_t object may be passed as a handler to an | |||
| * asynchronous operation, typically using the special value | |||
| * @c asio::detached. For example: | |||
| * @code my_socket.async_send(my_buffer, asio::detached); | |||
| * @endcode | |||
| */ | |||
| class detached_t | |||
| { | |||
| public: | |||
| /// Constructor. | |||
| ASIO_CONSTEXPR detached_t() | |||
| { | |||
| } | |||
| }; | |||
| /// A special value, similar to std::nothrow. | |||
| /** | |||
| * See the documentation for asio::detached_t for a usage example. | |||
| */ | |||
| #if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION) | |||
| constexpr detached_t detached; | |||
| #elif defined(ASIO_MSVC) | |||
| __declspec(selectany) detached_t detached; | |||
| #endif | |||
| } // namespace asio | |||
| #include "asio/detail/pop_options.hpp" | |||
| #include "asio/impl/detached.hpp" | |||
| #endif // ASIO_DETACHED_HPP | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/array.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/array_fwd.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/assert.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/atomic_count.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/base_from_completion_cond.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) | |||
| @@ -27,8 +27,9 @@ template <typename CompletionCondition> | |||
| class base_from_completion_cond | |||
| { | |||
| protected: | |||
| explicit base_from_completion_cond(CompletionCondition completion_condition) | |||
| : completion_condition_(completion_condition) | |||
| explicit base_from_completion_cond(CompletionCondition& completion_condition) | |||
| : completion_condition_( | |||
| ASIO_MOVE_CAST(CompletionCondition)(completion_condition)) | |||
| { | |||
| } | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/bind_handler.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) | |||
| @@ -223,6 +223,363 @@ inline binder2<typename decay<Handler>::type, Arg1, Arg2> bind_handler( | |||
| ASIO_MOVE_CAST(Handler)(handler), arg1, arg2); | |||
| } | |||
| template <typename Handler, typename Arg1, typename Arg2, typename Arg3> | |||
| class binder3 | |||
| { | |||
| public: | |||
| template <typename T> | |||
| binder3(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1, | |||
| const Arg2& arg2, const Arg3& arg3) | |||
| : handler_(ASIO_MOVE_CAST(T)(handler)), | |||
| arg1_(arg1), | |||
| arg2_(arg2), | |||
| arg3_(arg3) | |||
| { | |||
| } | |||
| binder3(Handler& handler, const Arg1& arg1, | |||
| const Arg2& arg2, const Arg3& arg3) | |||
| : handler_(ASIO_MOVE_CAST(Handler)(handler)), | |||
| arg1_(arg1), | |||
| arg2_(arg2), | |||
| arg3_(arg3) | |||
| { | |||
| } | |||
| #if defined(ASIO_HAS_MOVE) | |||
| binder3(const binder3& other) | |||
| : handler_(other.handler_), | |||
| arg1_(other.arg1_), | |||
| arg2_(other.arg2_), | |||
| arg3_(other.arg3_) | |||
| { | |||
| } | |||
| binder3(binder3&& other) | |||
| : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), | |||
| arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), | |||
| arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)), | |||
| arg3_(ASIO_MOVE_CAST(Arg3)(other.arg3_)) | |||
| { | |||
| } | |||
| #endif // defined(ASIO_HAS_MOVE) | |||
| void operator()() | |||
| { | |||
| handler_(static_cast<const Arg1&>(arg1_), | |||
| static_cast<const Arg2&>(arg2_), static_cast<const Arg3&>(arg3_)); | |||
| } | |||
| void operator()() const | |||
| { | |||
| handler_(arg1_, arg2_, arg3_); | |||
| } | |||
| //private: | |||
| Handler handler_; | |||
| Arg1 arg1_; | |||
| Arg2 arg2_; | |||
| Arg3 arg3_; | |||
| }; | |||
| template <typename Handler, typename Arg1, typename Arg2, typename Arg3> | |||
| inline void* asio_handler_allocate(std::size_t size, | |||
| binder3<Handler, Arg1, Arg2, Arg3>* this_handler) | |||
| { | |||
| return asio_handler_alloc_helpers::allocate( | |||
| size, this_handler->handler_); | |||
| } | |||
| template <typename Handler, typename Arg1, typename Arg2, typename Arg3> | |||
| inline void asio_handler_deallocate(void* pointer, std::size_t size, | |||
| binder3<Handler, Arg1, Arg2, Arg3>* this_handler) | |||
| { | |||
| asio_handler_alloc_helpers::deallocate( | |||
| pointer, size, this_handler->handler_); | |||
| } | |||
| template <typename Handler, typename Arg1, typename Arg2, typename Arg3> | |||
| inline bool asio_handler_is_continuation( | |||
| binder3<Handler, Arg1, Arg2, Arg3>* this_handler) | |||
| { | |||
| return asio_handler_cont_helpers::is_continuation( | |||
| this_handler->handler_); | |||
| } | |||
| template <typename Function, typename Handler, | |||
| typename Arg1, typename Arg2, typename Arg3> | |||
| inline void asio_handler_invoke(Function& function, | |||
| binder3<Handler, Arg1, Arg2, Arg3>* this_handler) | |||
| { | |||
| asio_handler_invoke_helpers::invoke( | |||
| function, this_handler->handler_); | |||
| } | |||
| template <typename Function, typename Handler, | |||
| typename Arg1, typename Arg2, typename Arg3> | |||
| inline void asio_handler_invoke(const Function& function, | |||
| binder3<Handler, Arg1, Arg2, Arg3>* this_handler) | |||
| { | |||
| asio_handler_invoke_helpers::invoke( | |||
| function, this_handler->handler_); | |||
| } | |||
| template <typename Handler, typename Arg1, typename Arg2, typename Arg3> | |||
| inline binder3<typename decay<Handler>::type, Arg1, Arg2, Arg3> bind_handler( | |||
| ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, const Arg2& arg2, | |||
| const Arg3& arg3) | |||
| { | |||
| return binder3<typename decay<Handler>::type, Arg1, Arg2, Arg3>(0, | |||
| ASIO_MOVE_CAST(Handler)(handler), arg1, arg2, arg3); | |||
| } | |||
| template <typename Handler, typename Arg1, | |||
| typename Arg2, typename Arg3, typename Arg4> | |||
| class binder4 | |||
| { | |||
| public: | |||
| template <typename T> | |||
| binder4(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1, | |||
| const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) | |||
| : handler_(ASIO_MOVE_CAST(T)(handler)), | |||
| arg1_(arg1), | |||
| arg2_(arg2), | |||
| arg3_(arg3), | |||
| arg4_(arg4) | |||
| { | |||
| } | |||
| binder4(Handler& handler, const Arg1& arg1, | |||
| const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) | |||
| : handler_(ASIO_MOVE_CAST(Handler)(handler)), | |||
| arg1_(arg1), | |||
| arg2_(arg2), | |||
| arg3_(arg3), | |||
| arg4_(arg4) | |||
| { | |||
| } | |||
| #if defined(ASIO_HAS_MOVE) | |||
| binder4(const binder4& other) | |||
| : handler_(other.handler_), | |||
| arg1_(other.arg1_), | |||
| arg2_(other.arg2_), | |||
| arg3_(other.arg3_), | |||
| arg4_(other.arg4_) | |||
| { | |||
| } | |||
| binder4(binder4&& other) | |||
| : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), | |||
| arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), | |||
| arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)), | |||
| arg3_(ASIO_MOVE_CAST(Arg3)(other.arg3_)), | |||
| arg4_(ASIO_MOVE_CAST(Arg4)(other.arg4_)) | |||
| { | |||
| } | |||
| #endif // defined(ASIO_HAS_MOVE) | |||
| void operator()() | |||
| { | |||
| handler_(static_cast<const Arg1&>(arg1_), | |||
| static_cast<const Arg2&>(arg2_), static_cast<const Arg3&>(arg3_), | |||
| static_cast<const Arg4&>(arg4_)); | |||
| } | |||
| void operator()() const | |||
| { | |||
| handler_(arg1_, arg2_, arg3_, arg4_); | |||
| } | |||
| //private: | |||
| Handler handler_; | |||
| Arg1 arg1_; | |||
| Arg2 arg2_; | |||
| Arg3 arg3_; | |||
| Arg4 arg4_; | |||
| }; | |||
| template <typename Handler, typename Arg1, | |||
| typename Arg2, typename Arg3, typename Arg4> | |||
| inline void* asio_handler_allocate(std::size_t size, | |||
| binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler) | |||
| { | |||
| return asio_handler_alloc_helpers::allocate( | |||
| size, this_handler->handler_); | |||
| } | |||
| template <typename Handler, typename Arg1, | |||
| typename Arg2, typename Arg3, typename Arg4> | |||
| inline void asio_handler_deallocate(void* pointer, std::size_t size, | |||
| binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler) | |||
| { | |||
| asio_handler_alloc_helpers::deallocate( | |||
| pointer, size, this_handler->handler_); | |||
| } | |||
| template <typename Handler, typename Arg1, | |||
| typename Arg2, typename Arg3, typename Arg4> | |||
| inline bool asio_handler_is_continuation( | |||
| binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler) | |||
| { | |||
| return asio_handler_cont_helpers::is_continuation( | |||
| this_handler->handler_); | |||
| } | |||
| template <typename Function, typename Handler, typename Arg1, | |||
| typename Arg2, typename Arg3, typename Arg4> | |||
| inline void asio_handler_invoke(Function& function, | |||
| binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler) | |||
| { | |||
| asio_handler_invoke_helpers::invoke( | |||
| function, this_handler->handler_); | |||
| } | |||
| template <typename Function, typename Handler, typename Arg1, | |||
| typename Arg2, typename Arg3, typename Arg4> | |||
| inline void asio_handler_invoke(const Function& function, | |||
| binder4<Handler, Arg1, Arg2, Arg3, Arg4>* this_handler) | |||
| { | |||
| asio_handler_invoke_helpers::invoke( | |||
| function, this_handler->handler_); | |||
| } | |||
| template <typename Handler, typename Arg1, | |||
| typename Arg2, typename Arg3, typename Arg4> | |||
| inline binder4<typename decay<Handler>::type, Arg1, Arg2, Arg3, Arg4> | |||
| bind_handler(ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, | |||
| const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) | |||
| { | |||
| return binder4<typename decay<Handler>::type, Arg1, Arg2, Arg3, Arg4>(0, | |||
| ASIO_MOVE_CAST(Handler)(handler), arg1, arg2, arg3, arg4); | |||
| } | |||
| template <typename Handler, typename Arg1, typename Arg2, | |||
| typename Arg3, typename Arg4, typename Arg5> | |||
| class binder5 | |||
| { | |||
| public: | |||
| template <typename T> | |||
| binder5(int, ASIO_MOVE_ARG(T) handler, const Arg1& arg1, | |||
| const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) | |||
| : handler_(ASIO_MOVE_CAST(T)(handler)), | |||
| arg1_(arg1), | |||
| arg2_(arg2), | |||
| arg3_(arg3), | |||
| arg4_(arg4), | |||
| arg5_(arg5) | |||
| { | |||
| } | |||
| binder5(Handler& handler, const Arg1& arg1, const Arg2& arg2, | |||
| const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) | |||
| : handler_(ASIO_MOVE_CAST(Handler)(handler)), | |||
| arg1_(arg1), | |||
| arg2_(arg2), | |||
| arg3_(arg3), | |||
| arg4_(arg4), | |||
| arg5_(arg5) | |||
| { | |||
| } | |||
| #if defined(ASIO_HAS_MOVE) | |||
| binder5(const binder5& other) | |||
| : handler_(other.handler_), | |||
| arg1_(other.arg1_), | |||
| arg2_(other.arg2_), | |||
| arg3_(other.arg3_), | |||
| arg4_(other.arg4_), | |||
| arg5_(other.arg5_) | |||
| { | |||
| } | |||
| binder5(binder5&& other) | |||
| : handler_(ASIO_MOVE_CAST(Handler)(other.handler_)), | |||
| arg1_(ASIO_MOVE_CAST(Arg1)(other.arg1_)), | |||
| arg2_(ASIO_MOVE_CAST(Arg2)(other.arg2_)), | |||
| arg3_(ASIO_MOVE_CAST(Arg3)(other.arg3_)), | |||
| arg4_(ASIO_MOVE_CAST(Arg4)(other.arg4_)), | |||
| arg5_(ASIO_MOVE_CAST(Arg5)(other.arg5_)) | |||
| { | |||
| } | |||
| #endif // defined(ASIO_HAS_MOVE) | |||
| void operator()() | |||
| { | |||
| handler_(static_cast<const Arg1&>(arg1_), | |||
| static_cast<const Arg2&>(arg2_), static_cast<const Arg3&>(arg3_), | |||
| static_cast<const Arg4&>(arg4_), static_cast<const Arg5&>(arg5_)); | |||
| } | |||
| void operator()() const | |||
| { | |||
| handler_(arg1_, arg2_, arg3_, arg4_, arg5_); | |||
| } | |||
| //private: | |||
| Handler handler_; | |||
| Arg1 arg1_; | |||
| Arg2 arg2_; | |||
| Arg3 arg3_; | |||
| Arg4 arg4_; | |||
| Arg5 arg5_; | |||
| }; | |||
| template <typename Handler, typename Arg1, typename Arg2, | |||
| typename Arg3, typename Arg4, typename Arg5> | |||
| inline void* asio_handler_allocate(std::size_t size, | |||
| binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler) | |||
| { | |||
| return asio_handler_alloc_helpers::allocate( | |||
| size, this_handler->handler_); | |||
| } | |||
| template <typename Handler, typename Arg1, typename Arg2, | |||
| typename Arg3, typename Arg4, typename Arg5> | |||
| inline void asio_handler_deallocate(void* pointer, std::size_t size, | |||
| binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler) | |||
| { | |||
| asio_handler_alloc_helpers::deallocate( | |||
| pointer, size, this_handler->handler_); | |||
| } | |||
| template <typename Handler, typename Arg1, typename Arg2, | |||
| typename Arg3, typename Arg4, typename Arg5> | |||
| inline bool asio_handler_is_continuation( | |||
| binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler) | |||
| { | |||
| return asio_handler_cont_helpers::is_continuation( | |||
| this_handler->handler_); | |||
| } | |||
| template <typename Function, typename Handler, typename Arg1, | |||
| typename Arg2, typename Arg3, typename Arg4, typename Arg5> | |||
| inline void asio_handler_invoke(Function& function, | |||
| binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler) | |||
| { | |||
| asio_handler_invoke_helpers::invoke( | |||
| function, this_handler->handler_); | |||
| } | |||
| template <typename Function, typename Handler, typename Arg1, | |||
| typename Arg2, typename Arg3, typename Arg4, typename Arg5> | |||
| inline void asio_handler_invoke(const Function& function, | |||
| binder5<Handler, Arg1, Arg2, Arg3, Arg4, Arg5>* this_handler) | |||
| { | |||
| asio_handler_invoke_helpers::invoke( | |||
| function, this_handler->handler_); | |||
| } | |||
| template <typename Handler, typename Arg1, typename Arg2, | |||
| typename Arg3, typename Arg4, typename Arg5> | |||
| inline binder5<typename decay<Handler>::type, Arg1, Arg2, Arg3, Arg4, Arg5> | |||
| bind_handler(ASIO_MOVE_ARG(Handler) handler, const Arg1& arg1, | |||
| const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) | |||
| { | |||
| return binder5<typename decay<Handler>::type, Arg1, Arg2, Arg3, Arg4, Arg5>(0, | |||
| ASIO_MOVE_CAST(Handler)(handler), arg1, arg2, arg3, arg4, arg5); | |||
| } | |||
| #if defined(ASIO_HAS_MOVE) | |||
| template <typename Handler, typename Arg1> | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/buffer_resize_guard.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/buffer_sequence_adapter.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) | |||
| @@ -27,11 +27,12 @@ namespace detail { | |||
| class buffer_sequence_adapter_base | |||
| { | |||
| protected: | |||
| #if defined(ASIO_WINDOWS_RUNTIME) | |||
| public: | |||
| // The maximum number of buffers to support in a single operation. | |||
| enum { max_buffers = 1 }; | |||
| protected: | |||
| typedef Windows::Storage::Streams::IBuffer^ native_buffer_type; | |||
| ASIO_DECL static void init_native_buffer( | |||
| @@ -42,9 +43,11 @@ protected: | |||
| native_buffer_type& buf, | |||
| const asio::const_buffer& buffer); | |||
| #elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) | |||
| public: | |||
| // The maximum number of buffers to support in a single operation. | |||
| enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; | |||
| protected: | |||
| typedef WSABUF native_buffer_type; | |||
| static void init_native_buffer(WSABUF& buf, | |||
| @@ -61,9 +64,11 @@ protected: | |||
| buf.len = static_cast<ULONG>(buffer.size()); | |||
| } | |||
| #else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) | |||
| public: | |||
| // The maximum number of buffers to support in a single operation. | |||
| enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; | |||
| protected: | |||
| typedef iovec native_buffer_type; | |||
| static void init_iov_base(void*& base, void* addr) | |||
| @@ -102,14 +107,9 @@ public: | |||
| explicit buffer_sequence_adapter(const Buffers& buffer_sequence) | |||
| : count_(0), total_buffer_size_(0) | |||
| { | |||
| typename Buffers::const_iterator iter = buffer_sequence.begin(); | |||
| typename Buffers::const_iterator end = buffer_sequence.end(); | |||
| for (; iter != end && count_ < max_buffers; ++iter, ++count_) | |||
| { | |||
| Buffer buffer(*iter); | |||
| init_native_buffer(buffers_[count_], buffer); | |||
| total_buffer_size_ += buffer.size(); | |||
| } | |||
| buffer_sequence_adapter::init( | |||
| asio::buffer_sequence_begin(buffer_sequence), | |||
| asio::buffer_sequence_end(buffer_sequence)); | |||
| } | |||
| native_buffer_type* buffers() | |||
| @@ -122,6 +122,11 @@ public: | |||
| return count_; | |||
| } | |||
| std::size_t total_size() const | |||
| { | |||
| return total_buffer_size_; | |||
| } | |||
| bool all_empty() const | |||
| { | |||
| return total_buffer_size_ == 0; | |||
| @@ -129,8 +134,42 @@ public: | |||
| static bool all_empty(const Buffers& buffer_sequence) | |||
| { | |||
| typename Buffers::const_iterator iter = buffer_sequence.begin(); | |||
| typename Buffers::const_iterator end = buffer_sequence.end(); | |||
| return buffer_sequence_adapter::all_empty( | |||
| asio::buffer_sequence_begin(buffer_sequence), | |||
| asio::buffer_sequence_end(buffer_sequence)); | |||
| } | |||
| static void validate(const Buffers& buffer_sequence) | |||
| { | |||
| buffer_sequence_adapter::validate( | |||
| asio::buffer_sequence_begin(buffer_sequence), | |||
| asio::buffer_sequence_end(buffer_sequence)); | |||
| } | |||
| static Buffer first(const Buffers& buffer_sequence) | |||
| { | |||
| return buffer_sequence_adapter::first( | |||
| asio::buffer_sequence_begin(buffer_sequence), | |||
| asio::buffer_sequence_end(buffer_sequence)); | |||
| } | |||
| private: | |||
| template <typename Iterator> | |||
| void init(Iterator begin, Iterator end) | |||
| { | |||
| Iterator iter = begin; | |||
| for (; iter != end && count_ < max_buffers; ++iter, ++count_) | |||
| { | |||
| Buffer buffer(*iter); | |||
| init_native_buffer(buffers_[count_], buffer); | |||
| total_buffer_size_ += buffer.size(); | |||
| } | |||
| } | |||
| template <typename Iterator> | |||
| static bool all_empty(Iterator begin, Iterator end) | |||
| { | |||
| Iterator iter = begin; | |||
| std::size_t i = 0; | |||
| for (; iter != end && i < max_buffers; ++iter, ++i) | |||
| if (Buffer(*iter).size() > 0) | |||
| @@ -138,10 +177,10 @@ public: | |||
| return true; | |||
| } | |||
| static void validate(const Buffers& buffer_sequence) | |||
| template <typename Iterator> | |||
| static void validate(Iterator begin, Iterator end) | |||
| { | |||
| typename Buffers::const_iterator iter = buffer_sequence.begin(); | |||
| typename Buffers::const_iterator end = buffer_sequence.end(); | |||
| Iterator iter = begin; | |||
| for (; iter != end; ++iter) | |||
| { | |||
| Buffer buffer(*iter); | |||
| @@ -149,10 +188,10 @@ public: | |||
| } | |||
| } | |||
| static Buffer first(const Buffers& buffer_sequence) | |||
| template <typename Iterator> | |||
| static Buffer first(Iterator begin, Iterator end) | |||
| { | |||
| typename Buffers::const_iterator iter = buffer_sequence.begin(); | |||
| typename Buffers::const_iterator end = buffer_sequence.end(); | |||
| Iterator iter = begin; | |||
| for (; iter != end; ++iter) | |||
| { | |||
| Buffer buffer(*iter); | |||
| @@ -162,12 +201,117 @@ public: | |||
| return Buffer(); | |||
| } | |||
| private: | |||
| native_buffer_type buffers_[max_buffers]; | |||
| std::size_t count_; | |||
| std::size_t total_buffer_size_; | |||
| }; | |||
| template <typename Buffer> | |||
| class buffer_sequence_adapter<Buffer, asio::mutable_buffer> | |||
| : buffer_sequence_adapter_base | |||
| { | |||
| public: | |||
| explicit buffer_sequence_adapter( | |||
| const asio::mutable_buffer& buffer_sequence) | |||
| { | |||
| init_native_buffer(buffer_, Buffer(buffer_sequence)); | |||
| total_buffer_size_ = buffer_sequence.size(); | |||
| } | |||
| native_buffer_type* buffers() | |||
| { | |||
| return &buffer_; | |||
| } | |||
| std::size_t count() const | |||
| { | |||
| return 1; | |||
| } | |||
| std::size_t total_size() const | |||
| { | |||
| return total_buffer_size_; | |||
| } | |||
| bool all_empty() const | |||
| { | |||
| return total_buffer_size_ == 0; | |||
| } | |||
| static bool all_empty(const asio::mutable_buffer& buffer_sequence) | |||
| { | |||
| return buffer_sequence.size() == 0; | |||
| } | |||
| static void validate(const asio::mutable_buffer& buffer_sequence) | |||
| { | |||
| buffer_sequence.data(); | |||
| } | |||
| static Buffer first(const asio::mutable_buffer& buffer_sequence) | |||
| { | |||
| return Buffer(buffer_sequence); | |||
| } | |||
| private: | |||
| native_buffer_type buffer_; | |||
| std::size_t total_buffer_size_; | |||
| }; | |||
| template <typename Buffer> | |||
| class buffer_sequence_adapter<Buffer, asio::const_buffer> | |||
| : buffer_sequence_adapter_base | |||
| { | |||
| public: | |||
| explicit buffer_sequence_adapter( | |||
| const asio::const_buffer& buffer_sequence) | |||
| { | |||
| init_native_buffer(buffer_, Buffer(buffer_sequence)); | |||
| total_buffer_size_ = buffer_sequence.size(); | |||
| } | |||
| native_buffer_type* buffers() | |||
| { | |||
| return &buffer_; | |||
| } | |||
| std::size_t count() const | |||
| { | |||
| return 1; | |||
| } | |||
| std::size_t total_size() const | |||
| { | |||
| return total_buffer_size_; | |||
| } | |||
| bool all_empty() const | |||
| { | |||
| return total_buffer_size_ == 0; | |||
| } | |||
| static bool all_empty(const asio::const_buffer& buffer_sequence) | |||
| { | |||
| return buffer_sequence.size() == 0; | |||
| } | |||
| static void validate(const asio::const_buffer& buffer_sequence) | |||
| { | |||
| buffer_sequence.data(); | |||
| } | |||
| static Buffer first(const asio::const_buffer& buffer_sequence) | |||
| { | |||
| return Buffer(buffer_sequence); | |||
| } | |||
| private: | |||
| native_buffer_type buffer_; | |||
| std::size_t total_buffer_size_; | |||
| }; | |||
| #if !defined(ASIO_NO_DEPRECATED) | |||
| template <typename Buffer> | |||
| class buffer_sequence_adapter<Buffer, asio::mutable_buffers_1> | |||
| : buffer_sequence_adapter_base | |||
| @@ -190,6 +334,11 @@ public: | |||
| return 1; | |||
| } | |||
| std::size_t total_size() const | |||
| { | |||
| return total_buffer_size_; | |||
| } | |||
| bool all_empty() const | |||
| { | |||
| return total_buffer_size_ == 0; | |||
| @@ -237,6 +386,11 @@ public: | |||
| return 1; | |||
| } | |||
| std::size_t total_size() const | |||
| { | |||
| return total_buffer_size_; | |||
| } | |||
| bool all_empty() const | |||
| { | |||
| return total_buffer_size_ == 0; | |||
| @@ -262,6 +416,8 @@ private: | |||
| std::size_t total_buffer_size_; | |||
| }; | |||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||
| template <typename Buffer, typename Elem> | |||
| class buffer_sequence_adapter<Buffer, boost::array<Elem, 2> > | |||
| : buffer_sequence_adapter_base | |||
| @@ -285,6 +441,11 @@ public: | |||
| return 2; | |||
| } | |||
| std::size_t total_size() const | |||
| { | |||
| return total_buffer_size_; | |||
| } | |||
| bool all_empty() const | |||
| { | |||
| return total_buffer_size_ == 0; | |||
| @@ -337,6 +498,11 @@ public: | |||
| return 2; | |||
| } | |||
| std::size_t total_size() const | |||
| { | |||
| return total_buffer_size_; | |||
| } | |||
| bool all_empty() const | |||
| { | |||
| return total_buffer_size_ == 0; | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/buffered_stream_storage.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/call_stack.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/chrono.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) | |||
| @@ -38,11 +38,11 @@ using std::chrono::minutes; | |||
| using std::chrono::hours; | |||
| using std::chrono::time_point_cast; | |||
| #if defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) | |||
| typedef std::chrono::monotonic_clock system_clock; | |||
| typedef std::chrono::monotonic_clock steady_clock; | |||
| #else // defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) | |||
| using std::chrono::system_clock; | |||
| #endif // defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) | |||
| using std::chrono::steady_clock; | |||
| #endif // defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) | |||
| using std::chrono::system_clock; | |||
| using std::chrono::high_resolution_clock; | |||
| #elif defined(ASIO_HAS_BOOST_CHRONO) | |||
| using boost::chrono::duration; | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/chrono_time_traits.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) | |||
| @@ -2,7 +2,7 @@ | |||
| // detail/completion_handler.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) | |||
| @@ -0,0 +1,94 @@ | |||
| // | |||
| // detail/concurrency_hint.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_DETAIL_CONCURRENCY_HINT_HPP | |||
| #define ASIO_DETAIL_CONCURRENCY_HINT_HPP | |||
| #if defined(_MSC_VER) && (_MSC_VER >= 1200) | |||
| # pragma once | |||
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |||
| #include "asio/detail/config.hpp" | |||
| #include "asio/detail/noncopyable.hpp" | |||
| // The concurrency hint ID and mask are used to identify when a "well-known" | |||
| // concurrency hint value has been passed to the io_context. | |||
| #define ASIO_CONCURRENCY_HINT_ID 0xA5100000u | |||
| #define ASIO_CONCURRENCY_HINT_ID_MASK 0xFFFF0000u | |||
| // If set, this bit indicates that the scheduler should perform locking. | |||
| #define ASIO_CONCURRENCY_HINT_LOCKING_SCHEDULER 0x1u | |||
| // If set, this bit indicates that the reactor should perform locking when | |||
| // managing descriptor registrations. | |||
| #define ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_REGISTRATION 0x2u | |||
| // If set, this bit indicates that the reactor should perform locking for I/O. | |||
| #define ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_IO 0x4u | |||
| // Helper macro to determine if we have a special concurrency hint. | |||
| #define ASIO_CONCURRENCY_HINT_IS_SPECIAL(hint) \ | |||
| ((static_cast<unsigned>(hint) \ | |||
| & ASIO_CONCURRENCY_HINT_ID_MASK) \ | |||
| == ASIO_CONCURRENCY_HINT_ID) | |||
| // Helper macro to determine if locking is enabled for a given facility. | |||
| #define ASIO_CONCURRENCY_HINT_IS_LOCKING(facility, hint) \ | |||
| (((static_cast<unsigned>(hint) \ | |||
| & (ASIO_CONCURRENCY_HINT_ID_MASK \ | |||
| | ASIO_CONCURRENCY_HINT_LOCKING_ ## facility)) \ | |||
| ^ ASIO_CONCURRENCY_HINT_ID) != 0) | |||
| // This special concurrency hint disables locking in both the scheduler and | |||
| // reactor I/O. This hint has the following restrictions: | |||
| // | |||
| // - Care must be taken to ensure that all operations on the io_context and any | |||
| // of its associated I/O objects (such as sockets and timers) occur in only | |||
| // one thread at a time. | |||
| // | |||
| // - Asynchronous resolve operations fail with operation_not_supported. | |||
| // | |||
| // - If a signal_set is used with the io_context, signal_set objects cannot be | |||
| // used with any other io_context in the program. | |||
| #define ASIO_CONCURRENCY_HINT_UNSAFE \ | |||
| static_cast<int>(ASIO_CONCURRENCY_HINT_ID) | |||
| // This special concurrency hint disables locking in the reactor I/O. This hint | |||
| // has the following restrictions: | |||
| // | |||
| // - Care must be taken to ensure that run functions on the io_context, and all | |||
| // operations on the io_context's associated I/O objects (such as sockets and | |||
| // timers), occur in only one thread at a time. | |||
| #define ASIO_CONCURRENCY_HINT_UNSAFE_IO \ | |||
| static_cast<int>(ASIO_CONCURRENCY_HINT_ID \ | |||
| | ASIO_CONCURRENCY_HINT_LOCKING_SCHEDULER \ | |||
| | ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_REGISTRATION) | |||
| // The special concurrency hint provides full thread safety. | |||
| #define ASIO_CONCURRENCY_HINT_SAFE \ | |||
| static_cast<int>(ASIO_CONCURRENCY_HINT_ID \ | |||
| | ASIO_CONCURRENCY_HINT_LOCKING_SCHEDULER \ | |||
| | ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_REGISTRATION \ | |||
| | ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_IO) | |||
| // This #define may be overridden at compile time to specify a program-wide | |||
| // default concurrency hint, used by the zero-argument io_context constructor. | |||
| #if !defined(ASIO_CONCURRENCY_HINT_DEFAULT) | |||
| # define ASIO_CONCURRENCY_HINT_DEFAULT -1 | |||
| #endif // !defined(ASIO_CONCURRENCY_HINT_DEFAULT) | |||
| // This #define may be overridden at compile time to specify a program-wide | |||
| // concurrency hint, used by the one-argument io_context constructor when | |||
| // passed a value of 1. | |||
| #if !defined(ASIO_CONCURRENCY_HINT_1) | |||
| # define ASIO_CONCURRENCY_HINT_1 1 | |||
| #endif // !defined(ASIO_CONCURRENCY_HINT_DEFAULT) | |||
| #endif // ASIO_DETAIL_CONCURRENCY_HINT_HPP | |||
| @@ -0,0 +1,112 @@ | |||
| // | |||
| // detail/conditionally_enabled_event.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_DETAIL_CONDITIONALLY_ENABLED_EVENT_HPP | |||
| #define ASIO_DETAIL_CONDITIONALLY_ENABLED_EVENT_HPP | |||
| #if defined(_MSC_VER) && (_MSC_VER >= 1200) | |||
| # pragma once | |||
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |||
| #include "asio/detail/config.hpp" | |||
| #include "asio/detail/conditionally_enabled_mutex.hpp" | |||
| #include "asio/detail/event.hpp" | |||
| #include "asio/detail/noncopyable.hpp" | |||
| #include "asio/detail/null_event.hpp" | |||
| #include "asio/detail/scoped_lock.hpp" | |||
| #include "asio/detail/push_options.hpp" | |||
| namespace asio { | |||
| namespace detail { | |||
| // Mutex adapter used to conditionally enable or disable locking. | |||
| class conditionally_enabled_event | |||
| : private noncopyable | |||
| { | |||
| public: | |||
| // Constructor. | |||
| conditionally_enabled_event() | |||
| { | |||
| } | |||
| // Destructor. | |||
| ~conditionally_enabled_event() | |||
| { | |||
| } | |||
| // Signal the event. (Retained for backward compatibility.) | |||
| void signal(conditionally_enabled_mutex::scoped_lock& lock) | |||
| { | |||
| if (lock.mutex_.enabled_) | |||
| event_.signal(lock); | |||
| } | |||
| // Signal all waiters. | |||
| void signal_all(conditionally_enabled_mutex::scoped_lock& lock) | |||
| { | |||
| if (lock.mutex_.enabled_) | |||
| event_.signal_all(lock); | |||
| } | |||
| // Unlock the mutex and signal one waiter. | |||
| void unlock_and_signal_one( | |||
| conditionally_enabled_mutex::scoped_lock& lock) | |||
| { | |||
| if (lock.mutex_.enabled_) | |||
| event_.unlock_and_signal_one(lock); | |||
| } | |||
| // If there's a waiter, unlock the mutex and signal it. | |||
| bool maybe_unlock_and_signal_one( | |||
| conditionally_enabled_mutex::scoped_lock& lock) | |||
| { | |||
| if (lock.mutex_.enabled_) | |||
| return event_.maybe_unlock_and_signal_one(lock); | |||
| else | |||
| return false; | |||
| } | |||
| // Reset the event. | |||
| void clear(conditionally_enabled_mutex::scoped_lock& lock) | |||
| { | |||
| if (lock.mutex_.enabled_) | |||
| event_.clear(lock); | |||
| } | |||
| // Wait for the event to become signalled. | |||
| void wait(conditionally_enabled_mutex::scoped_lock& lock) | |||
| { | |||
| if (lock.mutex_.enabled_) | |||
| event_.wait(lock); | |||
| else | |||
| null_event().wait(lock); | |||
| } | |||
| // Timed wait for the event to become signalled. | |||
| bool wait_for_usec( | |||
| conditionally_enabled_mutex::scoped_lock& lock, long usec) | |||
| { | |||
| if (lock.mutex_.enabled_) | |||
| return event_.wait_for_usec(lock, usec); | |||
| else | |||
| return null_event().wait_for_usec(lock, usec); | |||
| } | |||
| private: | |||
| asio::detail::event event_; | |||
| }; | |||
| } // namespace detail | |||
| } // namespace asio | |||
| #include "asio/detail/pop_options.hpp" | |||
| #endif // ASIO_DETAIL_CONDITIONALLY_ENABLED_EVENT_HPP | |||