| @@ -119,7 +119,7 @@ endif | |||||
| ifeq ($(NOOPT),true) | ifeq ($(NOOPT),true) | ||||
| # No CPU-specific optimization flags | # 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 | endif | ||||
| ifeq ($(WIN32),true) | ifeq ($(WIN32),true) | ||||
| @@ -218,10 +218,9 @@ HAVE_HYLIA = true | |||||
| endif | endif | ||||
| 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) | ifeq ($(MACOS_OR_WIN32),true) | ||||
| HAVE_DGL = true | HAVE_DGL = true | ||||
| @@ -64,6 +64,21 @@ public: | |||||
| outputLatency = latency; | 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) | void process(const uint32_t frames, LinkTimeInfo* const info) | ||||
| { | { | ||||
| const std::chrono::microseconds hostTime = hostTimeFilter.sampleTimeToHostTime(sampleTime) | 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); | ((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) | void hylia_process(hylia_t* link, uint32_t frames, hylia_time_info_t* info) | ||||
| { | { | ||||
| ((HyliaTransport*)link)->process(frames, (LinkTimeInfo*)info); | ((HyliaTransport*)link)->process(frames, (LinkTimeInfo*)info); | ||||
| @@ -30,6 +30,7 @@ typedef struct _hylia_t hylia_t; | |||||
| typedef struct _hylia_time_info_t { | typedef struct _hylia_time_info_t { | ||||
| double beatsPerBar, beatsPerMinute, beat, phase; | double beatsPerBar, beatsPerMinute, beat, phase; | ||||
| bool playing; | |||||
| } hylia_time_info_t; | } hylia_time_info_t; | ||||
| hylia_t* hylia_create(void); | 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_bar(hylia_t* link, double beatsPerBar); | ||||
| void hylia_set_beats_per_minute(hylia_t* link, double beatsPerMinute); | 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_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); | void hylia_cleanup(hylia_t* link); | ||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| @@ -19,10 +19,6 @@ | |||||
| #include "AudioEngine.hpp" | #include "AudioEngine.hpp" | ||||
| // Make sure to define this before <cmath> is included for Windows | |||||
| #define _USE_MATH_DEFINES | |||||
| #include <cmath> | |||||
| namespace ableton | namespace ableton | ||||
| { | { | ||||
| namespace link | namespace link | ||||
| @@ -30,19 +26,38 @@ namespace link | |||||
| AudioEngine::AudioEngine(Link& link) | AudioEngine::AudioEngine(Link& link) | ||||
| : mLink(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 | 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) | void AudioEngine::setTempo(double tempo) | ||||
| { | { | ||||
| const std::lock_guard<std::mutex> lock(mEngineDataGuard); | |||||
| std::lock_guard<std::mutex> lock(mEngineDataGuard); | |||||
| mSharedEngineData.requestedTempo = tempo; | mSharedEngineData.requestedTempo = tempo; | ||||
| } | } | ||||
| @@ -53,26 +68,38 @@ double AudioEngine::quantum() const | |||||
| void AudioEngine::setQuantum(double quantum) | void AudioEngine::setQuantum(double quantum) | ||||
| { | { | ||||
| const std::lock_guard<std::mutex> lock(mEngineDataGuard); | |||||
| std::lock_guard<std::mutex> lock(mEngineDataGuard); | |||||
| mSharedEngineData.quantum = quantum; | 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() | AudioEngine::EngineData AudioEngine::pullEngineData() | ||||
| { | { | ||||
| auto engineData = EngineData{}; | auto engineData = EngineData{}; | ||||
| if (mEngineDataGuard.try_lock()) | if (mEngineDataGuard.try_lock()) | ||||
| { | { | ||||
| engineData.requestedTempo = mSharedEngineData.requestedTempo; | engineData.requestedTempo = mSharedEngineData.requestedTempo; | ||||
| mSharedEngineData.requestedTempo = 0; | 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(); | mEngineDataGuard.unlock(); | ||||
| } | } | ||||
| engineData.quantum = mLockfreeEngineData.quantum; | |||||
| return engineData; | return engineData; | ||||
| } | } | ||||
| @@ -81,29 +108,44 @@ void AudioEngine::timelineCallback(const std::chrono::microseconds hostTime, Lin | |||||
| { | { | ||||
| const auto engineData = pullEngineData(); | 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) | if (engineData.requestedTempo > 0) | ||||
| { | { | ||||
| // Set the newly requested tempo from the beginning of this buffer | // 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 | // Timeline modifications are complete, commit the results | ||||
| mLink.commitAudioTimeline(timeline); | |||||
| mLink.commitAudioSessionState(sessionState); | |||||
| // Save timeline info | |||||
| // Save session state | |||||
| info->beatsPerBar = engineData.quantum; | 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 | } // namespace link | ||||
| @@ -19,13 +19,27 @@ | |||||
| #pragma once | #pragma once | ||||
| // Make sure to define this before <cmath> is included for Windows | |||||
| #ifdef LINK_PLATFORM_WINDOWS | |||||
| #define _USE_MATH_DEFINES | #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 "ableton/Link.hpp" | ||||
| #include <mutex> | |||||
| struct LinkTimeInfo { | struct LinkTimeInfo { | ||||
| double beatsPerBar, beatsPerMinute, beat, phase; | double beatsPerBar, beatsPerMinute, beat, phase; | ||||
| bool playing; | |||||
| }; | }; | ||||
| namespace ableton | namespace ableton | ||||
| @@ -37,10 +51,15 @@ class AudioEngine | |||||
| { | { | ||||
| public: | public: | ||||
| AudioEngine(Link& link); | AudioEngine(Link& link); | ||||
| void startPlaying(); | |||||
| void stopPlaying(); | |||||
| bool isPlaying() const; | |||||
| double beatTime() const; | double beatTime() const; | ||||
| void setTempo(double tempo); | void setTempo(double tempo); | ||||
| double quantum() const; | double quantum() const; | ||||
| void setQuantum(double quantum); | void setQuantum(double quantum); | ||||
| bool isStartStopSyncEnabled() const; | |||||
| void setStartStopSyncEnabled(bool enabled); | |||||
| void timelineCallback(const std::chrono::microseconds hostTime, LinkTimeInfo* const info); | void timelineCallback(const std::chrono::microseconds hostTime, LinkTimeInfo* const info); | ||||
| @@ -48,14 +67,18 @@ private: | |||||
| struct EngineData | struct EngineData | ||||
| { | { | ||||
| double requestedTempo; | double requestedTempo; | ||||
| bool resetBeatTime; | |||||
| bool requestStart; | |||||
| bool requestStop; | |||||
| double quantum; | double quantum; | ||||
| bool startStopSyncOn; | |||||
| }; | }; | ||||
| EngineData pullEngineData(); | EngineData pullEngineData(); | ||||
| Link& mLink; | Link& mLink; | ||||
| EngineData mSharedEngineData; | EngineData mSharedEngineData; | ||||
| EngineData mLockfreeEngineData; | |||||
| bool mIsPlaying; | |||||
| std::mutex mEngineDataGuard; | std::mutex mEngineDataGuard; | ||||
| }; | }; | ||||
| @@ -32,13 +32,20 @@ namespace ableton | |||||
| /*! @class Link | /*! @class Link | ||||
| * @brief Class that represents a participant in a Link session. | * @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 | * Each method of the Link type documents its thread-safety and | ||||
| * realtime-safety properties. When a method is marked thread-safe, | * 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 | * it does not block and is appropriate for use in the thread that | ||||
| * performs audio IO. | * 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 | * 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 | * 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: | public: | ||||
| class Timeline; | |||||
| using Clock = link::platform::Clock; | |||||
| class SessionState; | |||||
| /*! @brief Construct with an initial tempo. */ | /*! @brief Construct with an initial tempo. */ | ||||
| BasicLink(double bpm); | |||||
| Link(double bpm); | |||||
| /*! @brief Link instances cannot be copied or moved */ | /*! @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? | /*! @brief Is Link currently enabled? | ||||
| * Thread-safe: yes | * Thread-safe: yes | ||||
| @@ -85,6 +92,18 @@ public: | |||||
| */ | */ | ||||
| void enable(bool bEnable); | 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? | /*! @brief How many peers are currently connected in a Link session? | ||||
| * Thread-safe: yes | * Thread-safe: yes | ||||
| * Realtime-safe: yes | * Realtime-safe: yes | ||||
| @@ -116,6 +135,19 @@ public: | |||||
| template <typename Callback> | template <typename Callback> | ||||
| void setTempoCallback(Callback 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. | /*! @brief The clock used by Link. | ||||
| * Thread-safe: yes | * Thread-safe: yes | ||||
| * Realtime-safe: yes | * Realtime-safe: yes | ||||
| @@ -130,69 +162,81 @@ public: | |||||
| */ | */ | ||||
| Clock clock() const; | 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 | * Thread-safe: no | ||||
| * Realtime-safe: yes | * Realtime-safe: yes | ||||
| * | * | ||||
| * @discussion This method should ONLY be called in the audio thread | * @discussion This method should ONLY be called in the audio thread | ||||
| * and must not be accessed from any other threads. The returned | * 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 | * 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. | * audio thread. | ||||
| * Thread-safe: no | * Thread-safe: no | ||||
| * Realtime-safe: yes | * Realtime-safe: yes | ||||
| * | * | ||||
| * @discussion This method should ONLY be called in the audio | * @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. | ||||
| * Thread-safe: yes | * Thread-safe: yes | ||||
| * Realtime-safe: no | * 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. | * application thread. | ||||
| * Thread-safe: yes | * Thread-safe: yes | ||||
| * Realtime-safe: no | * 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: | 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 */ | /*! @brief: The tempo of the timeline, in bpm */ | ||||
| double tempo() const; | double tempo() const; | ||||
| @@ -286,29 +330,45 @@ public: | |||||
| */ | */ | ||||
| void forceBeatAtTime(double beat, std::chrono::microseconds time, double quantum); | 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; | bool mbRespectQuantum; | ||||
| link::Timeline mTimeline; | |||||
| }; | }; | ||||
| private: | private: | ||||
| using Controller = ableton::link::Controller<link::PeerCountCallback, | |||||
| link::TempoCallback, | |||||
| Clock, | |||||
| link::platform::IoContext>; | |||||
| std::mutex mCallbackMutex; | std::mutex mCallbackMutex; | ||||
| link::PeerCountCallback mPeerCountCallback; | link::PeerCountCallback mPeerCountCallback; | ||||
| link::TempoCallback mTempoCallback; | link::TempoCallback mTempoCallback; | ||||
| link::StartStopStateCallback mStartStopCallback; | |||||
| Clock mClock; | Clock mClock; | ||||
| Controller mController; | |||||
| link::platform::Controller mController; | |||||
| }; | }; | ||||
| using Link = BasicLink<link::platform::Clock>; | |||||
| } // ableton | |||||
| } // namespace ableton | |||||
| #include <ableton/Link.ipp> | #include <ableton/Link.ipp> | ||||
| @@ -23,11 +23,40 @@ | |||||
| namespace ableton | 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) {}) | : mPeerCountCallback([](std::size_t) {}) | ||||
| , mTempoCallback([](link::Tempo) {}) | , mTempoCallback([](link::Tempo) {}) | ||||
| , mStartStopCallback([](bool) {}) | |||||
| , mClock{} | |||||
| , mController(link::Tempo(bpm), | , mController(link::Tempo(bpm), | ||||
| [this](const std::size_t peers) { | [this](const std::size_t peers) { | ||||
| std::lock_guard<std::mutex> lock(mCallbackMutex); | 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); | std::lock_guard<std::mutex> lock(mCallbackMutex); | ||||
| mTempoCallback(tempo); | mTempoCallback(tempo); | ||||
| }, | }, | ||||
| [this](const bool isPlaying) { | |||||
| std::lock_guard<std::mutex> lock(mCallbackMutex); | |||||
| mStartStopCallback(isPlaying); | |||||
| }, | |||||
| mClock, | mClock, | ||||
| util::injectVal(link::platform::IoContext{})) | util::injectVal(link::platform::IoContext{})) | ||||
| { | { | ||||
| } | } | ||||
| template <typename Clock> | |||||
| inline bool BasicLink<Clock>::isEnabled() const | |||||
| inline bool Link::isEnabled() const | |||||
| { | { | ||||
| return mController.isEnabled(); | return mController.isEnabled(); | ||||
| } | } | ||||
| template <typename Clock> | |||||
| inline void BasicLink<Clock>::enable(const bool bEnable) | |||||
| inline void Link::enable(const bool bEnable) | |||||
| { | { | ||||
| mController.enable(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(); | return mController.numPeers(); | ||||
| } | } | ||||
| template <typename Clock> | |||||
| template <typename Callback> | template <typename Callback> | ||||
| void BasicLink<Clock>::setNumPeersCallback(Callback callback) | |||||
| void Link::setNumPeersCallback(Callback callback) | |||||
| { | { | ||||
| std::lock_guard<std::mutex> lock(mCallbackMutex); | std::lock_guard<std::mutex> lock(mCallbackMutex); | ||||
| mPeerCountCallback = [callback](const std::size_t numPeers) { callback(numPeers); }; | mPeerCountCallback = [callback](const std::size_t numPeers) { callback(numPeers); }; | ||||
| } | } | ||||
| template <typename Clock> | |||||
| template <typename Callback> | template <typename Callback> | ||||
| void BasicLink<Clock>::setTempoCallback(Callback callback) | |||||
| void Link::setTempoCallback(Callback callback) | |||||
| { | { | ||||
| std::lock_guard<std::mutex> lock(mCallbackMutex); | std::lock_guard<std::mutex> lock(mCallbackMutex); | ||||
| mTempoCallback = [callback](const link::Tempo tempo) { callback(tempo.bpm()); }; | 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; | 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) | , 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 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 | 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 | const std::chrono::microseconds time, const double quantum) const | ||||
| { | { | ||||
| return link::phase(link::Beats{beatAtTime(time, quantum)}, link::Beats{quantum}) | return link::phase(link::Beats{beatAtTime(time, quantum)}, link::Beats{quantum}) | ||||
| .floating(); | .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 | 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) | const double beat, std::chrono::microseconds time, const double quantum) | ||||
| { | { | ||||
| if (mbRespectQuantum) | if (mbRespectQuantum) | ||||
| @@ -177,8 +206,7 @@ inline void BasicLink<Clock>::Timeline::requestBeatAtTime( | |||||
| forceBeatAtTime(beat, time, quantum); | 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) | const double beat, const std::chrono::microseconds time, const double quantum) | ||||
| { | { | ||||
| // There are two components to the beat adjustment: a phase shift | // 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 curBeatAtTime = link::Beats{beatAtTime(time, quantum)}; | ||||
| const auto closestInPhase = | const auto closestInPhase = | ||||
| link::closestPhaseMatch(curBeatAtTime, link::Beats{beat}, link::Beats{quantum}); | 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 | // 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 | #pragma once | ||||
| #include <ableton/platforms/asio/AsioService.hpp> | |||||
| #include <ableton/platforms/asio/AsioWrapper.hpp> | |||||
| #include <ableton/util/Injected.hpp> | #include <ableton/util/Injected.hpp> | ||||
| namespace ableton | namespace ableton | ||||
| @@ -29,7 +29,7 @@ namespace discovery | |||||
| inline asio::ip::udp::endpoint multicastEndpoint() | 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 | // Type tags for dispatching between unicast and multicast packets | ||||
| @@ -20,9 +20,9 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <ableton/platforms/asio/AsioWrapper.hpp> | #include <ableton/platforms/asio/AsioWrapper.hpp> | ||||
| #if LINK_PLATFORM_MACOSX | |||||
| #if defined(LINK_PLATFORM_MACOSX) | |||||
| #include <ableton/platforms/darwin/Darwin.hpp> | #include <ableton/platforms/darwin/Darwin.hpp> | ||||
| #elif LINK_PLATFORM_LINUX | |||||
| #elif defined(LINK_PLATFORM_LINUX) | |||||
| #include <ableton/platforms/linux/Linux.hpp> | #include <ableton/platforms/linux/Linux.hpp> | ||||
| #endif | #endif | ||||
| @@ -32,10 +32,10 @@ | |||||
| #include <utility> | #include <utility> | ||||
| #include <vector> | #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 | #endif | ||||
| namespace ableton | namespace ableton | ||||
| @@ -80,7 +80,8 @@ struct Deserialize | |||||
| // Default size implementation. Works for primitive types. | // 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) | std::uint32_t sizeInByteStream(T) | ||||
| { | { | ||||
| return sizeof(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> | 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; | 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; | return bytesBegin; | ||||
| } | } | ||||
| } // detail | |||||
| } // namespace detail | |||||
| // Need specific overloads for each container type, but use above | // Need specific overloads for each container type, but use above | ||||
| // utilities for common implementation | // utilities for common implementation | ||||
| @@ -341,12 +366,13 @@ struct Deserialize<std::array<T, Size>> | |||||
| template <typename T, typename Alloc> | template <typename T, typename Alloc> | ||||
| std::uint32_t sizeInByteStream(const std::vector<T, Alloc>& vec) | 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> | template <typename T, typename Alloc, typename It> | ||||
| It toNetworkByteStream(const std::vector<T, Alloc>& vec, It out) | 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)); | return detail::containerToNetworkByteStream(vec, std::move(out)); | ||||
| } | } | ||||
| @@ -358,17 +384,42 @@ struct Deserialize<std::vector<T, Alloc>> | |||||
| It bytesBegin, It bytesEnd) | It bytesBegin, It bytesEnd) | ||||
| { | { | ||||
| using namespace std; | using namespace std; | ||||
| auto result_size = | |||||
| Deserialize<uint32_t>::fromNetworkByteStream(move(bytesBegin), bytesEnd); | |||||
| vector<T, Alloc> result; | 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)); | 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 | // 3-tuple | ||||
| template <typename X, typename Y, typename Z> | template <typename X, typename Y, typename Z> | ||||
| std::uint32_t sizeInByteStream(const std::tuple<X, Y, Z>& tup) | 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 xres = Deserialize<X>::fromNetworkByteStream(begin, end); | ||||
| auto yres = Deserialize<Y>::fromNetworkByteStream(xres.second, end); | auto yres = Deserialize<Y>::fromNetworkByteStream(xres.second, end); | ||||
| auto zres = Deserialize<Z>::fromNetworkByteStream(yres.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)); | move(zres.second)); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -21,6 +21,7 @@ | |||||
| #include <ableton/discovery/NetworkByteStreamSerializable.hpp> | #include <ableton/discovery/NetworkByteStreamSerializable.hpp> | ||||
| #include <functional> | #include <functional> | ||||
| #include <sstream> | |||||
| #include <unordered_map> | #include <unordered_map> | ||||
| namespace ableton | namespace ableton | ||||
| @@ -21,7 +21,6 @@ | |||||
| #include <ableton/discovery/UdpMessenger.hpp> | #include <ableton/discovery/UdpMessenger.hpp> | ||||
| #include <ableton/discovery/v1/Messages.hpp> | #include <ableton/discovery/v1/Messages.hpp> | ||||
| #include <ableton/platforms/asio/AsioService.hpp> | |||||
| #include <ableton/util/SafeAsyncHandler.hpp> | #include <ableton/util/SafeAsyncHandler.hpp> | ||||
| #include <memory> | #include <memory> | ||||
| @@ -216,11 +215,10 @@ PeerGateway<Messenger, PeerObserver, IoContext> makePeerGateway( | |||||
| // IpV4 gateway types | // IpV4 gateway types | ||||
| template <typename StateQuery, typename IoContext> | 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> | template <typename PeerObserver, typename StateQuery, typename IoContext> | ||||
| using IpV4Gateway = | 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) | void setReceiveHandler(Handler handler) | ||||
| { | { | ||||
| mPeerStateHandler = [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)); }; | mByeByeHandler = [handler](ByeBye<NodeId> byeBye) { handler(std::move(byeBye)); }; | ||||
| } | } | ||||
| @@ -42,8 +42,8 @@ public: | |||||
| template <typename Callback, typename Tag> | template <typename Callback, typename Tag> | ||||
| void receive(Callback callback, Tag 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)); | callback(tag, from, begin(buffer), end(buffer)); | ||||
| }; | }; | ||||
| } | } | ||||
| @@ -21,6 +21,7 @@ | |||||
| #include <ableton/discovery/NetworkByteStreamSerializable.hpp> | #include <ableton/discovery/NetworkByteStreamSerializable.hpp> | ||||
| #include <cstdint> | #include <cstdint> | ||||
| #include <tuple> | |||||
| #include <utility> | #include <utility> | ||||
| namespace ableton | namespace ableton | ||||
| @@ -35,10 +36,8 @@ namespace test | |||||
| // A fixed-size entry type | // A fixed-size entry type | ||||
| struct Foo | struct Foo | ||||
| { | { | ||||
| enum | |||||
| { | |||||
| key = '_foo' | |||||
| }; | |||||
| static const std::int32_t key = '_foo'; | |||||
| static_assert(key == 0x5f666f6f, "Unexpected byte order"); | |||||
| std::int32_t fooVal; | std::int32_t fooVal; | ||||
| @@ -66,10 +65,8 @@ struct Foo | |||||
| // A variable-size entry type | // A variable-size entry type | ||||
| struct Bar | 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; | 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 test | ||||
| } // namespace discovery | } // namespace discovery | ||||
| } // namespace ableton | } // namespace ableton | ||||
| @@ -53,7 +53,9 @@ public: | |||||
| void receive(Handler handler) | void receive(Handler handler) | ||||
| { | { | ||||
| mCallback = [handler](const asio::ip::udp::endpoint& from, | 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> | template <typename It> | ||||
| @@ -19,6 +19,7 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <ableton/link/Optional.hpp> | |||||
| #include <array> | #include <array> | ||||
| #include <atomic> | #include <atomic> | ||||
| #include <cassert> | #include <cassert> | ||||
| @@ -34,12 +35,6 @@ template <typename Type, std::size_t size> | |||||
| class CircularFifo | class CircularFifo | ||||
| { | { | ||||
| public: | public: | ||||
| struct PoppedItem | |||||
| { | |||||
| Type item; | |||||
| bool valid; | |||||
| }; | |||||
| CircularFifo() | CircularFifo() | ||||
| : tail(0) | : tail(0) | ||||
| , head(0) | , head(0) | ||||
| @@ -60,32 +55,17 @@ public: | |||||
| return false; | return false; | ||||
| } | } | ||||
| PoppedItem pop() | |||||
| Optional<Type> pop() | |||||
| { | { | ||||
| const auto currentHead = head.load(); | const auto currentHead = head.load(); | ||||
| if (currentHead == tail.load()) | if (currentHead == tail.load()) | ||||
| { | { | ||||
| return {Type{}, false}; | |||||
| return {}; | |||||
| } | } | ||||
| auto item = data[currentHead]; | auto item = data[currentHead]; | ||||
| head.store(nextIndex(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 | bool isEmpty() const | ||||
| @@ -109,5 +89,5 @@ private: | |||||
| std::array<Type, size + 1> data; | 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; | return client; | ||||
| } | } | ||||
| } // link | |||||
| } // ableton | |||||
| } // namespace link | |||||
| } // namespace ableton | |||||
| @@ -26,7 +26,9 @@ | |||||
| #include <ableton/link/GhostXForm.hpp> | #include <ableton/link/GhostXForm.hpp> | ||||
| #include <ableton/link/NodeState.hpp> | #include <ableton/link/NodeState.hpp> | ||||
| #include <ableton/link/Peers.hpp> | #include <ableton/link/Peers.hpp> | ||||
| #include <ableton/link/SessionState.hpp> | |||||
| #include <ableton/link/Sessions.hpp> | #include <ableton/link/Sessions.hpp> | ||||
| #include <ableton/link/StartStopState.hpp> | |||||
| #include <mutex> | #include <mutex> | ||||
| namespace ableton | namespace ableton | ||||
| @@ -44,20 +46,79 @@ GhostXForm initXForm(const Clock& clock) | |||||
| return {1.0, -clock.micros()}; | 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 | // The timespan in which local modifications to the timeline will be | ||||
| // preferred over any modifications coming from the network. | // preferred over any modifications coming from the network. | ||||
| const auto kLocalModGracePeriod = std::chrono::milliseconds(1000); | const auto kLocalModGracePeriod = std::chrono::milliseconds(1000); | ||||
| const auto kRtHandlerFallbackPeriod = kLocalModGracePeriod / 2; | 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 | } // namespace detail | ||||
| // function types corresponding to the Controller callback type params | // function types corresponding to the Controller callback type params | ||||
| using PeerCountCallback = std::function<void(std::size_t)>; | using PeerCountCallback = std::function<void(std::size_t)>; | ||||
| using TempoCallback = std::function<void(ableton::link::Tempo)>; | using TempoCallback = std::function<void(ableton::link::Tempo)>; | ||||
| using StartStopStateCallback = std::function<void(bool)>; | |||||
| // The main Link controller | // The main Link controller | ||||
| template <typename PeerCountCallback, | template <typename PeerCountCallback, | ||||
| typename TempoCallback, | typename TempoCallback, | ||||
| typename StartStopStateCallback, | |||||
| typename Clock, | typename Clock, | ||||
| typename IoContext> | typename IoContext> | ||||
| class Controller | class Controller | ||||
| @@ -66,33 +127,38 @@ public: | |||||
| Controller(Tempo tempo, | Controller(Tempo tempo, | ||||
| PeerCountCallback peerCallback, | PeerCountCallback peerCallback, | ||||
| TempoCallback tempoCallback, | TempoCallback tempoCallback, | ||||
| StartStopStateCallback startStopStateCallback, | |||||
| Clock clock, | Clock clock, | ||||
| util::Injected<IoContext> io) | util::Injected<IoContext> io) | ||||
| : mTempoCallback(std::move(tempoCallback)) | : mTempoCallback(std::move(tempoCallback)) | ||||
| , mStartStopStateCallback(std::move(startStopStateCallback)) | |||||
| , mClock(std::move(clock)) | , mClock(std::move(clock)) | ||||
| , mNodeId(NodeId::random()) | , mNodeId(NodeId::random()) | ||||
| , mSessionId(mNodeId) | , 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)) | , mSessionPeerCounter(*this, std::move(peerCallback)) | ||||
| , mEnabled(false) | , mEnabled(false) | ||||
| , mStartStopSyncEnabled(false) | |||||
| , mIo(std::move(io)) | , mIo(std::move(io)) | ||||
| , mRtTimelineSetter(*this) | |||||
| , mRtClientStateSetter(*this) | |||||
| , mPeers(util::injectRef(*mIo), | , mPeers(util::injectRef(*mIo), | ||||
| std::ref(mSessionPeerCounter), | 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), | util::injectRef(mPeers), | ||||
| MeasurePeer{*this}, | MeasurePeer{*this}, | ||||
| JoinSessionCallback{*this}, | JoinSessionCallback{*this}, | ||||
| util::injectRef(*mIo), | util::injectRef(*mIo), | ||||
| mClock) | 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}, | GatewayFactory{*this}, | ||||
| util::injectVal(mIo->clone(UdpSendExceptionHandler{*this}))) | util::injectVal(mIo->clone(UdpSendExceptionHandler{*this}))) | ||||
| { | { | ||||
| @@ -126,102 +192,186 @@ public: | |||||
| return mEnabled; | return mEnabled; | ||||
| } | } | ||||
| void enableStartStopSync(const bool bEnable) | |||||
| { | |||||
| mStartStopSyncEnabled = bEnable; | |||||
| } | |||||
| bool isStartStopSyncEnabled() const | |||||
| { | |||||
| return mStartStopSyncEnabled; | |||||
| } | |||||
| std::size_t numPeers() const | std::size_t numPeers() const | ||||
| { | { | ||||
| return mSessionPeerCounter.mSessionPeerCount; | 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. | // 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 | // 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 | // 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: | 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) | 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) | 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 | // 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) | if (oldTimeline.tempo != newTimeline.tempo) | ||||
| { | { | ||||
| mTempoCallback(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) | void handleTimelineFromSession(SessionId id, Timeline timeline) | ||||
| { | { | ||||
| debug(mIo->log()) << "Received timeline with tempo: " << timeline.tempo.bpm() | debug(mIo->log()) << "Received timeline with tempo: " << timeline.tempo.bpm() | ||||
| << " for session: " << id; | << " 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) | void joinSession(const Session& session) | ||||
| { | { | ||||
| const bool sessionIdChanged = mSessionId != session.sessionId; | const bool sessionIdChanged = mSessionId != session.sessionId; | ||||
| 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); | updateSessionTiming(session.timeline, session.measurement.xform); | ||||
| updateDiscovery(); | |||||
| if (sessionIdChanged) | if (sessionIdChanged) | ||||
| { | { | ||||
| @@ -279,11 +519,14 @@ private: | |||||
| // the beat on the old session timeline corresponding to the | // the beat on the old session timeline corresponding to the | ||||
| // current host time and mapping it to the new ghost time | // current host time and mapping it to the new ghost time | ||||
| // representation of the current host 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)}; | xform.hostToGhost(hostTime)}; | ||||
| resetSessionStartStopState(); | |||||
| updateSessionTiming(newTl, xform); | updateSessionTiming(newTl, xform); | ||||
| updateDiscovery(); | |||||
| mSessions.resetSession({mNodeId, newTl, {xform, hostTime}}); | mSessions.resetSession({mNodeId, newTl, {xform, hostTime}}); | ||||
| mPeers.resetPeers(); | mPeers.resetPeers(); | ||||
| @@ -299,25 +542,22 @@ private: | |||||
| Controller& mController; | Controller& mController; | ||||
| }; | }; | ||||
| struct RtTimelineSetter | |||||
| struct RtClientStateSetter | |||||
| { | { | ||||
| using CallbackDispatcher = | using CallbackDispatcher = | ||||
| typename IoContext::template LockFreeCallbackDispatcher<std::function<void()>, | typename IoContext::template LockFreeCallbackDispatcher<std::function<void()>, | ||||
| std::chrono::milliseconds>; | std::chrono::milliseconds>; | ||||
| using RtTimeline = std::pair<Timeline, std::chrono::microseconds>; | |||||
| RtTimelineSetter(Controller& controller) | |||||
| RtClientStateSetter(Controller& controller) | |||||
| : mController(controller) | : mController(controller) | ||||
| , mHasPendingTimelines(false) | |||||
| , mCallbackDispatcher( | , 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) | if (success) | ||||
| { | { | ||||
| mCallbackDispatcher.invoke(); | mCallbackDispatcher.invoke(); | ||||
| @@ -325,34 +565,51 @@ private: | |||||
| return success; | return success; | ||||
| } | } | ||||
| bool hasPendingTimelines() const | |||||
| { | |||||
| return mHasPendingTimelines; | |||||
| } | |||||
| private: | 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; | Controller& mController; | ||||
| // Assuming a wake up time of one ms for the threads owned by the CallbackDispatcher | // 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; | CallbackDispatcher mCallbackDispatcher; | ||||
| }; | }; | ||||
| struct SessionStartStopStateCallback | |||||
| { | |||||
| void operator()(SessionId sessionId, StartStopState startStopState) | |||||
| { | |||||
| mController.handleStartStopStateFromSession(sessionId, startStopState); | |||||
| } | |||||
| Controller& mController; | |||||
| }; | |||||
| struct SessionPeerCounter | struct SessionPeerCounter | ||||
| { | { | ||||
| SessionPeerCounter(Controller& controller, PeerCountCallback callback) | SessionPeerCounter(Controller& controller, PeerCountCallback callback) | ||||
| @@ -423,8 +680,10 @@ private: | |||||
| using IoType = typename util::Injected<IoContext>::type; | 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 = | using ControllerGateway = | ||||
| Gateway<typename ControllerPeers::GatewayObserver, Clock, IoType&>; | Gateway<typename ControllerPeers::GatewayObserver, Clock, IoType&>; | ||||
| @@ -464,28 +723,30 @@ private: | |||||
| }; | }; | ||||
| TempoCallback mTempoCallback; | TempoCallback mTempoCallback; | ||||
| StartStopStateCallback mStartStopStateCallback; | |||||
| Clock mClock; | Clock mClock; | ||||
| NodeId mNodeId; | NodeId mNodeId; | ||||
| SessionId mSessionId; | 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; | SessionPeerCounter mSessionPeerCounter; | ||||
| std::atomic<bool> mEnabled; | std::atomic<bool> mEnabled; | ||||
| std::atomic<bool> mStartStopSyncEnabled; | |||||
| util::Injected<IoContext> mIo; | util::Injected<IoContext> mIo; | ||||
| RtTimelineSetter mRtTimelineSetter; | |||||
| RtClientStateSetter mRtClientStateSetter; | |||||
| ControllerPeers mPeers; | ControllerPeers mPeers; | ||||
| @@ -38,14 +38,13 @@ public: | |||||
| NodeState nodeState, | NodeState nodeState, | ||||
| GhostXForm ghostXForm, | GhostXForm ghostXForm, | ||||
| Clock clock) | Clock clock) | ||||
| // TODO: Measurement should have an IoContext injected | |||||
| : mIo(std::move(io)), | |||||
| mMeasurement(addr, | |||||
| : mIo(std::move(io)) | |||||
| , mMeasurement(addr, | |||||
| nodeState.sessionId, | nodeState.sessionId, | ||||
| std::move(ghostXForm), | std::move(ghostXForm), | ||||
| std::move(clock), | 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(addr), | ||||
| std::move(observer), | std::move(observer), | ||||
| PeerState{std::move(nodeState), mMeasurement.endpoint()})) | PeerState{std::move(nodeState), mMeasurement.endpoint()})) | ||||
| @@ -84,7 +83,7 @@ public: | |||||
| private: | private: | ||||
| util::Injected<IoContext> mIo; | util::Injected<IoContext> mIo; | ||||
| MeasurementService<Clock, typename util::Injected<IoContext>::type::Log> mMeasurement; | |||||
| MeasurementService<Clock, typename std::remove_reference<IoContext>::type> mMeasurement; | |||||
| discovery:: | discovery:: | ||||
| IpV4Gateway<PeerObserver, PeerState, typename util::Injected<IoContext>::type&> | IpV4Gateway<PeerObserver, PeerState, typename util::Injected<IoContext>::type&> | ||||
| mPeerGateway; | mPeerGateway; | ||||
| @@ -20,13 +20,12 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <ableton/discovery/Payload.hpp> | #include <ableton/discovery/Payload.hpp> | ||||
| #include <ableton/discovery/Socket.hpp> | |||||
| #include <ableton/link/PayloadEntries.hpp> | #include <ableton/link/PayloadEntries.hpp> | ||||
| #include <ableton/link/PeerState.hpp> | #include <ableton/link/PeerState.hpp> | ||||
| #include <ableton/link/SessionId.hpp> | #include <ableton/link/SessionId.hpp> | ||||
| #include <ableton/link/v1/Messages.hpp> | #include <ableton/link/v1/Messages.hpp> | ||||
| #include <ableton/platforms/asio/AsioService.hpp> | |||||
| #include <ableton/util/Injected.hpp> | #include <ableton/util/Injected.hpp> | ||||
| #include <ableton/util/SafeAsyncHandler.hpp> | |||||
| #include <chrono> | #include <chrono> | ||||
| #include <memory> | #include <memory> | ||||
| @@ -35,83 +34,57 @@ namespace ableton | |||||
| namespace link | namespace link | ||||
| { | { | ||||
| template <typename IoService, typename Clock, typename Socket, typename Log> | |||||
| template <typename Clock, typename IoContext> | |||||
| struct Measurement | struct Measurement | ||||
| { | { | ||||
| using Point = std::pair<double, double>; | using Point = std::pair<double, double>; | ||||
| using Callback = std::function<void(std::vector<Point>)>; | using Callback = std::function<void(std::vector<Point>)>; | ||||
| using Micros = std::chrono::microseconds; | using Micros = std::chrono::microseconds; | ||||
| using Timer = typename IoService::Timer; | |||||
| static const std::size_t kNumberDataPoints = 100; | static const std::size_t kNumberDataPoints = 100; | ||||
| static const std::size_t kNumberMeasurements = 5; | static const std::size_t kNumberMeasurements = 5; | ||||
| Measurement() = default; | |||||
| Measurement(const PeerState& state, | Measurement(const PeerState& state, | ||||
| Callback callback, | Callback callback, | ||||
| asio::ip::address_v4 address, | asio::ip::address_v4 address, | ||||
| Clock clock, | 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(callback), | ||||
| std::move(address), | std::move(address), | ||||
| std::move(clock), | std::move(clock), | ||||
| std::move(log))) | |||||
| mIo)) | |||||
| { | { | ||||
| mpImpl->listen(); | 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> | 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, | Callback callback, | ||||
| asio::ip::address_v4 address, | asio::ip::address_v4 address, | ||||
| Clock clock, | 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) | , mSessionId(state.nodeState.sessionId) | ||||
| , mEndpoint(state.endpoint) | , mEndpoint(state.endpoint) | ||||
| , mCallback(std::move(callback)) | , mCallback(std::move(callback)) | ||||
| , mClock(std::move(clock)) | , mClock(std::move(clock)) | ||||
| , mTimer(util::injectVal(io.makeTimer())) | |||||
| , mTimer(io.makeTimer()) | |||||
| , mMeasurementsStarted(0) | , mMeasurementsStarted(0) | ||||
| , mLog(std::move(log)) | |||||
| , mLog(channel(io.log(), "Measurement on gateway@" + address.to_string())) | |||||
| , mSuccess(false) | , mSuccess(false) | ||||
| { | { | ||||
| configureUnicastSocket(*mpSocket, address); | |||||
| const auto ht = HostTime{mClock.micros()}; | const auto ht = HostTime{mClock.micros()}; | ||||
| sendPing(mEndpoint, discovery::makePayload(ht)); | sendPing(mEndpoint, discovery::makePayload(ht)); | ||||
| resetTimer(); | resetTimer(); | ||||
| @@ -119,9 +92,9 @@ struct Measurement | |||||
| void resetTimer() | 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 (!e) | ||||
| { | { | ||||
| if (mMeasurementsStarted < kNumberMeasurements) | if (mMeasurementsStarted < kNumberMeasurements) | ||||
| @@ -141,7 +114,7 @@ struct Measurement | |||||
| void listen() | 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 | // Operator to handle incoming messages on the interface | ||||
| @@ -156,7 +129,7 @@ struct Measurement | |||||
| if (header.messageType == v1::kPong) | if (header.messageType == v1::kPong) | ||||
| { | { | ||||
| debug(*mLog) << "Received Pong message from " << from; | |||||
| debug(mLog) << "Received Pong message from " << from; | |||||
| // parse for all entries | // parse for all entries | ||||
| SessionId sessionId{}; | SessionId sessionId{}; | ||||
| @@ -175,7 +148,7 @@ struct Measurement | |||||
| } | } | ||||
| catch (const std::runtime_error& err) | 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(); | listen(); | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -215,7 +188,7 @@ struct Measurement | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| debug(*mLog) << "Received invalid message from " << from; | |||||
| debug(mLog) << "Received invalid message from " << from; | |||||
| listen(); | listen(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -230,40 +203,40 @@ struct Measurement | |||||
| try | try | ||||
| { | { | ||||
| mpSocket->send(buffer.data(), numBytes, to); | |||||
| mSocket.send(buffer.data(), numBytes, to); | |||||
| } | } | ||||
| catch (const std::runtime_error& err) | 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() | void finish() | ||||
| { | { | ||||
| mTimer->cancel(); | |||||
| mTimer.cancel(); | |||||
| mCallback(std::move(mData)); | mCallback(std::move(mData)); | ||||
| mData = {}; | mData = {}; | ||||
| mSuccess = true; | mSuccess = true; | ||||
| debug(*mLog) << "Measuring " << mEndpoint << " done."; | |||||
| debug(mLog) << "Measuring " << mEndpoint << " done."; | |||||
| } | } | ||||
| void fail() | void fail() | ||||
| { | { | ||||
| mCallback(std::vector<Point>{}); | mCallback(std::vector<Point>{}); | ||||
| mData = {}; | mData = {}; | ||||
| debug(*mLog) << "Measuring " << mEndpoint << " failed."; | |||||
| debug(mLog) << "Measuring " << mEndpoint << " failed."; | |||||
| } | } | ||||
| std::shared_ptr<Socket> mpSocket; | |||||
| Socket mSocket; | |||||
| SessionId mSessionId; | SessionId mSessionId; | ||||
| asio::ip::udp::endpoint mEndpoint; | asio::ip::udp::endpoint mEndpoint; | ||||
| std::vector<std::pair<double, double>> mData; | std::vector<std::pair<double, double>> mData; | ||||
| Callback mCallback; | Callback mCallback; | ||||
| Clock mClock; | Clock mClock; | ||||
| util::Injected<typename IoService::Timer> mTimer; | |||||
| Timer mTimer; | |||||
| std::size_t mMeasurementsStarted; | std::size_t mMeasurementsStarted; | ||||
| util::Injected<Log> mLog; | |||||
| Log mLog; | |||||
| bool mSuccess; | bool mSuccess; | ||||
| }; | }; | ||||
| @@ -288,7 +261,7 @@ struct Measurement | |||||
| std::shared_ptr<Impl> mpImpl; | std::shared_ptr<Impl> mpImpl; | ||||
| }; | }; | ||||
| std::unique_ptr<IoService> mpIo; | |||||
| IoContext mIo; | |||||
| std::shared_ptr<Impl> mpImpl; | std::shared_ptr<Impl> mpImpl; | ||||
| }; | }; | ||||
| @@ -29,10 +29,8 @@ namespace link | |||||
| struct MeasurementEndpointV4 | struct MeasurementEndpointV4 | ||||
| { | { | ||||
| enum | |||||
| { | |||||
| key = 'mep4' | |||||
| }; | |||||
| static const std::int32_t key = 'mep4'; | |||||
| static_assert(key == 0x6d657034, "Unexpected byte order"); | |||||
| // Model the NetworkByteStreamSerializable concept | // Model the NetworkByteStreamSerializable concept | ||||
| friend std::uint32_t sizeInByteStream(const MeasurementEndpointV4 mep) | friend std::uint32_t sizeInByteStream(const MeasurementEndpointV4 mep) | ||||
| @@ -19,7 +19,6 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <ableton/discovery/Socket.hpp> | |||||
| #include <ableton/link/GhostXForm.hpp> | #include <ableton/link/GhostXForm.hpp> | ||||
| #include <ableton/link/Kalman.hpp> | #include <ableton/link/Kalman.hpp> | ||||
| #include <ableton/link/LinearRegression.hpp> | #include <ableton/link/LinearRegression.hpp> | ||||
| @@ -28,48 +27,34 @@ | |||||
| #include <ableton/link/PingResponder.hpp> | #include <ableton/link/PingResponder.hpp> | ||||
| #include <ableton/link/SessionId.hpp> | #include <ableton/link/SessionId.hpp> | ||||
| #include <ableton/link/v1/Messages.hpp> | #include <ableton/link/v1/Messages.hpp> | ||||
| #include <ableton/platforms/asio/AsioService.hpp> | |||||
| #include <ableton/util/Log.hpp> | |||||
| #include <map> | #include <map> | ||||
| #include <memory> | #include <memory> | ||||
| #include <thread> | |||||
| namespace ableton | namespace ableton | ||||
| { | { | ||||
| namespace link | namespace link | ||||
| { | { | ||||
| template <typename Clock, typename Log> | |||||
| template <typename Clock, typename IoContext> | |||||
| class MeasurementService | class MeasurementService | ||||
| { | { | ||||
| public: | public: | ||||
| using IoType = util::Injected<IoContext>; | |||||
| using Point = std::pair<double, double>; | 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, | MeasurementService(asio::ip::address_v4 address, | ||||
| SessionId sessionId, | SessionId sessionId, | ||||
| GhostXForm ghostXForm, | GhostXForm ghostXForm, | ||||
| Clock clock, | Clock clock, | ||||
| util::Injected<Log> log) | |||||
| IoType io) | |||||
| : mClock(std::move(clock)) | : mClock(std::move(clock)) | ||||
| , mLog(std::move(log)) | |||||
| , mIo(std::move(io)) | |||||
| , mPingResponder(std::move(address), | , mPingResponder(std::move(address), | ||||
| std::move(sessionId), | std::move(sessionId), | ||||
| std::move(ghostXForm), | std::move(ghostXForm), | ||||
| util::injectRef(mIo), | |||||
| mClock, | mClock, | ||||
| mLog) | |||||
| util::injectRef(*mIo)) | |||||
| { | { | ||||
| } | } | ||||
| @@ -78,10 +63,10 @@ public: | |||||
| ~MeasurementService() | ~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 | // 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) | void updateNodeState(const SessionId& sessionId, const GhostXForm& xform) | ||||
| @@ -100,19 +85,22 @@ public: | |||||
| { | { | ||||
| using namespace std; | using namespace std; | ||||
| mIo.post([this, state, handler] { | |||||
| mIo->async([this, state, handler] { | |||||
| const auto nodeId = state.nodeState.nodeId; | const auto nodeId = state.nodeState.nodeId; | ||||
| auto addr = mPingResponder.endpoint().address().to_v4(); | auto addr = mPingResponder.endpoint().address().to_v4(); | ||||
| auto callback = CompletionCallback<Handler>{*this, nodeId, handler}; | auto callback = CompletionCallback<Handler>{*this, nodeId, handler}; | ||||
| try | try | ||||
| { | { | ||||
| mMeasurementMap[nodeId] = | 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) | 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{}); | handler(GhostXForm{}); | ||||
| } | } | ||||
| }); | }); | ||||
| @@ -142,14 +130,14 @@ private: | |||||
| using namespace std; | using namespace std; | ||||
| using std::chrono::microseconds; | 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 | // don't delete the measurement object in its stack. Capture all | ||||
| // needed data separately from this, since this object may be | // needed data separately from this, since this object may be | ||||
| // gone by the time the block gets executed. | // gone by the time the block gets executed. | ||||
| auto nodeId = mNodeId; | auto nodeId = mNodeId; | ||||
| auto handler = mHandler; | 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); | const auto it = measurementMap.find(nodeId); | ||||
| if (it != measurementMap.end()) | if (it != measurementMap.end()) | ||||
| { | { | ||||
| @@ -166,20 +154,19 @@ private: | |||||
| }); | }); | ||||
| } | } | ||||
| MeasurementService& mService; | |||||
| MeasurementService& mMeasurementService; | |||||
| NodeId mNodeId; | NodeId mNodeId; | ||||
| Handler mHandler; | 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 | // the members are guaranteed to be valid when any final handlers | ||||
| // are begin run. | // are begin run. | ||||
| using MeasurementMap = std::map<NodeId, MeasurementInstance>; | |||||
| using MeasurementMap = std::map<NodeId, std::unique_ptr<MeasurementInstance>>; | |||||
| MeasurementMap mMeasurementMap; | MeasurementMap mMeasurementMap; | ||||
| Clock mClock; | Clock mClock; | ||||
| util::Injected<Log> mLog; | |||||
| platforms::asio::AsioService mIo; | |||||
| MeasurementServicePingResponder mPingResponder; | |||||
| IoType mIo; | |||||
| PingResponder<Clock, IoContext> mPingResponder; | |||||
| }; | }; | ||||
| } // namespace link | } // namespace link | ||||
| @@ -22,6 +22,7 @@ | |||||
| #include <ableton/discovery/Payload.hpp> | #include <ableton/discovery/Payload.hpp> | ||||
| #include <ableton/link/NodeId.hpp> | #include <ableton/link/NodeId.hpp> | ||||
| #include <ableton/link/SessionId.hpp> | #include <ableton/link/SessionId.hpp> | ||||
| #include <ableton/link/StartStopState.hpp> | |||||
| #include <ableton/link/Timeline.hpp> | #include <ableton/link/Timeline.hpp> | ||||
| namespace ableton | namespace ableton | ||||
| @@ -31,7 +32,8 @@ namespace link | |||||
| struct NodeState | struct NodeState | ||||
| { | { | ||||
| using Payload = decltype(discovery::makePayload(Timeline{}, SessionMembership{})); | |||||
| using Payload = | |||||
| decltype(discovery::makePayload(Timeline{}, SessionMembership{}, StartStopState{})); | |||||
| NodeId ident() const | NodeId ident() const | ||||
| { | { | ||||
| @@ -40,29 +42,34 @@ struct NodeState | |||||
| friend bool operator==(const NodeState& lhs, const NodeState& rhs) | 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) | 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> | template <typename It> | ||||
| static NodeState fromPayload(NodeId id, It begin, It end) | |||||
| static NodeState fromPayload(NodeId nodeId, It begin, It end) | |||||
| { | { | ||||
| using namespace std; | 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; | NodeId nodeId; | ||||
| SessionId sessionId; | SessionId sessionId; | ||||
| Timeline timeline; | Timeline timeline; | ||||
| StartStopState startStopState; | |||||
| }; | }; | ||||
| } // namespace link | } // 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 | struct HostTime | ||||
| { | { | ||||
| enum | |||||
| { | |||||
| key = '__ht' | |||||
| }; | |||||
| static const std::int32_t key = '__ht'; | |||||
| static_assert(key == 0x5f5f6874, "Unexpected byte order"); | |||||
| HostTime() = default; | HostTime() = default; | ||||
| @@ -66,12 +64,10 @@ struct HostTime | |||||
| std::chrono::microseconds time; | 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; | GHostTime() = default; | ||||
| @@ -106,10 +102,8 @@ struct GHostTime : HostTime | |||||
| struct PrevGHostTime | struct PrevGHostTime | ||||
| { | { | ||||
| enum | |||||
| { | |||||
| key = '_pgt' | |||||
| }; | |||||
| static const std::int32_t key = '_pgt'; | |||||
| static_assert(key == 0x5f706774, "Unexpected byte order"); | |||||
| PrevGHostTime() = default; | PrevGHostTime() = default; | ||||
| @@ -51,6 +51,11 @@ struct PeerState | |||||
| return nodeState.timeline; | return nodeState.timeline; | ||||
| } | } | ||||
| StartStopState startStopState() const | |||||
| { | |||||
| return nodeState.startStopState; | |||||
| } | |||||
| friend bool operator==(const PeerState& lhs, const PeerState& rhs) | friend bool operator==(const PeerState& lhs, const PeerState& rhs) | ||||
| { | { | ||||
| return lhs.nodeState == rhs.nodeState && lhs.endpoint == rhs.endpoint; | 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 | // SessionTimelineCallback is invoked with a session id and a timeline | ||||
| // whenever a new combination of these values is seen | // 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, | template <typename IoContext, | ||||
| typename SessionMembershipCallback, | typename SessionMembershipCallback, | ||||
| typename SessionTimelineCallback> | |||||
| typename SessionTimelineCallback, | |||||
| typename SessionStartStopStateCallback> | |||||
| class Peers | class Peers | ||||
| { | { | ||||
| // non-movable private implementation type | // non-movable private implementation type | ||||
| @@ -47,9 +51,10 @@ public: | |||||
| Peers(util::Injected<IoContext> io, | Peers(util::Injected<IoContext> io, | ||||
| SessionMembershipCallback membership, | 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, | Impl(util::Injected<IoContext> io, | ||||
| SessionMembershipCallback membership, | SessionMembershipCallback membership, | ||||
| SessionTimelineCallback timeline) | |||||
| SessionTimelineCallback timeline, | |||||
| SessionStartStopStateCallback startStop) | |||||
| : mIo(std::move(io)) | : mIo(std::move(io)) | ||||
| , mSessionMembershipCallback(std::move(membership)) | , mSessionMembershipCallback(std::move(membership)) | ||||
| , mSessionTimelineCallback(std::move(timeline)) | , mSessionTimelineCallback(std::move(timeline)) | ||||
| , mSessionStartStopStateCallback(std::move(startStop)) | |||||
| { | { | ||||
| } | } | ||||
| @@ -209,10 +216,14 @@ private: | |||||
| const auto peerSession = peerState.sessionId(); | const auto peerSession = peerState.sessionId(); | ||||
| const auto peerTimeline = peerState.timeline(); | const auto peerTimeline = peerState.timeline(); | ||||
| const auto peerStartStopState = peerState.startStopState(); | |||||
| bool isNewSessionTimeline = false; | bool isNewSessionTimeline = false; | ||||
| bool isNewSessionStartStopState = false; | |||||
| bool didSessionMembershipChange = false; | bool didSessionMembershipChange = false; | ||||
| { | { | ||||
| isNewSessionTimeline = !sessionTimelineExists(peerSession, peerTimeline); | isNewSessionTimeline = !sessionTimelineExists(peerSession, peerTimeline); | ||||
| isNewSessionStartStopState = | |||||
| !sessionStartStopStateExists(peerSession, peerStartStopState); | |||||
| auto peer = make_pair(move(peerState), move(gatewayAddr)); | auto peer = make_pair(move(peerState), move(gatewayAddr)); | ||||
| const auto idRange = equal_range(begin(mPeers), end(mPeers), peer, PeerIdComp{}); | const auto idRange = equal_range(begin(mPeers), end(mPeers), peer, PeerIdComp{}); | ||||
| @@ -254,6 +265,15 @@ private: | |||||
| mSessionTimelineCallback(peerSession, peerTimeline); | 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) | if (didSessionMembershipChange) | ||||
| { | { | ||||
| mSessionMembershipCallback(); | mSessionMembershipCallback(); | ||||
| @@ -297,14 +317,29 @@ private: | |||||
| mSessionMembershipCallback(); | mSessionMembershipCallback(); | ||||
| } | } | ||||
| bool sessionTimelineExists(const SessionId& session, const Timeline& tl) | |||||
| template <typename Predicate> | |||||
| bool hasPeerWith(const SessionId& sessionId, Predicate predicate) | |||||
| { | { | ||||
| using namespace std; | using namespace std; | ||||
| return find_if(begin(mPeers), end(mPeers), [&](const Peer& peer) { | 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); | }) != 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 | struct PeerIdComp | ||||
| { | { | ||||
| bool operator()(const Peer& lhs, const Peer& rhs) const | bool operator()(const Peer& lhs, const Peer& rhs) const | ||||
| @@ -324,6 +359,7 @@ private: | |||||
| util::Injected<IoContext> mIo; | util::Injected<IoContext> mIo; | ||||
| SessionMembershipCallback mSessionMembershipCallback; | SessionMembershipCallback mSessionMembershipCallback; | ||||
| SessionTimelineCallback mSessionTimelineCallback; | SessionTimelineCallback mSessionTimelineCallback; | ||||
| SessionStartStopStateCallback mSessionStartStopStateCallback; | |||||
| std::vector<Peer> mPeers; // sorted by peerId, unique by (peerId, addr) | std::vector<Peer> mPeers; // sorted by peerId, unique by (peerId, addr) | ||||
| }; | }; | ||||
| @@ -342,13 +378,19 @@ private: | |||||
| template <typename Io, | template <typename Io, | ||||
| typename SessionMembershipCallback, | 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, | 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 | } // namespace link | ||||
| @@ -96,5 +96,5 @@ inline std::chrono::microseconds fromPhaseEncodedBeats( | |||||
| return tl.fromBeats(tl.beatOrigin + originOffset + quantum - inversePhaseOffset); | 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/PayloadEntries.hpp> | ||||
| #include <ableton/link/SessionId.hpp> | #include <ableton/link/SessionId.hpp> | ||||
| #include <ableton/link/v1/Messages.hpp> | #include <ableton/link/v1/Messages.hpp> | ||||
| #include <ableton/platforms/asio/AsioWrapper.hpp> | |||||
| #include <ableton/util/Injected.hpp> | #include <ableton/util/Injected.hpp> | ||||
| #include <ableton/util/SafeAsyncHandler.hpp> | #include <ableton/util/SafeAsyncHandler.hpp> | ||||
| #include <chrono> | #include <chrono> | ||||
| #include <memory> | #include <memory> | ||||
| #include <thread> | |||||
| namespace ableton | namespace ableton | ||||
| { | { | ||||
| namespace link | namespace link | ||||
| { | { | ||||
| template <typename Io, typename Clock, typename Socket, typename Log> | |||||
| template <typename Clock, typename IoContext> | |||||
| class PingResponder | class PingResponder | ||||
| { | { | ||||
| using IoType = util::Injected<IoContext&>; | |||||
| using Socket = typename IoType::type::template Socket<v1::kMaxMessageSize>; | |||||
| public: | public: | ||||
| PingResponder(asio::ip::address_v4 address, | PingResponder(asio::ip::address_v4 address, | ||||
| SessionId sessionId, | SessionId sessionId, | ||||
| GhostXForm ghostXForm, | GhostXForm ghostXForm, | ||||
| util::Injected<Io> io, | |||||
| Clock clock, | 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(sessionId), | ||||
| std::move(ghostXForm), | std::move(ghostXForm), | ||||
| std::move(clock), | std::move(clock), | ||||
| std::move(log))) | |||||
| std::move(io))) | |||||
| { | { | ||||
| mpImpl->listen(); | mpImpl->listen(); | ||||
| } | } | ||||
| @@ -61,16 +60,16 @@ public: | |||||
| ~PingResponder() | ~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 | // it happens in the same thread as its handlers | ||||
| auto pImpl = mpImpl; | auto pImpl = mpImpl; | ||||
| mIo->post([pImpl]() mutable { pImpl.reset(); }); | |||||
| mIo->async([pImpl]() mutable { pImpl.reset(); }); | |||||
| } | } | ||||
| void updateNodeState(const SessionId& sessionId, const GhostXForm& xform) | void updateNodeState(const SessionId& sessionId, const GhostXForm& xform) | ||||
| { | { | ||||
| auto pImpl = mpImpl; | auto pImpl = mpImpl; | ||||
| mIo->post([pImpl, sessionId, xform] { | |||||
| mIo->async([pImpl, sessionId, xform] { | |||||
| pImpl->mSessionId = std::move(sessionId); | pImpl->mSessionId = std::move(sessionId); | ||||
| pImpl->mGhostXForm = std::move(xform); | pImpl->mGhostXForm = std::move(xform); | ||||
| }); | }); | ||||
| @@ -94,19 +93,17 @@ public: | |||||
| private: | private: | ||||
| struct Impl : std::enable_shared_from_this<Impl> | 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, | SessionId sessionId, | ||||
| GhostXForm ghostXForm, | GhostXForm ghostXForm, | ||||
| Clock clock, | Clock clock, | ||||
| util::Injected<Log> log) | |||||
| IoType io) | |||||
| : mSessionId(std::move(sessionId)) | : mSessionId(std::move(sessionId)) | ||||
| , mGhostXForm(std::move(ghostXForm)) | , mGhostXForm(std::move(ghostXForm)) | ||||
| , mClock(std::move(clock)) | , 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() | void listen() | ||||
| @@ -131,7 +128,7 @@ private: | |||||
| sizeInByteStream(makePayload(HostTime{}, PrevGHostTime{})); | sizeInByteStream(makePayload(HostTime{}, PrevGHostTime{})); | ||||
| if (header.messageType == v1::kPing && payloadSize <= maxPayloadSize) | if (header.messageType == v1::kPing && payloadSize <= maxPayloadSize) | ||||
| { | { | ||||
| debug(*mLog) << "Received ping message from " << from; | |||||
| debug(mLog) << " Received ping message from " << from; | |||||
| try | try | ||||
| { | { | ||||
| @@ -139,12 +136,12 @@ private: | |||||
| } | } | ||||
| catch (const std::runtime_error& err) | 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 | else | ||||
| { | { | ||||
| info(*mLog) << "Received invalid Message from " << from << "."; | |||||
| info(mLog) << " Received invalid Message from " << from << "."; | |||||
| } | } | ||||
| listen(); | listen(); | ||||
| } | } | ||||
| @@ -173,11 +170,11 @@ private: | |||||
| SessionId mSessionId; | SessionId mSessionId; | ||||
| GhostXForm mGhostXForm; | GhostXForm mGhostXForm; | ||||
| Clock mClock; | Clock mClock; | ||||
| util::Injected<Log> mLog; | |||||
| typename IoType::type::Log mLog; | |||||
| Socket mSocket; | Socket mSocket; | ||||
| }; | }; | ||||
| util::Injected<Io> mIo; | |||||
| IoType mIo; | |||||
| std::shared_ptr<Impl> mpImpl; | std::shared_ptr<Impl> mpImpl; | ||||
| }; | }; | ||||
| @@ -33,10 +33,8 @@ using SessionId = NodeId; | |||||
| // A payload entry indicating membership in a particular session | // A payload entry indicating membership in a particular session | ||||
| struct SessionMembership | struct SessionMembership | ||||
| { | { | ||||
| enum | |||||
| { | |||||
| key = 'sess' | |||||
| }; | |||||
| static const std::int32_t key = 'sess'; | |||||
| static_assert(key == 0x73657373, "Unexpected byte order"); | |||||
| // Model the NetworkByteStreamSerializable concept | // Model the NetworkByteStreamSerializable concept | ||||
| friend std::uint32_t sizeInByteStream(const SessionMembership& sm) | 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); | range.first->measurement = move(measurement); | ||||
| // If session times too close - fall back to session id order | // If session times too close - fall back to session id order | ||||
| const auto ghostDiff = newGhost - curGhost; | 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 | // The new session wins, switch over to it | ||||
| auto current = mCurrent; | 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 | 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 | Beats toBeats(const std::chrono::microseconds time) const | ||||
| { | { | ||||
| @@ -22,15 +22,15 @@ | |||||
| #include <ableton/link/Controller.hpp> | #include <ableton/link/Controller.hpp> | ||||
| #include <ableton/util/Log.hpp> | #include <ableton/util/Log.hpp> | ||||
| #if LINK_PLATFORM_WINDOWS | |||||
| #if defined(LINK_PLATFORM_WINDOWS) | |||||
| #include <ableton/platforms/asio/Context.hpp> | #include <ableton/platforms/asio/Context.hpp> | ||||
| #include <ableton/platforms/windows/Clock.hpp> | #include <ableton/platforms/windows/Clock.hpp> | ||||
| #include <ableton/platforms/windows/ScanIpIfAddrs.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/asio/Context.hpp> | ||||
| #include <ableton/platforms/darwin/Clock.hpp> | #include <ableton/platforms/darwin/Clock.hpp> | ||||
| #include <ableton/platforms/posix/ScanIpIfAddrs.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/asio/Context.hpp> | ||||
| #include <ableton/platforms/linux/Clock.hpp> | #include <ableton/platforms/linux/Clock.hpp> | ||||
| #include <ableton/platforms/posix/ScanIpIfAddrs.hpp> | #include <ableton/platforms/posix/ScanIpIfAddrs.hpp> | ||||
| @@ -43,28 +43,25 @@ namespace link | |||||
| namespace platform | namespace platform | ||||
| { | { | ||||
| #if LINK_PLATFORM_WINDOWS | |||||
| #if defined(LINK_PLATFORM_WINDOWS) | |||||
| using Clock = platforms::windows::Clock; | using Clock = platforms::windows::Clock; | ||||
| using IoContext = | using IoContext = | ||||
| platforms::asio::Context<platforms::windows::ScanIpIfAddrs, util::NullLog>; | platforms::asio::Context<platforms::windows::ScanIpIfAddrs, util::NullLog>; | ||||
| #elif LINK_PLATFORM_MACOSX | |||||
| #elif defined(LINK_PLATFORM_MACOSX) | |||||
| using Clock = platforms::darwin::Clock; | using Clock = platforms::darwin::Clock; | ||||
| using IoContext = | using IoContext = | ||||
| platforms::asio::Context<platforms::posix::ScanIpIfAddrs, util::NullLog>; | 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; | using Clock = platforms::linux::ClockMonotonic; | ||||
| #endif | |||||
| using IoContext = | using IoContext = | ||||
| platforms::asio::Context<platforms::posix::ScanIpIfAddrs, util::NullLog>; | platforms::asio::Context<platforms::posix::ScanIpIfAddrs, util::NullLog>; | ||||
| #endif | #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") | #pragma push_macro("ASIO_NO_TYPEID") | ||||
| #define ASIO_NO_TYPEID 1 | #define ASIO_NO_TYPEID 1 | ||||
| #if LINK_PLATFORM_WINDOWS | |||||
| #if defined(LINK_PLATFORM_WINDOWS) | |||||
| #pragma push_macro("INCL_EXTRA_HTON_FUNCTIONS") | #pragma push_macro("INCL_EXTRA_HTON_FUNCTIONS") | ||||
| #define INCL_EXTRA_HTON_FUNCTIONS 1 | #define INCL_EXTRA_HTON_FUNCTIONS 1 | ||||
| #endif | #endif | ||||
| #if defined(WIN32) || defined(_WIN32) | |||||
| #if !defined(_WIN32_WINNT) | |||||
| #define _WIN32_WINNT 0x0501 | |||||
| #endif | |||||
| #endif | |||||
| #if defined(__clang__) | #if defined(__clang__) | ||||
| #pragma clang diagnostic push | #pragma clang diagnostic push | ||||
| #if __has_warning("-Wcomma") | #if __has_warning("-Wcomma") | ||||
| #pragma clang diagnostic ignored "-Wcomma" | #pragma clang diagnostic ignored "-Wcomma" | ||||
| #endif | #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 | #endif | ||||
| #if defined(_MSC_VER) | #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 | #endif | ||||
| #include <asio.hpp> | #include <asio.hpp> | ||||
| #include <asio/system_timer.hpp> | #include <asio/system_timer.hpp> | ||||
| #if LINK_PLATFORM_WINDOWS | |||||
| #if defined(LINK_PLATFORM_WINDOWS) | |||||
| #pragma pop_macro("INCL_EXTRA_HTON_FUNCTIONS") | #pragma pop_macro("INCL_EXTRA_HTON_FUNCTIONS") | ||||
| #endif | #endif | ||||
| @@ -69,6 +75,7 @@ | |||||
| #if defined(_MSC_VER) | #if defined(_MSC_VER) | ||||
| #pragma warning(pop) | #pragma warning(pop) | ||||
| #undef _SCL_SECURE_NO_WARNINGS | |||||
| #endif | #endif | ||||
| #if defined(__clang__) | #if defined(__clang__) | ||||
| @@ -85,5 +85,5 @@ private: | |||||
| std::thread mThread; | std::thread mThread; | ||||
| }; | }; | ||||
| } // platforms | |||||
| } // ableton | |||||
| } // namespace platforms | |||||
| } // namespace ableton | |||||
| @@ -38,6 +38,6 @@ AsioAddrType makeAddress(const char* pAddr) | |||||
| return AsioAddrType{bytes}; | return AsioAddrType{bytes}; | ||||
| } | } | ||||
| } // asio | |||||
| } // platforms | |||||
| } // ableton | |||||
| } // namespace asio | |||||
| } // namespace platforms | |||||
| } // namespace ableton | |||||
| @@ -49,17 +49,7 @@ public: | |||||
| }; | }; | ||||
| using ClockMonotonic = Clock<CLOCK_MONOTONIC>; | using ClockMonotonic = Clock<CLOCK_MONOTONIC>; | ||||
| #ifdef CLOCK_MONOTONIC_RAW | |||||
| using ClockMonotonicRaw = Clock<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 linux | ||||
| } // namespace platforms | } // namespace platforms | ||||
| @@ -67,7 +67,7 @@ private: | |||||
| struct ifaddrs* interfaces = NULL; | struct ifaddrs* interfaces = NULL; | ||||
| }; | }; | ||||
| } // detail | |||||
| } // namespace detail | |||||
| // Posix implementation of ip interface address scanner | // Posix implementation of ip interface address scanner | ||||
| @@ -93,7 +93,7 @@ private: | |||||
| IP_ADAPTER_ADDRESSES* adapter; | IP_ADAPTER_ADDRESSES* adapter; | ||||
| }; | }; | ||||
| } // detail | |||||
| } // namespace detail | |||||
| struct ScanIpIfAddrs | struct ScanIpIfAddrs | ||||
| { | { | ||||
| @@ -26,18 +26,15 @@ | |||||
| * which are specific to that library. | * which are specific to that library. | ||||
| */ | */ | ||||
| // Visual Studio | |||||
| #if defined(_MSC_VER) | #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) | #pragma warning(disable : 4702) | ||||
| #endif | #endif | ||||
| #include <catch.hpp> | #include <catch.hpp> | ||||
| // Visual Studio | |||||
| #if defined(_MSC_VER) | #if defined(_MSC_VER) | ||||
| #pragma warning(pop) | #pragma warning(pop) | ||||
| #endif | #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; | double mSampleRate; | ||||
| }; | }; | ||||
| } // util | |||||
| } // ableton | |||||
| } // namespace util | |||||
| } // namespace ableton | |||||
| @@ -2,7 +2,7 @@ | |||||
| // asio.hpp | // 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 | // 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) | // 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_allocator.hpp" | ||||
| #include "asio/associated_executor.hpp" | #include "asio/associated_executor.hpp" | ||||
| #include "asio/async_result.hpp" | #include "asio/async_result.hpp" | ||||
| #include "asio/awaitable.hpp" | |||||
| #include "asio/basic_datagram_socket.hpp" | #include "asio/basic_datagram_socket.hpp" | ||||
| #include "asio/basic_deadline_timer.hpp" | #include "asio/basic_deadline_timer.hpp" | ||||
| #include "asio/basic_io_object.hpp" | #include "asio/basic_io_object.hpp" | ||||
| @@ -25,6 +26,7 @@ | |||||
| #include "asio/basic_seq_packet_socket.hpp" | #include "asio/basic_seq_packet_socket.hpp" | ||||
| #include "asio/basic_serial_port.hpp" | #include "asio/basic_serial_port.hpp" | ||||
| #include "asio/basic_signal_set.hpp" | #include "asio/basic_signal_set.hpp" | ||||
| #include "asio/basic_socket.hpp" | |||||
| #include "asio/basic_socket_acceptor.hpp" | #include "asio/basic_socket_acceptor.hpp" | ||||
| #include "asio/basic_socket_iostream.hpp" | #include "asio/basic_socket_iostream.hpp" | ||||
| #include "asio/basic_socket_streambuf.hpp" | #include "asio/basic_socket_streambuf.hpp" | ||||
| @@ -40,13 +42,14 @@ | |||||
| #include "asio/buffered_write_stream_fwd.hpp" | #include "asio/buffered_write_stream_fwd.hpp" | ||||
| #include "asio/buffered_write_stream.hpp" | #include "asio/buffered_write_stream.hpp" | ||||
| #include "asio/buffers_iterator.hpp" | #include "asio/buffers_iterator.hpp" | ||||
| #include "asio/co_spawn.hpp" | |||||
| #include "asio/completion_condition.hpp" | #include "asio/completion_condition.hpp" | ||||
| #include "asio/compose.hpp" | |||||
| #include "asio/connect.hpp" | #include "asio/connect.hpp" | ||||
| #include "asio/coroutine.hpp" | #include "asio/coroutine.hpp" | ||||
| #include "asio/datagram_socket_service.hpp" | |||||
| #include "asio/deadline_timer_service.hpp" | |||||
| #include "asio/deadline_timer.hpp" | #include "asio/deadline_timer.hpp" | ||||
| #include "asio/defer.hpp" | #include "asio/defer.hpp" | ||||
| #include "asio/detached.hpp" | |||||
| #include "asio/dispatch.hpp" | #include "asio/dispatch.hpp" | ||||
| #include "asio/error.hpp" | #include "asio/error.hpp" | ||||
| #include "asio/error_code.hpp" | #include "asio/error_code.hpp" | ||||
| @@ -61,7 +64,7 @@ | |||||
| #include "asio/handler_alloc_hook.hpp" | #include "asio/handler_alloc_hook.hpp" | ||||
| #include "asio/handler_continuation_hook.hpp" | #include "asio/handler_continuation_hook.hpp" | ||||
| #include "asio/handler_invoke_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.hpp" | ||||
| #include "asio/io_context_strand.hpp" | #include "asio/io_context_strand.hpp" | ||||
| #include "asio/io_service.hpp" | #include "asio/io_service.hpp" | ||||
| @@ -73,6 +76,8 @@ | |||||
| #include "asio/ip/address_v6.hpp" | #include "asio/ip/address_v6.hpp" | ||||
| #include "asio/ip/address_v6_iterator.hpp" | #include "asio/ip/address_v6_iterator.hpp" | ||||
| #include "asio/ip/address_v6_range.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/bad_address_cast.hpp" | ||||
| #include "asio/ip/basic_endpoint.hpp" | #include "asio/ip/basic_endpoint.hpp" | ||||
| #include "asio/ip/basic_resolver.hpp" | #include "asio/ip/basic_resolver.hpp" | ||||
| @@ -84,7 +89,6 @@ | |||||
| #include "asio/ip/multicast.hpp" | #include "asio/ip/multicast.hpp" | ||||
| #include "asio/ip/resolver_base.hpp" | #include "asio/ip/resolver_base.hpp" | ||||
| #include "asio/ip/resolver_query_base.hpp" | #include "asio/ip/resolver_query_base.hpp" | ||||
| #include "asio/ip/resolver_service.hpp" | |||||
| #include "asio/ip/tcp.hpp" | #include "asio/ip/tcp.hpp" | ||||
| #include "asio/ip/udp.hpp" | #include "asio/ip/udp.hpp" | ||||
| #include "asio/ip/unicast.hpp" | #include "asio/ip/unicast.hpp" | ||||
| @@ -96,48 +100,47 @@ | |||||
| #include "asio/local/connect_pair.hpp" | #include "asio/local/connect_pair.hpp" | ||||
| #include "asio/local/datagram_protocol.hpp" | #include "asio/local/datagram_protocol.hpp" | ||||
| #include "asio/local/stream_protocol.hpp" | #include "asio/local/stream_protocol.hpp" | ||||
| #include "asio/packaged_task.hpp" | |||||
| #include "asio/placeholders.hpp" | #include "asio/placeholders.hpp" | ||||
| #include "asio/posix/basic_descriptor.hpp" | #include "asio/posix/basic_descriptor.hpp" | ||||
| #include "asio/posix/basic_stream_descriptor.hpp" | #include "asio/posix/basic_stream_descriptor.hpp" | ||||
| #include "asio/posix/descriptor.hpp" | |||||
| #include "asio/posix/descriptor_base.hpp" | #include "asio/posix/descriptor_base.hpp" | ||||
| #include "asio/posix/stream_descriptor.hpp" | #include "asio/posix/stream_descriptor.hpp" | ||||
| #include "asio/posix/stream_descriptor_service.hpp" | |||||
| #include "asio/post.hpp" | #include "asio/post.hpp" | ||||
| #include "asio/raw_socket_service.hpp" | |||||
| #include "asio/read.hpp" | #include "asio/read.hpp" | ||||
| #include "asio/read_at.hpp" | #include "asio/read_at.hpp" | ||||
| #include "asio/read_until.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.hpp" | ||||
| #include "asio/serial_port_base.hpp" | #include "asio/serial_port_base.hpp" | ||||
| #include "asio/serial_port_service.hpp" | |||||
| #include "asio/signal_set.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/socket_base.hpp" | ||||
| #include "asio/steady_timer.hpp" | |||||
| #include "asio/strand.hpp" | #include "asio/strand.hpp" | ||||
| #include "asio/stream_socket_service.hpp" | |||||
| #include "asio/streambuf.hpp" | #include "asio/streambuf.hpp" | ||||
| #include "asio/system_context.hpp" | |||||
| #include "asio/system_error.hpp" | #include "asio/system_error.hpp" | ||||
| #include "asio/system_executor.hpp" | #include "asio/system_executor.hpp" | ||||
| #include "asio/system_timer.hpp" | |||||
| #include "asio/this_coro.hpp" | |||||
| #include "asio/thread.hpp" | #include "asio/thread.hpp" | ||||
| #include "asio/thread_pool.hpp" | #include "asio/thread_pool.hpp" | ||||
| #include "asio/time_traits.hpp" | #include "asio/time_traits.hpp" | ||||
| #include "asio/use_awaitable.hpp" | |||||
| #include "asio/use_future.hpp" | |||||
| #include "asio/uses_executor.hpp" | #include "asio/uses_executor.hpp" | ||||
| #include "asio/version.hpp" | #include "asio/version.hpp" | ||||
| #include "asio/wait_traits.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_object_handle.hpp" | ||||
| #include "asio/windows/basic_overlapped_handle.hpp" | |||||
| #include "asio/windows/basic_random_access_handle.hpp" | #include "asio/windows/basic_random_access_handle.hpp" | ||||
| #include "asio/windows/basic_stream_handle.hpp" | #include "asio/windows/basic_stream_handle.hpp" | ||||
| #include "asio/windows/object_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/overlapped_ptr.hpp" | ||||
| #include "asio/windows/random_access_handle.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.hpp" | ||||
| #include "asio/windows/stream_handle_service.hpp" | |||||
| #include "asio/write.hpp" | #include "asio/write.hpp" | ||||
| #include "asio/write_at.hpp" | #include "asio/write_at.hpp" | ||||
| @@ -2,7 +2,7 @@ | |||||
| // associated_allocator.hpp | // 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 | // 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) | // 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); | 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 | } // namespace asio | ||||
| #include "asio/detail/pop_options.hpp" | #include "asio/detail/pop_options.hpp" | ||||
| @@ -2,7 +2,7 @@ | |||||
| // associated_executor.hpp | // 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 | // 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) | // 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()); | 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 | } // namespace asio | ||||
| #include "asio/detail/pop_options.hpp" | #include "asio/detail/pop_options.hpp" | ||||
| @@ -2,7 +2,7 @@ | |||||
| // async_result.hpp | // 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 | // 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) | // 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/config.hpp" | ||||
| #include "asio/detail/type_traits.hpp" | #include "asio/detail/type_traits.hpp" | ||||
| #include "asio/handler_type.hpp" | |||||
| #include "asio/detail/variadic_templates.hpp" | |||||
| #include "asio/detail/push_options.hpp" | #include "asio/detail/push_options.hpp" | ||||
| @@ -25,58 +25,134 @@ namespace asio { | |||||
| /// An interface for customising the behaviour of an initiating function. | /// 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 | class async_result | ||||
| { | { | ||||
| public: | public: | ||||
| /// The concrete completion handler type for the specific signature. | |||||
| typedef CompletionToken completion_handler_type; | |||||
| /// The return type of the initiating function. | /// The return type of the initiating function. | ||||
| typedef void type; | |||||
| typedef void return_type; | |||||
| /// Construct an async result from a given handler. | /// Construct an async result from a given handler. | ||||
| /** | /** | ||||
| * When using a specalised async_result, the constructor has an opportunity | * 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. | /// 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 | struct async_completion | ||||
| { | { | ||||
| /// The real handler type to be used for the asynchronous operation. | /// 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. | /// 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) | #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) | #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. | /// A copy of, or reference to, a real handler object. | ||||
| #if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | #if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | ||||
| typename conditional< | 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) | #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) | #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | ||||
| /// The result of the asynchronous operation's initiating function. | /// 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 { | 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) | #if defined(GENERATING_DOCUMENTATION) | ||||
| # define ASIO_INITFN_RESULT_TYPE(h, sig) \ | |||||
| # define ASIO_INITFN_RESULT_TYPE(ct, sig) \ | |||||
| void_or_deduced | void_or_deduced | ||||
| #elif defined(_MSC_VER) && (_MSC_VER < 1500) | #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 | #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::async_result< \ | ||||
| typename ::asio::handler_type<h, sig>::type>::type | |||||
| typename ::asio::decay<ct>::type, sig>::completion_handler_type | |||||
| #endif | #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 | #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 | // 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 | // 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) | // 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 "asio/detail/config.hpp" | ||||
| #include <cstddef> | #include <cstddef> | ||||
| #include "asio/basic_socket.hpp" | #include "asio/basic_socket.hpp" | ||||
| #include "asio/datagram_socket_service.hpp" | |||||
| #include "asio/detail/handler_type_requirements.hpp" | #include "asio/detail/handler_type_requirements.hpp" | ||||
| #include "asio/detail/non_const_lvalue.hpp" | |||||
| #include "asio/detail/throw_error.hpp" | #include "asio/detail/throw_error.hpp" | ||||
| #include "asio/detail/type_traits.hpp" | #include "asio/detail/type_traits.hpp" | ||||
| #include "asio/error.hpp" | #include "asio/error.hpp" | ||||
| @@ -28,6 +28,15 @@ | |||||
| namespace asio { | 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. | /// Provides datagram-oriented socket functionality. | ||||
| /** | /** | ||||
| * The basic_datagram_socket class template provides asynchronous and blocking | * The basic_datagram_socket class template provides asynchronous and blocking | ||||
| @@ -37,14 +46,29 @@ namespace asio { | |||||
| * @e Distinct @e objects: Safe.@n | * @e Distinct @e objects: Safe.@n | ||||
| * @e Shared @e objects: Unsafe. | * @e Shared @e objects: Unsafe. | ||||
| */ | */ | ||||
| template <typename Protocol, | |||||
| typename DatagramSocketService = datagram_socket_service<Protocol> > | |||||
| template <typename Protocol, typename Executor> | |||||
| class basic_datagram_socket | class basic_datagram_socket | ||||
| : public basic_socket<Protocol, DatagramSocketService> | |||||
| : public basic_socket<Protocol, Executor> | |||||
| { | { | ||||
| public: | 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. | /// 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. | /// The protocol type. | ||||
| typedef Protocol protocol_type; | typedef Protocol protocol_type; | ||||
| @@ -57,12 +81,29 @@ public: | |||||
| * This constructor creates a datagram socket without opening it. The open() | * 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. | * 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. | * 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. | * @param protocol An object specifying protocol parameters to be used. | ||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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 | * to the specified endpoint on the local machine. The protocol used is the | ||||
| * protocol associated with the given endpoint. | * 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 | * @param endpoint An endpoint on the local machine to which the datagram | ||||
| * socket will be bound. | * socket will be bound. | ||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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 | * This constructor creates a datagram socket object to hold an existing | ||||
| * native socket. | * 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. | * @param protocol An object specifying protocol parameters to be used. | ||||
| * | * | ||||
| @@ -121,10 +205,34 @@ public: | |||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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) | 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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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; | return *this; | ||||
| } | } | ||||
| @@ -172,15 +280,16 @@ public: | |||||
| * will occur. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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; | return *this; | ||||
| } | } | ||||
| #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | #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. | /// Send some data on a connected socket. | ||||
| /** | /** | ||||
| * This function is used to send data on the datagram socket. The function | * 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) | std::size_t send(const ConstBufferSequence& buffers) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -262,8 +381,8 @@ public: | |||||
| socket_base::message_flags flags) | socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -289,8 +408,8 @@ public: | |||||
| std::size_t send(const ConstBufferSequence& buffers, | std::size_t send(const ConstBufferSequence& buffers, | ||||
| socket_base::message_flags flags, asio::error_code& ec) | 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. | /// Start an asynchronous send on a connected socket. | ||||
| @@ -311,9 +430,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Use the async_send_to function to send data on an unconnected datagram | ||||
| @@ -334,12 +453,10 @@ public: | |||||
| async_send(const ConstBufferSequence& buffers, | async_send(const ConstBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Start an asynchronous send on a connected socket. | ||||
| @@ -362,9 +479,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Use the async_send_to function to send data on an unconnected datagram | ||||
| @@ -377,12 +494,9 @@ public: | |||||
| socket_base::message_flags flags, | socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Send a datagram to the specified endpoint. | ||||
| @@ -415,8 +529,8 @@ public: | |||||
| const endpoint_type& destination) | const endpoint_type& destination) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send_to"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -442,8 +556,8 @@ public: | |||||
| const endpoint_type& destination, socket_base::message_flags flags) | const endpoint_type& destination, socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send_to"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -469,7 +583,7 @@ public: | |||||
| const endpoint_type& destination, socket_base::message_flags flags, | const endpoint_type& destination, socket_base::message_flags flags, | ||||
| asio::error_code& ec) | 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); | buffers, destination, flags, ec); | ||||
| } | } | ||||
| @@ -494,9 +608,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * To send a single data buffer use the @ref buffer function as follows: | * To send a single data buffer use the @ref buffer function as follows: | ||||
| @@ -517,13 +631,10 @@ public: | |||||
| const endpoint_type& destination, | const endpoint_type& destination, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Start an asynchronous send. | ||||
| @@ -549,9 +660,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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> | template <typename ConstBufferSequence, typename WriteHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(WriteHandler, | ASIO_INITFN_RESULT_TYPE(WriteHandler, | ||||
| @@ -560,13 +671,9 @@ public: | |||||
| const endpoint_type& destination, socket_base::message_flags flags, | const endpoint_type& destination, socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Receive some data on a connected socket. | ||||
| @@ -597,8 +704,8 @@ public: | |||||
| std::size_t receive(const MutableBufferSequence& buffers) | std::size_t receive(const MutableBufferSequence& buffers) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -626,8 +733,8 @@ public: | |||||
| socket_base::message_flags flags) | socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -654,8 +761,8 @@ public: | |||||
| std::size_t receive(const MutableBufferSequence& buffers, | std::size_t receive(const MutableBufferSequence& buffers, | ||||
| socket_base::message_flags flags, asio::error_code& ec) | 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. | /// Start an asynchronous receive on a connected socket. | ||||
| @@ -676,9 +783,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Use the async_receive_from function to receive data on an unconnected | ||||
| @@ -700,12 +807,10 @@ public: | |||||
| async_receive(const MutableBufferSequence& buffers, | async_receive(const MutableBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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. | /// Start an asynchronous receive on a connected socket. | ||||
| @@ -728,9 +833,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Use the async_receive_from function to receive data on an unconnected | ||||
| @@ -743,12 +848,9 @@ public: | |||||
| socket_base::message_flags flags, | socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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. | /// Receive a datagram with the endpoint of the sender. | ||||
| @@ -782,8 +884,8 @@ public: | |||||
| endpoint_type& sender_endpoint) | endpoint_type& sender_endpoint) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive_from"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -809,8 +911,8 @@ public: | |||||
| endpoint_type& sender_endpoint, socket_base::message_flags flags) | endpoint_type& sender_endpoint, socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive_from"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -836,8 +938,8 @@ public: | |||||
| endpoint_type& sender_endpoint, socket_base::message_flags flags, | endpoint_type& sender_endpoint, socket_base::message_flags flags, | ||||
| asio::error_code& ec) | 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. | /// Start an asynchronous receive. | ||||
| @@ -863,9 +965,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * To receive into a single data buffer use the @ref buffer function as | * To receive into a single data buffer use the @ref buffer function as | ||||
| @@ -883,13 +985,10 @@ public: | |||||
| endpoint_type& sender_endpoint, | endpoint_type& sender_endpoint, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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. | /// Start an asynchronous receive. | ||||
| @@ -917,9 +1016,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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> | template <typename MutableBufferSequence, typename ReadHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(ReadHandler, | ASIO_INITFN_RESULT_TYPE(ReadHandler, | ||||
| @@ -928,14 +1027,85 @@ public: | |||||
| endpoint_type& sender_endpoint, socket_base::message_flags flags, | endpoint_type& sender_endpoint, socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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 | } // namespace asio | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_deadline_timer.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -21,11 +21,15 @@ | |||||
| || defined(GENERATING_DOCUMENTATION) | || defined(GENERATING_DOCUMENTATION) | ||||
| #include <cstddef> | #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/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/throw_error.hpp" | ||||
| #include "asio/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" | #include "asio/detail/push_options.hpp" | ||||
| @@ -50,7 +54,7 @@ namespace asio { | |||||
| * Performing a blocking wait: | * Performing a blocking wait: | ||||
| * @code | * @code | ||||
| * // Construct a timer without setting an expiry time. | * // 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. | * // Set an expiry time relative to now. | ||||
| * timer.expires_from_now(boost::posix_time::seconds(5)); | * timer.expires_from_now(boost::posix_time::seconds(5)); | ||||
| @@ -73,7 +77,7 @@ namespace asio { | |||||
| * ... | * ... | ||||
| * | * | ||||
| * // Construct a timer with an absolute expiry time. | * // 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")); | * boost::posix_time::time_from_string("2005-12-07 23:59:59.000")); | ||||
| * | * | ||||
| * // Start an asynchronous wait. | * // Start an asynchronous wait. | ||||
| @@ -121,11 +125,13 @@ namespace asio { | |||||
| */ | */ | ||||
| template <typename Time, | template <typename Time, | ||||
| typename TimeTraits = asio::time_traits<Time>, | typename TimeTraits = asio::time_traits<Time>, | ||||
| typename TimerService = deadline_timer_service<Time, TimeTraits> > | |||||
| typename Executor = executor> | |||||
| class basic_deadline_timer | class basic_deadline_timer | ||||
| : public basic_io_object<TimerService> | |||||
| { | { | ||||
| public: | public: | ||||
| /// The type of the executor associated with the object. | |||||
| typedef Executor executor_type; | |||||
| /// The time traits type. | /// The time traits type. | ||||
| typedef TimeTraits traits_type; | typedef TimeTraits traits_type; | ||||
| @@ -141,11 +147,30 @@ public: | |||||
| * expires_at() or expires_from_now() functions must be called to set an | * expires_at() or expires_from_now() functions must be called to set an | ||||
| * expiry time before the timer can be waited on. | * 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. | * 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 | * @param expiry_time The expiry time to be used for the timer, expressed | ||||
| * as an absolute time. | * 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; | 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"); | asio::detail::throw_error(ec, "expires_at"); | ||||
| } | } | ||||
| @@ -172,22 +219,98 @@ public: | |||||
| /** | /** | ||||
| * This constructor creates a timer and sets the expiry 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 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 | * @param expiry_time The expiry time to be used for the timer, relative to | ||||
| * now. | * now. | ||||
| */ | */ | ||||
| basic_deadline_timer(asio::io_context& io_context, | |||||
| basic_deadline_timer(const executor_type& ex, | |||||
| const duration_type& expiry_time) | const duration_type& expiry_time) | ||||
| : basic_io_object<TimerService>(io_context) | |||||
| : impl_(ex) | |||||
| { | { | ||||
| asio::error_code ec; | 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"); | 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. | /// Cancel any asynchronous operations that are waiting on the timer. | ||||
| /** | /** | ||||
| * This function forces the completion of any pending asynchronous wait | * This function forces the completion of any pending asynchronous wait | ||||
| @@ -213,7 +336,7 @@ public: | |||||
| std::size_t cancel() | std::size_t cancel() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "cancel"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -242,7 +365,7 @@ public: | |||||
| */ | */ | ||||
| std::size_t cancel(asio::error_code& ec) | 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. | /// Cancels one asynchronous operation that is waiting on the timer. | ||||
| @@ -272,8 +395,8 @@ public: | |||||
| std::size_t cancel_one() | std::size_t cancel_one() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "cancel_one"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -304,7 +427,7 @@ public: | |||||
| */ | */ | ||||
| std::size_t cancel_one(asio::error_code& ec) | 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. | /// Get the timer's expiry time as an absolute time. | ||||
| @@ -314,7 +437,7 @@ public: | |||||
| */ | */ | ||||
| time_type expires_at() const | 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. | /// 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) | std::size_t expires_at(const time_type& expiry_time) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "expires_at"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -373,8 +496,8 @@ public: | |||||
| std::size_t expires_at(const time_type& expiry_time, | std::size_t expires_at(const time_type& expiry_time, | ||||
| asio::error_code& ec) | 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. | /// Get the timer's expiry time relative to now. | ||||
| @@ -384,7 +507,7 @@ public: | |||||
| */ | */ | ||||
| duration_type expires_from_now() const | 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. | /// 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) | std::size_t expires_from_now(const duration_type& expiry_time) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "expires_from_now"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -443,8 +566,8 @@ public: | |||||
| std::size_t expires_from_now(const duration_type& expiry_time, | std::size_t expires_from_now(const duration_type& expiry_time, | ||||
| asio::error_code& ec) | 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. | /// Perform a blocking wait on the timer. | ||||
| @@ -457,7 +580,7 @@ public: | |||||
| void wait() | void wait() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "wait"); | ||||
| } | } | ||||
| @@ -470,7 +593,7 @@ public: | |||||
| */ | */ | ||||
| void wait(asio::error_code& ec) | 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. | /// Start an asynchronous wait on the timer. | ||||
| @@ -493,22 +616,44 @@ public: | |||||
| * const asio::error_code& error // Result of operation. | * const asio::error_code& error // Result of operation. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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> | template <typename WaitHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(WaitHandler, | ASIO_INITFN_RESULT_TYPE(WaitHandler, | ||||
| void (asio::error_code)) | void (asio::error_code)) | ||||
| async_wait(ASIO_MOVE_ARG(WaitHandler) handler) | 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 | } // namespace asio | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_io_object.hpp | // 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 | // 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) | // 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; | typedef typename service_type::implementation_type implementation_type; | ||||
| template <typename T, typename U> | 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: | public: | ||||
| static const bool value = | static const bool value = | ||||
| sizeof(service_has_move::eval( | |||||
| sizeof(asio_service_has_move_eval( | |||||
| static_cast<service_type*>(0), | static_cast<service_type*>(0), | ||||
| static_cast<implementation_type*>(0))) == 1; | static_cast<implementation_type*>(0))) == 1; | ||||
| }; | }; | ||||
| @@ -137,6 +138,11 @@ protected: | |||||
| * @note Available only for services that support movability, | * @note Available only for services that support movability, | ||||
| */ | */ | ||||
| basic_io_object& operator=(basic_io_object&& other); | 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) | #endif // defined(GENERATING_DOCUMENTATION) | ||||
| /// Protected destructor to prevent deletion through this type. | /// Protected destructor to prevent deletion through this type. | ||||
| @@ -225,6 +231,16 @@ protected: | |||||
| service_->move_construct(implementation_, other.implementation_); | 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() | ~basic_io_object() | ||||
| { | { | ||||
| service_->destroy(implementation_); | service_->destroy(implementation_); | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_raw_socket.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -19,15 +19,24 @@ | |||||
| #include <cstddef> | #include <cstddef> | ||||
| #include "asio/basic_socket.hpp" | #include "asio/basic_socket.hpp" | ||||
| #include "asio/detail/handler_type_requirements.hpp" | #include "asio/detail/handler_type_requirements.hpp" | ||||
| #include "asio/detail/non_const_lvalue.hpp" | |||||
| #include "asio/detail/throw_error.hpp" | #include "asio/detail/throw_error.hpp" | ||||
| #include "asio/detail/type_traits.hpp" | #include "asio/detail/type_traits.hpp" | ||||
| #include "asio/error.hpp" | #include "asio/error.hpp" | ||||
| #include "asio/raw_socket_service.hpp" | |||||
| #include "asio/detail/push_options.hpp" | #include "asio/detail/push_options.hpp" | ||||
| namespace asio { | 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. | /// Provides raw-oriented socket functionality. | ||||
| /** | /** | ||||
| * The basic_raw_socket class template provides asynchronous and blocking | * The basic_raw_socket class template provides asynchronous and blocking | ||||
| @@ -37,14 +46,29 @@ namespace asio { | |||||
| * @e Distinct @e objects: Safe.@n | * @e Distinct @e objects: Safe.@n | ||||
| * @e Shared @e objects: Unsafe. | * @e Shared @e objects: Unsafe. | ||||
| */ | */ | ||||
| template <typename Protocol, | |||||
| typename RawSocketService = raw_socket_service<Protocol> > | |||||
| template <typename Protocol, typename Executor> | |||||
| class basic_raw_socket | class basic_raw_socket | ||||
| : public basic_socket<Protocol, RawSocketService> | |||||
| : public basic_socket<Protocol, Executor> | |||||
| { | { | ||||
| public: | 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. | /// 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. | /// The protocol type. | ||||
| typedef Protocol protocol_type; | typedef Protocol protocol_type; | ||||
| @@ -57,12 +81,29 @@ public: | |||||
| * This constructor creates a raw socket without opening it. The open() | * 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. | * 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. | * 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. | * @param protocol An object specifying protocol parameters to be used. | ||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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 | * to the specified endpoint on the local machine. The protocol used is the | ||||
| * protocol associated with the given endpoint. | * 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 | * @param endpoint An endpoint on the local machine to which the raw | ||||
| * socket will be bound. | * socket will be bound. | ||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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 | * This constructor creates a raw socket object to hold an existing | ||||
| * native socket. | * 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. | * @param protocol An object specifying protocol parameters to be used. | ||||
| * | * | ||||
| @@ -121,10 +203,34 @@ public: | |||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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) | 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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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; | 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. | * 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 | * @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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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; | return *this; | ||||
| } | } | ||||
| #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | #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. | /// Send some data on a connected socket. | ||||
| /** | /** | ||||
| * This function is used to send data on the raw socket. The function call | * 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) | std::size_t send(const ConstBufferSequence& buffers) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -255,8 +374,8 @@ public: | |||||
| socket_base::message_flags flags) | socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -281,8 +400,8 @@ public: | |||||
| std::size_t send(const ConstBufferSequence& buffers, | std::size_t send(const ConstBufferSequence& buffers, | ||||
| socket_base::message_flags flags, asio::error_code& ec) | 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. | /// Start an asynchronous send on a connected socket. | ||||
| @@ -303,9 +422,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Use the async_send_to function to send data on an unconnected raw | ||||
| @@ -326,12 +445,10 @@ public: | |||||
| async_send(const ConstBufferSequence& buffers, | async_send(const ConstBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Start an asynchronous send on a connected socket. | ||||
| @@ -354,9 +471,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Use the async_send_to function to send data on an unconnected raw | ||||
| @@ -369,12 +486,9 @@ public: | |||||
| socket_base::message_flags flags, | socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Send raw data to the specified endpoint. | ||||
| @@ -407,8 +521,8 @@ public: | |||||
| const endpoint_type& destination) | const endpoint_type& destination) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send_to"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -434,8 +548,8 @@ public: | |||||
| const endpoint_type& destination, socket_base::message_flags flags) | const endpoint_type& destination, socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send_to"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -461,7 +575,7 @@ public: | |||||
| const endpoint_type& destination, socket_base::message_flags flags, | const endpoint_type& destination, socket_base::message_flags flags, | ||||
| asio::error_code& ec) | 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); | buffers, destination, flags, ec); | ||||
| } | } | ||||
| @@ -486,9 +600,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * To send a single data buffer use the @ref buffer function as follows: | * To send a single data buffer use the @ref buffer function as follows: | ||||
| @@ -509,12 +623,10 @@ public: | |||||
| const endpoint_type& destination, | const endpoint_type& destination, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Start an asynchronous send. | ||||
| @@ -540,9 +652,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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> | template <typename ConstBufferSequence, typename WriteHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(WriteHandler, | ASIO_INITFN_RESULT_TYPE(WriteHandler, | ||||
| @@ -551,13 +663,9 @@ public: | |||||
| const endpoint_type& destination, socket_base::message_flags flags, | const endpoint_type& destination, socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Receive some data on a connected socket. | ||||
| @@ -588,8 +696,8 @@ public: | |||||
| std::size_t receive(const MutableBufferSequence& buffers) | std::size_t receive(const MutableBufferSequence& buffers) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -617,8 +725,8 @@ public: | |||||
| socket_base::message_flags flags) | socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -645,8 +753,8 @@ public: | |||||
| std::size_t receive(const MutableBufferSequence& buffers, | std::size_t receive(const MutableBufferSequence& buffers, | ||||
| socket_base::message_flags flags, asio::error_code& ec) | 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. | /// Start an asynchronous receive on a connected socket. | ||||
| @@ -667,9 +775,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Use the async_receive_from function to receive data on an unconnected | ||||
| @@ -691,12 +799,10 @@ public: | |||||
| async_receive(const MutableBufferSequence& buffers, | async_receive(const MutableBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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. | /// Start an asynchronous receive on a connected socket. | ||||
| @@ -719,9 +825,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Use the async_receive_from function to receive data on an unconnected | ||||
| @@ -734,12 +840,9 @@ public: | |||||
| socket_base::message_flags flags, | socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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. | /// Receive raw data with the endpoint of the sender. | ||||
| @@ -773,8 +876,8 @@ public: | |||||
| endpoint_type& sender_endpoint) | endpoint_type& sender_endpoint) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive_from"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -800,8 +903,8 @@ public: | |||||
| endpoint_type& sender_endpoint, socket_base::message_flags flags) | endpoint_type& sender_endpoint, socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive_from"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -827,8 +930,8 @@ public: | |||||
| endpoint_type& sender_endpoint, socket_base::message_flags flags, | endpoint_type& sender_endpoint, socket_base::message_flags flags, | ||||
| asio::error_code& ec) | 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. | /// Start an asynchronous receive. | ||||
| @@ -854,9 +957,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * To receive into a single data buffer use the @ref buffer function as | * To receive into a single data buffer use the @ref buffer function as | ||||
| @@ -874,13 +977,10 @@ public: | |||||
| endpoint_type& sender_endpoint, | endpoint_type& sender_endpoint, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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. | /// Start an asynchronous receive. | ||||
| @@ -908,9 +1008,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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> | template <typename MutableBufferSequence, typename ReadHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(ReadHandler, | ASIO_INITFN_RESULT_TYPE(ReadHandler, | ||||
| @@ -919,14 +1019,85 @@ public: | |||||
| endpoint_type& sender_endpoint, socket_base::message_flags flags, | endpoint_type& sender_endpoint, socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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 | } // namespace asio | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_seq_packet_socket.hpp | // 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 | // 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) | // 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/handler_type_requirements.hpp" | ||||
| #include "asio/detail/throw_error.hpp" | #include "asio/detail/throw_error.hpp" | ||||
| #include "asio/error.hpp" | #include "asio/error.hpp" | ||||
| #include "asio/seq_packet_socket_service.hpp" | |||||
| #include "asio/detail/push_options.hpp" | #include "asio/detail/push_options.hpp" | ||||
| namespace asio { | 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. | /// Provides sequenced packet socket functionality. | ||||
| /** | /** | ||||
| * The basic_seq_packet_socket class template provides asynchronous and blocking | * The basic_seq_packet_socket class template provides asynchronous and blocking | ||||
| @@ -36,15 +44,29 @@ namespace asio { | |||||
| * @e Distinct @e objects: Safe.@n | * @e Distinct @e objects: Safe.@n | ||||
| * @e Shared @e objects: Unsafe. | * @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 | class basic_seq_packet_socket | ||||
| : public basic_socket<Protocol, SeqPacketSocketService> | |||||
| : public basic_socket<Protocol, Executor> | |||||
| { | { | ||||
| public: | 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. | /// 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. | /// The protocol type. | ||||
| typedef Protocol protocol_type; | typedef Protocol protocol_type; | ||||
| @@ -58,12 +80,30 @@ public: | |||||
| * socket needs to be opened and then connected or accepted before data can | * socket needs to be opened and then connected or accepted before data can | ||||
| * be sent or received on it. | * 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 | * needs to be connected or accepted before data can be sent or received on | ||||
| * it. | * 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. | * @param protocol An object specifying protocol parameters to be used. | ||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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 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 | * it bound to the specified endpoint on the local machine. The protocol used | ||||
| * is the protocol associated with the given endpoint. | * 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 | * @param endpoint An endpoint on the local machine to which the sequenced | ||||
| * packet socket will be bound. | * packet socket will be bound. | ||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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) | 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 | * This constructor creates a sequenced packet socket object to hold an | ||||
| * existing native socket. | * 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. | * @param protocol An object specifying protocol parameters to be used. | ||||
| * | * | ||||
| @@ -124,10 +211,34 @@ public: | |||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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) | 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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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; | return *this; | ||||
| } | } | ||||
| @@ -177,15 +288,16 @@ public: | |||||
| * will occur. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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; | return *this; | ||||
| } | } | ||||
| #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | #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. | /// Send some data on the socket. | ||||
| /** | /** | ||||
| * This function is used to send data on the sequenced packet socket. The | * This function is used to send data on the sequenced packet socket. The | ||||
| @@ -241,8 +363,8 @@ public: | |||||
| socket_base::message_flags flags) | socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -269,8 +391,8 @@ public: | |||||
| std::size_t send(const ConstBufferSequence& buffers, | std::size_t send(const ConstBufferSequence& buffers, | ||||
| socket_base::message_flags flags, asio::error_code& ec) | 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. | /// Start an asynchronous send. | ||||
| @@ -293,9 +415,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * To send a single data buffer use the @ref buffer function as follows: | * To send a single data buffer use the @ref buffer function as follows: | ||||
| @@ -313,12 +435,9 @@ public: | |||||
| socket_base::message_flags flags, | socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Receive some data on the socket. | ||||
| @@ -355,8 +474,8 @@ public: | |||||
| socket_base::message_flags& out_flags) | socket_base::message_flags& out_flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -402,8 +521,8 @@ public: | |||||
| socket_base::message_flags& out_flags) | socket_base::message_flags& out_flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -436,8 +555,8 @@ public: | |||||
| socket_base::message_flags in_flags, | socket_base::message_flags in_flags, | ||||
| socket_base::message_flags& out_flags, asio::error_code& ec) | 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. | /// Start an asynchronous receive. | ||||
| @@ -464,9 +583,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * To receive into a single data buffer use the @ref buffer function as | * To receive into a single data buffer use the @ref buffer function as | ||||
| @@ -485,13 +604,10 @@ public: | |||||
| socket_base::message_flags& out_flags, | socket_base::message_flags& out_flags, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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. | /// Start an asynchronous receive. | ||||
| @@ -520,9 +636,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * To receive into a single data buffer use the @ref buffer function as | * To receive into a single data buffer use the @ref buffer function as | ||||
| @@ -544,14 +660,49 @@ public: | |||||
| socket_base::message_flags& out_flags, | socket_base::message_flags& out_flags, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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 | } // namespace asio | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_serial_port.hpp | // 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) | // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) | ||||
| // | // | ||||
| // Distributed under the Boost Software License, Version 1.0. (See accompanying | // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||||
| @@ -22,12 +22,25 @@ | |||||
| || defined(GENERATING_DOCUMENTATION) | || defined(GENERATING_DOCUMENTATION) | ||||
| #include <string> | #include <string> | ||||
| #include "asio/basic_io_object.hpp" | |||||
| #include "asio/async_result.hpp" | |||||
| #include "asio/detail/handler_type_requirements.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/throw_error.hpp" | ||||
| #include "asio/detail/type_traits.hpp" | |||||
| #include "asio/error.hpp" | #include "asio/error.hpp" | ||||
| #include "asio/execution_context.hpp" | |||||
| #include "asio/executor.hpp" | |||||
| #include "asio/serial_port_base.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" | #include "asio/detail/push_options.hpp" | ||||
| @@ -35,35 +48,84 @@ namespace asio { | |||||
| /// Provides serial port functionality. | /// 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 | * @par Thread Safety | ||||
| * @e Distinct @e objects: Safe.@n | * @e Distinct @e objects: Safe.@n | ||||
| * @e Shared @e objects: Unsafe. | * @e Shared @e objects: Unsafe. | ||||
| */ | */ | ||||
| template <typename SerialPortService = serial_port_service> | |||||
| template <typename Executor = executor> | |||||
| class basic_serial_port | class basic_serial_port | ||||
| : public basic_io_object<SerialPortService>, | |||||
| public serial_port_base | |||||
| : public serial_port_base | |||||
| { | { | ||||
| public: | public: | ||||
| /// The type of the executor associated with the object. | |||||
| typedef Executor executor_type; | |||||
| /// The native representation of a serial port. | /// 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. | /// Construct a basic_serial_port without opening it. | ||||
| /** | /** | ||||
| * This constructor creates a 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. | /// Construct and open a basic_serial_port. | ||||
| @@ -71,18 +133,22 @@ public: | |||||
| * This constructor creates and opens a serial port for the specified device | * This constructor creates and opens a serial port for the specified device | ||||
| * name. | * 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 | * @param device The platform-specific device name for this serial | ||||
| * port. | * 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; | 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"); | asio::detail::throw_error(ec, "open"); | ||||
| } | } | ||||
| @@ -91,18 +157,42 @@ public: | |||||
| * This constructor creates and opens a serial port for the specified device | * This constructor creates and opens a serial port for the specified device | ||||
| * name. | * 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 | * @param device The platform-specific device name for this serial | ||||
| * port. | * 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; | 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"); | asio::detail::throw_error(ec, "open"); | ||||
| } | } | ||||
| @@ -111,19 +201,47 @@ public: | |||||
| * This constructor creates a serial port object to hold an existing native | * This constructor creates a serial port object to hold an existing native | ||||
| * serial port. | * 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. | * @param native_serial_port A native serial port. | ||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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) | const native_handle_type& native_serial_port) | ||||
| : basic_io_object<SerialPortService>(io_context) | |||||
| : impl_(ex) | |||||
| { | { | ||||
| asio::error_code ec; | 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); | native_serial_port, ec); | ||||
| asio::detail::throw_error(ec, "assign"); | asio::detail::throw_error(ec, "assign"); | ||||
| } | } | ||||
| @@ -137,11 +255,11 @@ public: | |||||
| * occur. | * occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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. | * occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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; | return *this; | ||||
| } | } | ||||
| #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | #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. | /// Get a reference to the lowest layer. | ||||
| /** | /** | ||||
| * This function returns a reference to the lowest layer in a stack of | * This function returns a reference to the lowest layer in a stack of | ||||
| @@ -202,7 +336,7 @@ public: | |||||
| void open(const std::string& device) | void open(const std::string& device) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "open"); | ||||
| } | } | ||||
| @@ -215,10 +349,11 @@ public: | |||||
| * | * | ||||
| * @param ec Set the indicate what error occurred, if any. | * @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) | 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. | /// Assign an existing native serial port to the serial port. | ||||
| @@ -232,7 +367,7 @@ public: | |||||
| void assign(const native_handle_type& native_serial_port) | void assign(const native_handle_type& native_serial_port) | ||||
| { | { | ||||
| asio::error_code ec; | asio::error_code ec; | ||||
| this->get_service().assign(this->get_implementation(), | |||||
| impl_.get_service().assign(impl_.get_implementation(), | |||||
| native_serial_port, ec); | native_serial_port, ec); | ||||
| asio::detail::throw_error(ec, "assign"); | asio::detail::throw_error(ec, "assign"); | ||||
| } | } | ||||
| @@ -245,17 +380,18 @@ public: | |||||
| * | * | ||||
| * @param ec Set to indicate what error occurred, if any. | * @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) | asio::error_code& ec) | ||||
| { | { | ||||
| return this->get_service().assign(this->get_implementation(), | |||||
| impl_.get_service().assign(impl_.get_implementation(), | |||||
| native_serial_port, ec); | native_serial_port, ec); | ||||
| ASIO_SYNC_OP_VOID_RETURN(ec); | |||||
| } | } | ||||
| /// Determine whether the serial port is open. | /// Determine whether the serial port is open. | ||||
| bool is_open() const | 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. | /// Close the serial port. | ||||
| @@ -269,7 +405,7 @@ public: | |||||
| void close() | void close() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "close"); | ||||
| } | } | ||||
| @@ -281,9 +417,10 @@ public: | |||||
| * | * | ||||
| * @param ec Set to indicate what error occurred, if any. | * @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. | /// Get the native serial port representation. | ||||
| @@ -294,7 +431,7 @@ public: | |||||
| */ | */ | ||||
| native_handle_type native_handle() | 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. | /// Cancel all asynchronous operations associated with the serial port. | ||||
| @@ -308,7 +445,7 @@ public: | |||||
| void cancel() | void cancel() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "cancel"); | ||||
| } | } | ||||
| @@ -320,9 +457,10 @@ public: | |||||
| * | * | ||||
| * @param ec Set to indicate what error occurred, if any. | * @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. | /// Send a break sequence to the serial port. | ||||
| @@ -335,7 +473,7 @@ public: | |||||
| void send_break() | void send_break() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send_break"); | ||||
| } | } | ||||
| @@ -346,9 +484,10 @@ public: | |||||
| * | * | ||||
| * @param ec Set to indicate what error occurred, if any. | * @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. | /// Set an option on the serial port. | ||||
| @@ -370,7 +509,7 @@ public: | |||||
| void set_option(const SettableSerialPortOption& option) | void set_option(const SettableSerialPortOption& option) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "set_option"); | ||||
| } | } | ||||
| @@ -390,11 +529,11 @@ public: | |||||
| * asio::serial_port_base::character_size | * asio::serial_port_base::character_size | ||||
| */ | */ | ||||
| template <typename SettableSerialPortOption> | template <typename SettableSerialPortOption> | ||||
| asio::error_code set_option(const SettableSerialPortOption& option, | |||||
| ASIO_SYNC_OP_VOID set_option(const SettableSerialPortOption& option, | |||||
| asio::error_code& ec) | 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. | /// Get an option from the serial port. | ||||
| @@ -417,7 +556,7 @@ public: | |||||
| void get_option(GettableSerialPortOption& option) | void get_option(GettableSerialPortOption& option) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "get_option"); | ||||
| } | } | ||||
| @@ -438,11 +577,11 @@ public: | |||||
| * asio::serial_port_base::character_size | * asio::serial_port_base::character_size | ||||
| */ | */ | ||||
| template <typename GettableSerialPortOption> | template <typename GettableSerialPortOption> | ||||
| asio::error_code get_option(GettableSerialPortOption& option, | |||||
| ASIO_SYNC_OP_VOID get_option(GettableSerialPortOption& option, | |||||
| asio::error_code& ec) | 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. | /// Write some data to the serial port. | ||||
| @@ -466,7 +605,7 @@ public: | |||||
| * @par Example | * @par Example | ||||
| * To write a single data buffer use the @ref buffer function as follows: | * To write a single data buffer use the @ref buffer function as follows: | ||||
| * @code | * @code | ||||
| * serial_port.write_some(asio::buffer(data, size)); | |||||
| * basic_serial_port.write_some(asio::buffer(data, size)); | |||||
| * @endcode | * @endcode | ||||
| * See the @ref buffer documentation for information on writing multiple | * See the @ref buffer documentation for information on writing multiple | ||||
| * buffers in one go, and how to use it with arrays, boost::array or | * 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) | std::size_t write_some(const ConstBufferSequence& buffers) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "write_some"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -502,8 +641,8 @@ public: | |||||
| std::size_t write_some(const ConstBufferSequence& buffers, | std::size_t write_some(const ConstBufferSequence& buffers, | ||||
| asio::error_code& ec) | 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. | /// Start an asynchronous write. | ||||
| @@ -524,9 +663,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes written. | * std::size_t bytes_transferred // Number of bytes written. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Consider using the @ref async_write function if you need to ensure that all | ||||
| @@ -535,7 +674,8 @@ public: | |||||
| * @par Example | * @par Example | ||||
| * To write a single data buffer use the @ref buffer function as follows: | * To write a single data buffer use the @ref buffer function as follows: | ||||
| * @code | * @code | ||||
| * serial_port.async_write_some(asio::buffer(data, size), handler); | |||||
| * basic_serial_port.async_write_some( | |||||
| * asio::buffer(data, size), handler); | |||||
| * @endcode | * @endcode | ||||
| * See the @ref buffer documentation for information on writing multiple | * See the @ref buffer documentation for information on writing multiple | ||||
| * buffers in one go, and how to use it with arrays, boost::array or | * 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, | async_write_some(const ConstBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Read some data from the serial port. | ||||
| @@ -577,7 +714,7 @@ public: | |||||
| * @par Example | * @par Example | ||||
| * To read into a single data buffer use the @ref buffer function as follows: | * To read into a single data buffer use the @ref buffer function as follows: | ||||
| * @code | * @code | ||||
| * serial_port.read_some(asio::buffer(data, size)); | |||||
| * basic_serial_port.read_some(asio::buffer(data, size)); | |||||
| * @endcode | * @endcode | ||||
| * See the @ref buffer documentation for information on reading into multiple | * 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 | * 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) | std::size_t read_some(const MutableBufferSequence& buffers) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "read_some"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -614,8 +751,8 @@ public: | |||||
| std::size_t read_some(const MutableBufferSequence& buffers, | std::size_t read_some(const MutableBufferSequence& buffers, | ||||
| asio::error_code& ec) | 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. | /// Start an asynchronous read. | ||||
| @@ -636,9 +773,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes read. | * std::size_t bytes_transferred // Number of bytes read. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Consider using the @ref async_read function if you need to ensure that the | ||||
| @@ -648,7 +785,8 @@ public: | |||||
| * @par Example | * @par Example | ||||
| * To read into a single data buffer use the @ref buffer function as follows: | * To read into a single data buffer use the @ref buffer function as follows: | ||||
| * @code | * @code | ||||
| * serial_port.async_read_some(asio::buffer(data, size), handler); | |||||
| * basic_serial_port.async_read_some( | |||||
| * asio::buffer(data, size), handler); | |||||
| * @endcode | * @endcode | ||||
| * See the @ref buffer documentation for information on reading into multiple | * 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 | * 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, | async_read_some(const MutableBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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 | } // namespace asio | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_signal_set.hpp | // 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 | // 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) | // 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/detail/config.hpp" | ||||
| #include "asio/basic_io_object.hpp" | |||||
| #include "asio/async_result.hpp" | |||||
| #include "asio/detail/handler_type_requirements.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/throw_error.hpp" | ||||
| #include "asio/detail/type_traits.hpp" | |||||
| #include "asio/error.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 { | namespace asio { | ||||
| /// Provides signal functionality. | /// 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 | * @par Thread Safety | ||||
| * @e Distinct @e objects: Safe.@n | * @e Distinct @e objects: Safe.@n | ||||
| @@ -54,7 +55,7 @@ namespace asio { | |||||
| * ... | * ... | ||||
| * | * | ||||
| * // Construct a signal set registered for process termination. | * // 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. | * // Start an asynchronous wait for one of the signals to occur. | ||||
| * signals.async_wait(handler); | * signals.async_wait(handler); | ||||
| @@ -89,41 +90,88 @@ namespace asio { | |||||
| * that any signals registered using signal_set objects are unblocked in at | * that any signals registered using signal_set objects are unblocked in at | ||||
| * least one thread. | * least one thread. | ||||
| */ | */ | ||||
| template <typename SignalSetService = signal_set_service> | |||||
| template <typename Executor = executor> | |||||
| class basic_signal_set | class basic_signal_set | ||||
| : public basic_io_object<SignalSetService> | |||||
| { | { | ||||
| public: | 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. | /// Construct a signal set without adding any signals. | ||||
| /** | /** | ||||
| * This constructor creates a signal set without registering for 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. | /// Construct a signal set and add one signal. | ||||
| /** | /** | ||||
| * This constructor creates a signal set and registers for 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. | * @param signal_number_1 The signal number to be added. | ||||
| * | * | ||||
| * @note This constructor is equivalent to performing: | * @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 | * 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; | 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"); | asio::detail::throw_error(ec, "add"); | ||||
| } | } | ||||
| @@ -131,26 +179,59 @@ public: | |||||
| /** | /** | ||||
| * This constructor creates a signal set and registers for two signals. | * 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_1 The first signal number to be added. | ||||
| * | * | ||||
| * @param signal_number_2 The second signal number to be added. | * @param signal_number_2 The second signal number to be added. | ||||
| * | * | ||||
| * @note This constructor is equivalent to performing: | * @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_1); | ||||
| * signals.add(signal_number_2); @endcode | * 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) | int signal_number_2) | ||||
| : basic_io_object<SignalSetService>(io_context) | |||||
| : impl_(ex) | |||||
| { | { | ||||
| asio::error_code ec; | 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"); | 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"); | asio::detail::throw_error(ec, "add"); | ||||
| } | } | ||||
| @@ -158,8 +239,9 @@ public: | |||||
| /** | /** | ||||
| * This constructor creates a signal set and registers for three signals. | * 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. | * @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. | * @param signal_number_3 The third signal number to be added. | ||||
| * | * | ||||
| * @note This constructor is equivalent to performing: | * @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_1); | ||||
| * signals.add(signal_number_2); | * signals.add(signal_number_2); | ||||
| * signals.add(signal_number_3); @endcode | * 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) | int signal_number_2, int signal_number_3) | ||||
| : basic_io_object<SignalSetService>(io_context) | |||||
| : impl_(ex) | |||||
| { | { | ||||
| asio::error_code ec; | 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"); | 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"); | 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"); | 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. | /// Add a signal to a signal_set. | ||||
| /** | /** | ||||
| * This function adds the specified signal to the set. It has no effect if the | * 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) | void add(int signal_number) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "add"); | ||||
| } | } | ||||
| @@ -211,11 +346,11 @@ public: | |||||
| * | * | ||||
| * @param ec Set to indicate what error occurred, if any. | * @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) | 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. | /// Remove a signal from a signal_set. | ||||
| @@ -233,7 +368,7 @@ public: | |||||
| void remove(int signal_number) | void remove(int signal_number) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "remove"); | ||||
| } | } | ||||
| @@ -249,11 +384,11 @@ public: | |||||
| * @note Removes any notifications that have been queued for the specified | * @note Removes any notifications that have been queued for the specified | ||||
| * signal number. | * signal number. | ||||
| */ | */ | ||||
| asio::error_code remove(int signal_number, | |||||
| ASIO_SYNC_OP_VOID remove(int signal_number, | |||||
| asio::error_code& ec) | 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. | /// Remove all signals from a signal_set. | ||||
| @@ -268,7 +403,7 @@ public: | |||||
| void clear() | void clear() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "clear"); | ||||
| } | } | ||||
| @@ -281,9 +416,10 @@ public: | |||||
| * | * | ||||
| * @note Removes all queued notifications. | * @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. | /// Cancel all operations associated with the signal set. | ||||
| @@ -310,7 +446,7 @@ public: | |||||
| void cancel() | void cancel() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "cancel"); | ||||
| } | } | ||||
| @@ -335,9 +471,10 @@ public: | |||||
| * These handlers can no longer be cancelled, and therefore are passed an | * These handlers can no longer be cancelled, and therefore are passed an | ||||
| * error code that indicates the successful completion of the wait operation. | * 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. | /// Start an asynchronous operation to wait for a signal to be delivered. | ||||
| @@ -361,26 +498,44 @@ public: | |||||
| * int signal_number // Indicates which signal occurred. | * int signal_number // Indicates which signal occurred. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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> | template <typename SignalHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(SignalHandler, | ASIO_INITFN_RESULT_TYPE(SignalHandler, | ||||
| void (asio::error_code, int)) | void (asio::error_code, int)) | ||||
| async_wait(ASIO_MOVE_ARG(SignalHandler) handler) | 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 | } // namespace asio | ||||
| #include "asio/detail/pop_options.hpp" | |||||
| #endif // ASIO_BASIC_SIGNAL_SET_HPP | #endif // ASIO_BASIC_SIGNAL_SET_HPP | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_socket_iostream.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -22,7 +22,6 @@ | |||||
| #include <istream> | #include <istream> | ||||
| #include <ostream> | #include <ostream> | ||||
| #include "asio/basic_socket_streambuf.hpp" | #include "asio/basic_socket_streambuf.hpp" | ||||
| #include "asio/stream_socket_service.hpp" | |||||
| #if !defined(ASIO_HAS_VARIADIC_TEMPLATES) | #if !defined(ASIO_HAS_VARIADIC_TEMPLATES) | ||||
| @@ -33,8 +32,7 @@ | |||||
| // explicit basic_socket_iostream(T1 x1, ..., Tn xn) | // explicit basic_socket_iostream(T1 x1, ..., Tn xn) | ||||
| // : std::basic_iostream<char>( | // : std::basic_iostream<char>( | ||||
| // &this->detail::socket_iostream_base< | // &this->detail::socket_iostream_base< | ||||
| // Protocol, StreamSocketService, Time, | |||||
| // TimeTraits, TimerService>::streambuf_) | |||||
| // Protocol, Clock, WaitTraits>::streambuf_) | |||||
| // { | // { | ||||
| // if (rdbuf()->connect(x1, ..., xn) == 0) | // if (rdbuf()->connect(x1, ..., xn) == 0) | ||||
| // this->setstate(std::ios_base::failbit); | // this->setstate(std::ios_base::failbit); | ||||
| @@ -46,8 +44,7 @@ | |||||
| explicit basic_socket_iostream(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ | explicit basic_socket_iostream(ASIO_VARIADIC_BYVAL_PARAMS(n)) \ | ||||
| : std::basic_iostream<char>( \ | : std::basic_iostream<char>( \ | ||||
| &this->detail::socket_iostream_base< \ | &this->detail::socket_iostream_base< \ | ||||
| Protocol, StreamSocketService, Time, \ | |||||
| TimeTraits, TimerService>::streambuf_) \ | |||||
| Protocol, Clock, WaitTraits>::streambuf_) \ | |||||
| { \ | { \ | ||||
| this->setf(std::ios_base::unitbuf); \ | this->setf(std::ios_base::unitbuf); \ | ||||
| if (rdbuf()->connect(ASIO_VARIADIC_BYVAL_ARGS(n)) == 0) \ | 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 | // A separate base class is used to ensure that the streambuf is initialised | ||||
| // prior to the basic_socket_iostream's basic_iostream base class. | // 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 | class socket_iostream_base | ||||
| { | { | ||||
| protected: | 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, | template <typename Protocol, | ||||
| typename StreamSocketService = stream_socket_service<Protocol>, | |||||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) \ | #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 | 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> | public std::basic_iostream<char> | ||||
| { | { | ||||
| private: | private: | ||||
| // These typedefs are intended keep this class's implementation independent | // 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: | public: | ||||
| /// The protocol type. | |||||
| typedef Protocol protocol_type; | |||||
| /// The endpoint type. | /// The endpoint type. | ||||
| typedef typename Protocol::endpoint endpoint_type; | typedef typename Protocol::endpoint endpoint_type; | ||||
| /// The clock type. | |||||
| typedef Clock clock_type; | |||||
| #if defined(GENERATING_DOCUMENTATION) | #if defined(GENERATING_DOCUMENTATION) | ||||
| /// (Deprecated: Use time_point.) The time type. | /// (Deprecated: Use time_point.) The time type. | ||||
| typedef typename TimeTraits::time_type time_type; | |||||
| typedef typename WaitTraits::time_type time_type; | |||||
| /// The 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. | /// (Deprecated: Use duration.) The duration type. | ||||
| typedef typename TimeTraits::duration_type duration_type; | |||||
| typedef typename WaitTraits::duration_type duration_type; | |||||
| /// The duration type. | /// The duration type. | ||||
| typedef typename TimeTraits::duration duration; | |||||
| typedef typename WaitTraits::duration duration; | |||||
| #else | #else | ||||
| # if !defined(ASIO_NO_DEPRECATED) | # if !defined(ASIO_NO_DEPRECATED) | ||||
| typedef typename traits_helper::time_type time_type; | typedef typename traits_helper::time_type time_type; | ||||
| @@ -149,12 +188,47 @@ public: | |||||
| basic_socket_iostream() | basic_socket_iostream() | ||||
| : std::basic_iostream<char>( | : std::basic_iostream<char>( | ||||
| &this->detail::socket_iostream_base< | &this->detail::socket_iostream_base< | ||||
| Protocol, StreamSocketService, Time, | |||||
| TimeTraits, TimerService>::streambuf_) | |||||
| Protocol, Clock, WaitTraits>::streambuf_) | |||||
| { | { | ||||
| this->setf(std::ios_base::unitbuf); | 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) | #if defined(GENERATING_DOCUMENTATION) | ||||
| /// Establish a connection to an endpoint corresponding to a resolver query. | /// Establish a connection to an endpoint corresponding to a resolver query. | ||||
| /** | /** | ||||
| @@ -169,8 +243,7 @@ public: | |||||
| explicit basic_socket_iostream(T... x) | explicit basic_socket_iostream(T... x) | ||||
| : std::basic_iostream<char>( | : std::basic_iostream<char>( | ||||
| &this->detail::socket_iostream_base< | &this->detail::socket_iostream_base< | ||||
| Protocol, StreamSocketService, Time, | |||||
| TimeTraits, TimerService>::streambuf_) | |||||
| Protocol, Clock, WaitTraits>::streambuf_) | |||||
| { | { | ||||
| this->setf(std::ios_base::unitbuf); | this->setf(std::ios_base::unitbuf); | ||||
| if (rdbuf()->connect(x...) == 0) | if (rdbuf()->connect(x...) == 0) | ||||
| @@ -208,14 +281,17 @@ public: | |||||
| } | } | ||||
| /// Return a pointer to the underlying streambuf. | /// 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< | &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. | /// Get the last error associated with the stream. | ||||
| @@ -232,7 +308,7 @@ public: | |||||
| */ | */ | ||||
| const asio::error_code& error() const | const asio::error_code& error() const | ||||
| { | { | ||||
| return rdbuf()->puberror(); | |||||
| return rdbuf()->error(); | |||||
| } | } | ||||
| #if !defined(ASIO_NO_DEPRECATED) | #if !defined(ASIO_NO_DEPRECATED) | ||||
| @@ -309,6 +385,12 @@ public: | |||||
| rdbuf()->expires_from_now(expiry_time); | rdbuf()->expires_from_now(expiry_time); | ||||
| } | } | ||||
| #endif // !defined(ASIO_NO_DEPRECATED) | #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 | } // namespace asio | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_socket_streambuf.hpp | // 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 | // 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) | // 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) | #if !defined(ASIO_NO_IOSTREAM) | ||||
| #include <streambuf> | #include <streambuf> | ||||
| #include <vector> | |||||
| #include "asio/basic_socket.hpp" | #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/detail/throw_error.hpp" | ||||
| #include "asio/io_context.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" | # 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) | #if !defined(ASIO_HAS_VARIADIC_TEMPLATES) | ||||
| @@ -39,14 +43,11 @@ | |||||
| // A macro that should expand to: | // A macro that should expand to: | ||||
| // template <typename T1, ..., typename Tn> | // 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(); | // init_buffers(); | ||||
| // this->basic_socket<Protocol, StreamSocketService>::close(ec_); | |||||
| // typedef typename Protocol::resolver resolver_type; | // typedef typename Protocol::resolver resolver_type; | ||||
| // resolver_type resolver(detail::socket_streambuf_base::io_context_); | |||||
| // resolver_type resolver(socket().get_executor()); | |||||
| // connect_to_endpoints( | // connect_to_endpoints( | ||||
| // resolver.resolve(x1, ..., xn, ec_)); | // resolver.resolve(x1, ..., xn, ec_)); | ||||
| // return !ec_ ? this : 0; | // return !ec_ ? this : 0; | ||||
| @@ -55,14 +56,11 @@ | |||||
| # define ASIO_PRIVATE_CONNECT_DEF(n) \ | # define ASIO_PRIVATE_CONNECT_DEF(n) \ | ||||
| template <ASIO_VARIADIC_TPARAMS(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(); \ | init_buffers(); \ | ||||
| this->basic_socket<Protocol, StreamSocketService>::close(ec_); \ | |||||
| typedef typename Protocol::resolver resolver_type; \ | typedef typename Protocol::resolver resolver_type; \ | ||||
| resolver_type resolver(detail::socket_streambuf_base::io_context_); \ | |||||
| resolver_type resolver(socket().get_executor()); \ | |||||
| connect_to_endpoints( \ | connect_to_endpoints( \ | ||||
| resolver.resolve(ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \ | resolver.resolve(ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \ | ||||
| return !ec_ ? this : 0; \ | return !ec_ ? this : 0; \ | ||||
| @@ -76,59 +74,111 @@ | |||||
| namespace asio { | namespace asio { | ||||
| namespace detail { | 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: | 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 | } // 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, | template <typename Protocol, | ||||
| typename StreamSocketService = stream_socket_service<Protocol>, | |||||
| #if defined(ASIO_HAS_BOOST_DATE_TIME) \ | #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 | class basic_socket_streambuf | ||||
| : public std::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: | private: | ||||
| // These typedefs are intended keep this class's implementation independent | // 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: | public: | ||||
| /// The protocol type. | |||||
| typedef Protocol protocol_type; | |||||
| /// The endpoint type. | /// The endpoint type. | ||||
| typedef typename Protocol::endpoint endpoint_type; | typedef typename Protocol::endpoint endpoint_type; | ||||
| /// The clock type. | |||||
| typedef Clock clock_type; | |||||
| #if defined(GENERATING_DOCUMENTATION) | #if defined(GENERATING_DOCUMENTATION) | ||||
| /// (Deprecated: Use time_point.) The time type. | /// (Deprecated: Use time_point.) The time type. | ||||
| typedef typename TimeTraits::time_type time_type; | |||||
| typedef typename WaitTraits::time_type time_type; | |||||
| /// The 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. | /// (Deprecated: Use duration.) The duration type. | ||||
| typedef typename TimeTraits::duration_type duration_type; | |||||
| typedef typename WaitTraits::duration_type duration_type; | |||||
| /// The duration type. | /// The duration type. | ||||
| typedef typename TimeTraits::duration duration; | |||||
| typedef typename WaitTraits::duration duration; | |||||
| #else | #else | ||||
| # if !defined(ASIO_NO_DEPRECATED) | # if !defined(ASIO_NO_DEPRECATED) | ||||
| typedef typename traits_helper::time_type time_type; | typedef typename traits_helper::time_type time_type; | ||||
| @@ -140,22 +190,64 @@ public: | |||||
| /// Construct a basic_socket_streambuf without establishing a connection. | /// Construct a basic_socket_streambuf without establishing a connection. | ||||
| basic_socket_streambuf() | 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(); | 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. | /// Destructor flushes buffered data. | ||||
| virtual ~basic_socket_streambuf() | virtual ~basic_socket_streambuf() | ||||
| { | { | ||||
| if (pptr() != pbase()) | if (pptr() != pbase()) | ||||
| overflow(traits_type::eof()); | overflow(traits_type::eof()); | ||||
| destroy_timer(); | |||||
| } | } | ||||
| /// Establish a connection. | /// Establish a connection. | ||||
| @@ -165,29 +257,11 @@ public: | |||||
| * @return \c this if a connection was successfully established, a null | * @return \c this if a connection was successfully established, a null | ||||
| * pointer otherwise. | * 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(); | 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; | return !ec_ ? this : 0; | ||||
| } | } | ||||
| @@ -202,17 +276,14 @@ public: | |||||
| * pointer otherwise. | * pointer otherwise. | ||||
| */ | */ | ||||
| template <typename T1, ..., typename TN> | 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) | #elif defined(ASIO_HAS_VARIADIC_TEMPLATES) | ||||
| template <typename... T> | template <typename... T> | ||||
| basic_socket_streambuf<Protocol, StreamSocketService, | |||||
| Time, TimeTraits, TimerService>* connect(T... x) | |||||
| basic_socket_streambuf* connect(T... x) | |||||
| { | { | ||||
| init_buffers(); | init_buffers(); | ||||
| this->basic_socket<Protocol, StreamSocketService>::close(ec_); | |||||
| typedef typename Protocol::resolver resolver_type; | 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_)); | connect_to_endpoints(resolver.resolve(x..., ec_)); | ||||
| return !ec_ ? this : 0; | return !ec_ ? this : 0; | ||||
| } | } | ||||
| @@ -225,17 +296,34 @@ public: | |||||
| * @return \c this if a connection was successfully established, a null | * @return \c this if a connection was successfully established, a null | ||||
| * pointer otherwise. | * pointer otherwise. | ||||
| */ | */ | ||||
| basic_socket_streambuf<Protocol, StreamSocketService, | |||||
| Time, TimeTraits, TimerService>* close() | |||||
| basic_socket_streambuf* close() | |||||
| { | { | ||||
| sync(); | sync(); | ||||
| this->basic_socket<Protocol, StreamSocketService>::close(ec_); | |||||
| socket().close(ec_); | |||||
| if (!ec_) | if (!ec_) | ||||
| init_buffers(); | init_buffers(); | ||||
| return !ec_ ? this : 0; | 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. | /// 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 | * @return An \c error_code corresponding to the last error from the stream | ||||
| * buffer. | * buffer. | ||||
| @@ -245,7 +333,6 @@ public: | |||||
| return error(); | return error(); | ||||
| } | } | ||||
| #if !defined(ASIO_NO_DEPRECATED) | |||||
| /// (Deprecated: Use expiry().) Get the stream buffer's expiry time as an | /// (Deprecated: Use expiry().) Get the stream buffer's expiry time as an | ||||
| /// absolute time. | /// absolute time. | ||||
| /** | /** | ||||
| @@ -254,9 +341,7 @@ public: | |||||
| */ | */ | ||||
| time_point expires_at() const | time_point expires_at() const | ||||
| { | { | ||||
| return timer_service_ | |||||
| ? timer_service_->expires_at(timer_implementation_) | |||||
| : time_point(); | |||||
| return expiry_time_; | |||||
| } | } | ||||
| #endif // !defined(ASIO_NO_DEPRECATED) | #endif // !defined(ASIO_NO_DEPRECATED) | ||||
| @@ -267,13 +352,7 @@ public: | |||||
| */ | */ | ||||
| time_point expiry() const | 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. | /// Set the stream buffer's expiry time as an absolute time. | ||||
| @@ -287,33 +366,7 @@ public: | |||||
| */ | */ | ||||
| void expires_at(const time_point& expiry_time) | 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. | /// Set the stream buffer's expiry time relative to now. | ||||
| @@ -327,17 +380,7 @@ public: | |||||
| */ | */ | ||||
| void expires_after(const duration& expiry_time) | 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) | #if !defined(ASIO_NO_DEPRECATED) | ||||
| @@ -363,108 +406,124 @@ public: | |||||
| */ | */ | ||||
| void expires_from_now(const duration& expiry_time) | 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) | #endif // !defined(ASIO_NO_DEPRECATED) | ||||
| protected: | protected: | ||||
| int_type underflow() | 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(); | 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(); | 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) | 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())) | 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()); | setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); | ||||
| // If the new character is eof then our work here is done. | // If the new character is eof then our work here is done. | ||||
| @@ -472,10 +531,12 @@ protected: | |||||
| return traits_type::not_eof(c); | return traits_type::not_eof(c); | ||||
| // Add the new character to the output buffer. | // Add the new character to the output buffer. | ||||
| *pptr() = traits_type::to_char_type(c); | |||||
| *pptr() = ch; | |||||
| pbump(1); | pbump(1); | ||||
| return c; | |||||
| } | } | ||||
| return c; | |||||
| #endif // defined(ASIO_WINDOWS_RUNTIME) | |||||
| } | } | ||||
| int sync() | int sync() | ||||
| @@ -487,150 +548,130 @@ protected: | |||||
| { | { | ||||
| if (pptr() == pbase() && s == 0 && n == 0) | if (pptr() == pbase() && s == 0 && n == 0) | ||||
| { | { | ||||
| unbuffered_ = true; | |||||
| put_buffer_.clear(); | |||||
| setp(0, 0); | setp(0, 0); | ||||
| sync(); | |||||
| return this; | return this; | ||||
| } | } | ||||
| return 0; | 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: | 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() | void init_buffers() | ||||
| { | { | ||||
| setg(&get_buffer_[0], | setg(&get_buffer_[0], | ||||
| &get_buffer_[0] + putback_max, | &get_buffer_[0] + putback_max, | ||||
| &get_buffer_[0] + putback_max); | &get_buffer_[0] + putback_max); | ||||
| if (unbuffered_) | |||||
| if (put_buffer_.empty()) | |||||
| setp(0, 0); | setp(0, 0); | ||||
| else | else | ||||
| setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size()); | 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 { 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_; | 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 | } // namespace asio | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_stream_socket.hpp | // 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 | // 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) | // 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/async_result.hpp" | ||||
| #include "asio/basic_socket.hpp" | #include "asio/basic_socket.hpp" | ||||
| #include "asio/detail/handler_type_requirements.hpp" | #include "asio/detail/handler_type_requirements.hpp" | ||||
| #include "asio/detail/non_const_lvalue.hpp" | |||||
| #include "asio/detail/throw_error.hpp" | #include "asio/detail/throw_error.hpp" | ||||
| #include "asio/error.hpp" | #include "asio/error.hpp" | ||||
| #include "asio/stream_socket_service.hpp" | |||||
| #include "asio/detail/push_options.hpp" | #include "asio/detail/push_options.hpp" | ||||
| namespace asio { | 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. | /// Provides stream-oriented socket functionality. | ||||
| /** | /** | ||||
| * The basic_stream_socket class template provides asynchronous and blocking | * The basic_stream_socket class template provides asynchronous and blocking | ||||
| @@ -40,14 +49,29 @@ namespace asio { | |||||
| * @par Concepts: | * @par Concepts: | ||||
| * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. | * AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. | ||||
| */ | */ | ||||
| template <typename Protocol, | |||||
| typename StreamSocketService = stream_socket_service<Protocol> > | |||||
| template <typename Protocol, typename Executor> | |||||
| class basic_stream_socket | class basic_stream_socket | ||||
| : public basic_socket<Protocol, StreamSocketService> | |||||
| : public basic_socket<Protocol, Executor> | |||||
| { | { | ||||
| public: | 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. | /// 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. | /// The protocol type. | ||||
| typedef Protocol 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 | * needs to be opened and then connected or accepted before data can be sent | ||||
| * or received on it. | * 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. | * 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 | * 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. | * 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. | * dispatch handlers for any asynchronous operations performed on the socket. | ||||
| * | * | ||||
| * @param protocol An object specifying protocol parameters to be used. | * @param protocol An object specifying protocol parameters to be used. | ||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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 | * to the specified endpoint on the local machine. The protocol used is the | ||||
| * protocol associated with the given endpoint. | * 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. | * dispatch handlers for any asynchronous operations performed on the socket. | ||||
| * | * | ||||
| * @param endpoint An endpoint on the local machine to which the stream | * @param endpoint An endpoint on the local machine to which the stream | ||||
| @@ -102,9 +166,33 @@ public: | |||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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 | * This constructor creates a stream socket object to hold an existing native | ||||
| * socket. | * 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. | * dispatch handlers for any asynchronous operations performed on the socket. | ||||
| * | * | ||||
| * @param protocol An object specifying protocol parameters to be used. | * @param protocol An object specifying protocol parameters to be used. | ||||
| @@ -122,10 +210,34 @@ public: | |||||
| * | * | ||||
| * @throws asio::system_error Thrown on failure. | * @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) | 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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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; | return *this; | ||||
| } | } | ||||
| @@ -172,15 +284,16 @@ public: | |||||
| * will occur. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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. | * will occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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; | return *this; | ||||
| } | } | ||||
| #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | #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. | /// Send some data on the socket. | ||||
| /** | /** | ||||
| * This function is used to send data on the stream socket. The function | * 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) | std::size_t send(const ConstBufferSequence& buffers) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -273,8 +396,8 @@ public: | |||||
| socket_base::message_flags flags) | socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "send"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -301,8 +424,8 @@ public: | |||||
| std::size_t send(const ConstBufferSequence& buffers, | std::size_t send(const ConstBufferSequence& buffers, | ||||
| socket_base::message_flags flags, asio::error_code& ec) | 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. | /// Start an asynchronous send. | ||||
| @@ -323,9 +446,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Consider using the @ref async_write function if you need to ensure that all | ||||
| @@ -346,13 +469,10 @@ public: | |||||
| async_send(const ConstBufferSequence& buffers, | async_send(const ConstBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Start an asynchronous send. | ||||
| @@ -375,9 +495,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes sent. | * std::size_t bytes_transferred // Number of bytes sent. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * Consider using the @ref async_write function if you need to ensure that all | ||||
| @@ -399,13 +519,9 @@ public: | |||||
| socket_base::message_flags flags, | socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Receive some data on the socket. | ||||
| @@ -440,8 +556,8 @@ public: | |||||
| std::size_t receive(const MutableBufferSequence& buffers) | std::size_t receive(const MutableBufferSequence& buffers) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -481,8 +597,8 @@ public: | |||||
| socket_base::message_flags flags) | socket_base::message_flags flags) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "receive"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -509,8 +625,8 @@ public: | |||||
| std::size_t receive(const MutableBufferSequence& buffers, | std::size_t receive(const MutableBufferSequence& buffers, | ||||
| socket_base::message_flags flags, asio::error_code& ec) | 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. | /// Start an asynchronous receive. | ||||
| @@ -531,9 +647,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @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 | * bytes. Consider using the @ref async_read function if you need to ensure | ||||
| @@ -556,12 +672,10 @@ public: | |||||
| async_receive(const MutableBufferSequence& buffers, | async_receive(const MutableBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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. | /// Start an asynchronous receive. | ||||
| @@ -584,9 +698,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes received. | * std::size_t bytes_transferred // Number of bytes received. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @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 | * bytes. Consider using the @ref async_read function if you need to ensure | ||||
| @@ -610,12 +724,9 @@ public: | |||||
| socket_base::message_flags flags, | socket_base::message_flags flags, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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. | /// Write some data to the socket. | ||||
| @@ -649,8 +760,8 @@ public: | |||||
| std::size_t write_some(const ConstBufferSequence& buffers) | std::size_t write_some(const ConstBufferSequence& buffers) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "write_some"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -675,7 +786,8 @@ public: | |||||
| std::size_t write_some(const ConstBufferSequence& buffers, | std::size_t write_some(const ConstBufferSequence& buffers, | ||||
| asio::error_code& ec) | 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. | /// Start an asynchronous write. | ||||
| @@ -696,9 +808,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes written. | * std::size_t bytes_transferred // Number of bytes written. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * 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, | async_write_some(const ConstBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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. | /// Read some data from the socket. | ||||
| @@ -759,8 +869,8 @@ public: | |||||
| std::size_t read_some(const MutableBufferSequence& buffers) | std::size_t read_some(const MutableBufferSequence& buffers) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "read_some"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -786,8 +896,8 @@ public: | |||||
| std::size_t read_some(const MutableBufferSequence& buffers, | std::size_t read_some(const MutableBufferSequence& buffers, | ||||
| asio::error_code& ec) | 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. | /// Start an asynchronous read. | ||||
| @@ -808,9 +918,9 @@ public: | |||||
| * std::size_t bytes_transferred // Number of bytes read. | * std::size_t bytes_transferred // Number of bytes read. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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. | * @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 | * 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, | async_read_some(const MutableBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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 | } // namespace asio | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_streambuf.hpp | // 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 | // 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) | // 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. | /// The type used to represent the output sequence as a list of buffers. | ||||
| typedef implementation_defined mutable_buffers_type; | typedef implementation_defined mutable_buffers_type; | ||||
| #else | #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 | #endif | ||||
| /// Construct a basic_streambuf object. | /// Construct a basic_streambuf object. | ||||
| @@ -232,8 +232,7 @@ public: | |||||
| */ | */ | ||||
| void commit(std::size_t n) | 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)); | pbump(static_cast<int>(n)); | ||||
| setg(eback(), gptr(), pptr()); | setg(eback(), gptr(), pptr()); | ||||
| } | } | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_streambuf_fwd.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -2,7 +2,7 @@ | |||||
| // basic_waitable_timer.hpp | // 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 | // 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) | // 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 "asio/detail/config.hpp" | ||||
| #include <cstddef> | #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/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/throw_error.hpp" | ||||
| #include "asio/error.hpp" | #include "asio/error.hpp" | ||||
| #include "asio/executor.hpp" | |||||
| #include "asio/wait_traits.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" | #include "asio/detail/push_options.hpp" | ||||
| namespace asio { | 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. | /// Provides waitable timer functionality. | ||||
| /** | /** | ||||
| * The basic_waitable_timer class template provides the ability to perform a | * The basic_waitable_timer class template provides the ability to perform a | ||||
| @@ -51,7 +69,7 @@ namespace asio { | |||||
| * Performing a blocking wait (C++11): | * Performing a blocking wait (C++11): | ||||
| * @code | * @code | ||||
| * // Construct a timer without setting an expiry time. | * // 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. | * // Set an expiry time relative to now. | ||||
| * timer.expires_after(std::chrono::seconds(5)); | * timer.expires_after(std::chrono::seconds(5)); | ||||
| @@ -74,7 +92,7 @@ namespace asio { | |||||
| * ... | * ... | ||||
| * | * | ||||
| * // Construct a timer with an absolute expiry time. | * // 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)); | * std::chrono::steady_clock::now() + std::chrono::seconds(60)); | ||||
| * | * | ||||
| * // Start an asynchronous wait. | * // Start an asynchronous wait. | ||||
| @@ -120,13 +138,13 @@ namespace asio { | |||||
| * @li If a wait handler is cancelled, the asio::error_code passed to | * @li If a wait handler is cancelled, the asio::error_code passed to | ||||
| * it contains the value asio::error::operation_aborted. | * 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 | class basic_waitable_timer | ||||
| : public basic_io_object<WaitableTimerService> | |||||
| { | { | ||||
| public: | public: | ||||
| /// The type of the executor associated with the object. | |||||
| typedef Executor executor_type; | |||||
| /// The clock type. | /// The clock type. | ||||
| typedef Clock clock_type; | typedef Clock clock_type; | ||||
| @@ -145,30 +163,72 @@ public: | |||||
| * expires_at() or expires_after() functions must be called to set an expiry | * expires_at() or expires_after() functions must be called to set an expiry | ||||
| * time before the timer can be waited on. | * 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. | /// Constructor to set a particular expiry time as an absolute time. | ||||
| /** | /** | ||||
| * This constructor creates a timer and sets the expiry 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 | * @param expiry_time The expiry time to be used for the timer, expressed | ||||
| * as an absolute time. | * 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; | 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"); | asio::detail::throw_error(ec, "expires_at"); | ||||
| } | } | ||||
| @@ -176,19 +236,43 @@ public: | |||||
| /** | /** | ||||
| * This constructor creates a timer and sets the expiry 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 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 | * @param expiry_time The expiry time to be used for the timer, relative to | ||||
| * now. | * 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; | 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"); | asio::detail::throw_error(ec, "expires_after"); | ||||
| } | } | ||||
| @@ -201,32 +285,48 @@ public: | |||||
| * occur. | * occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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. | /// 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 | * @param other The other basic_waitable_timer object from which the move will | ||||
| * occur. | * occur. | ||||
| * | * | ||||
| * @note Following the move, the moved-from object is in the same state as if | * @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_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; | return *this; | ||||
| } | } | ||||
| #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION) | #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. | /// Cancel any asynchronous operations that are waiting on the timer. | ||||
| /** | /** | ||||
| * This function forces the completion of any pending asynchronous wait | * This function forces the completion of any pending asynchronous wait | ||||
| @@ -252,12 +352,14 @@ public: | |||||
| std::size_t cancel() | std::size_t cancel() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "cancel"); | ||||
| return s; | 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 | * This function forces the completion of any pending asynchronous wait | ||||
| * operations against the timer. The handler for each cancelled operation will | * operations against the timer. The handler for each cancelled operation will | ||||
| @@ -281,8 +383,9 @@ public: | |||||
| */ | */ | ||||
| std::size_t cancel(asio::error_code& ec) | 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. | /// Cancels one asynchronous operation that is waiting on the timer. | ||||
| /** | /** | ||||
| @@ -311,13 +414,15 @@ public: | |||||
| std::size_t cancel_one() | std::size_t cancel_one() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "cancel_one"); | ||||
| return s; | 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 | * This function forces the completion of one pending asynchronous wait | ||||
| * operation against the timer. Handlers are cancelled in FIFO order. The | * 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) | 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 | /// (Deprecated: Use expiry().) Get the timer's expiry time as an absolute | ||||
| /// time. | /// time. | ||||
| /** | /** | ||||
| @@ -355,7 +459,7 @@ public: | |||||
| */ | */ | ||||
| time_point expires_at() const | 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) | #endif // !defined(ASIO_NO_DEPRECATED) | ||||
| @@ -366,7 +470,7 @@ public: | |||||
| */ | */ | ||||
| time_point expiry() const | 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. | /// 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) | std::size_t expires_at(const time_point& expiry_time) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "expires_at"); | ||||
| return s; | 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 | * This function sets the expiry time. Any pending asynchronous wait | ||||
| * operations will be cancelled. The handler for each cancelled operation will | * 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, | std::size_t expires_at(const time_point& expiry_time, | ||||
| asio::error_code& ec) | 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. | /// Set the timer's expiry time relative to now. | ||||
| /** | /** | ||||
| @@ -454,41 +561,12 @@ public: | |||||
| std::size_t expires_after(const duration& expiry_time) | std::size_t expires_after(const duration& expiry_time) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "expires_after"); | ||||
| return s; | 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) | #if !defined(ASIO_NO_DEPRECATED) | ||||
| /// (Deprecated: Use expiry().) Get the timer's expiry time relative to now. | /// (Deprecated: Use expiry().) Get the timer's expiry time relative to now. | ||||
| /** | /** | ||||
| @@ -497,7 +575,7 @@ public: | |||||
| */ | */ | ||||
| duration expires_from_now() const | 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 | /// (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) | std::size_t expires_from_now(const duration& expiry_time) | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "expires_from_now"); | ||||
| return s; | return s; | ||||
| } | } | ||||
| @@ -558,8 +636,8 @@ public: | |||||
| std::size_t expires_from_now(const duration& expiry_time, | std::size_t expires_from_now(const duration& expiry_time, | ||||
| asio::error_code& ec) | 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) | #endif // !defined(ASIO_NO_DEPRECATED) | ||||
| @@ -573,7 +651,7 @@ public: | |||||
| void wait() | void wait() | ||||
| { | { | ||||
| asio::error_code ec; | 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"); | asio::detail::throw_error(ec, "wait"); | ||||
| } | } | ||||
| @@ -586,7 +664,7 @@ public: | |||||
| */ | */ | ||||
| void wait(asio::error_code& ec) | 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. | /// Start an asynchronous wait on the timer. | ||||
| @@ -609,22 +687,46 @@ public: | |||||
| * const asio::error_code& error // Result of operation. | * const asio::error_code& error // Result of operation. | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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> | template <typename WaitHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(WaitHandler, | ASIO_INITFN_RESULT_TYPE(WaitHandler, | ||||
| void (asio::error_code)) | void (asio::error_code)) | ||||
| async_wait(ASIO_MOVE_ARG(WaitHandler) handler) | 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 | } // namespace asio | ||||
| @@ -2,7 +2,7 @@ | |||||
| // bind_executor.hpp | // 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 | // 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) | // 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/config.hpp" | ||||
| #include "asio/detail/type_traits.hpp" | #include "asio/detail/type_traits.hpp" | ||||
| #include "asio/detail/variadic_templates.hpp" | #include "asio/detail/variadic_templates.hpp" | ||||
| #include "asio/associated_executor.hpp" | |||||
| #include "asio/associated_allocator.hpp" | #include "asio/associated_allocator.hpp" | ||||
| #include "asio/async_result.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/uses_executor.hpp" | ||||
| #include "asio/detail/push_options.hpp" | #include "asio/detail/push_options.hpp" | ||||
| @@ -166,27 +168,27 @@ class executor_binder_base<T, Executor, true> | |||||
| protected: | protected: | ||||
| template <typename E, typename U> | template <typename E, typename U> | ||||
| executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) 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_; | T target_; | ||||
| }; | }; | ||||
| template <typename T, typename Executor> | template <typename T, typename Executor> | ||||
| class executor_binder_base<T, Executor, false> | class executor_binder_base<T, Executor, false> | ||||
| : protected Executor | |||||
| { | { | ||||
| protected: | protected: | ||||
| template <typename E, typename U> | template <typename E, typename U> | ||||
| executor_binder_base(ASIO_MOVE_ARG(E) e, ASIO_MOVE_ARG(U) 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)) | target_(ASIO_MOVE_CAST(U)(u)) | ||||
| { | { | ||||
| } | } | ||||
| Executor executor_; | |||||
| T target_; | T target_; | ||||
| }; | }; | ||||
| @@ -393,7 +395,7 @@ public: | |||||
| /// Obtain the associated executor. | /// Obtain the associated executor. | ||||
| executor_type get_executor() const ASIO_NOEXCEPT | executor_type get_executor() const ASIO_NOEXCEPT | ||||
| { | { | ||||
| return static_cast<const Executor&>(*this); | |||||
| return this->executor_; | |||||
| } | } | ||||
| #if defined(GENERATING_DOCUMENTATION) | #if defined(GENERATING_DOCUMENTATION) | ||||
| @@ -519,30 +521,30 @@ struct uses_executor<executor_binder<T, Executor>, Executor> | |||||
| : true_type {}; | : true_type {}; | ||||
| template <typename T, typename Executor, typename Signature> | 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< | 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) | explicit async_result(executor_binder<T, Executor>& b) | ||||
| : target_(b.get()) | : target_(b.get()) | ||||
| { | { | ||||
| } | } | ||||
| type get() | |||||
| return_type get() | |||||
| { | { | ||||
| return target_.get(); | return target_.get(); | ||||
| } | } | ||||
| private: | 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> | template <typename T, typename Executor, typename Allocator> | ||||
| @@ -2,7 +2,7 @@ | |||||
| // buffered_read_stream.hpp | // 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 | // 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) | // 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/noncopyable.hpp" | ||||
| #include "asio/detail/type_traits.hpp" | #include "asio/detail/type_traits.hpp" | ||||
| #include "asio/error.hpp" | #include "asio/error.hpp" | ||||
| #include "asio/io_context.hpp" | |||||
| #include "asio/detail/push_options.hpp" | #include "asio/detail/push_options.hpp" | ||||
| @@ -105,22 +104,6 @@ public: | |||||
| return next_layer_.lowest_layer().get_executor(); | 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. | /// Close the stream. | ||||
| void close() | void close() | ||||
| { | { | ||||
| @@ -128,9 +111,10 @@ public: | |||||
| } | } | ||||
| /// Close the stream. | /// 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. | /// Write the given data to the stream. Returns the number of bytes written. | ||||
| @@ -158,14 +142,8 @@ public: | |||||
| async_write_some(const ConstBufferSequence& buffers, | async_write_some(const ConstBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(WriteHandler) handler) | 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 | /// Fill the buffer with some data. Returns the number of bytes placed in the | ||||
| @@ -2,7 +2,7 @@ | |||||
| // buffered_read_stream_fwd.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -2,7 +2,7 @@ | |||||
| // buffered_stream.hpp | // 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 | // 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) | // 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/buffered_stream_fwd.hpp" | ||||
| #include "asio/detail/noncopyable.hpp" | #include "asio/detail/noncopyable.hpp" | ||||
| #include "asio/error.hpp" | #include "asio/error.hpp" | ||||
| #include "asio/io_context.hpp" | |||||
| #include "asio/detail/push_options.hpp" | #include "asio/detail/push_options.hpp" | ||||
| @@ -96,22 +95,6 @@ public: | |||||
| return stream_impl_.lowest_layer().get_executor(); | 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. | /// Close the stream. | ||||
| void close() | void close() | ||||
| { | { | ||||
| @@ -119,9 +102,10 @@ public: | |||||
| } | } | ||||
| /// Close the stream. | /// 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 | /// Flush all data from the buffer to the next layer. Returns the number of | ||||
| @@ -2,7 +2,7 @@ | |||||
| // buffered_stream_fwd.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -2,7 +2,7 @@ | |||||
| // buffered_write_stream.hpp | // 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 | // 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) | // 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/noncopyable.hpp" | ||||
| #include "asio/detail/type_traits.hpp" | #include "asio/detail/type_traits.hpp" | ||||
| #include "asio/error.hpp" | #include "asio/error.hpp" | ||||
| #include "asio/io_context.hpp" | |||||
| #include "asio/write.hpp" | #include "asio/write.hpp" | ||||
| #include "asio/detail/push_options.hpp" | #include "asio/detail/push_options.hpp" | ||||
| @@ -105,22 +104,6 @@ public: | |||||
| return next_layer_.lowest_layer().get_executor(); | 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. | /// Close the stream. | ||||
| void close() | void close() | ||||
| { | { | ||||
| @@ -128,9 +111,10 @@ public: | |||||
| } | } | ||||
| /// Close the stream. | /// 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 | /// 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, | async_read_some(const MutableBufferSequence& buffers, | ||||
| ASIO_MOVE_ARG(ReadHandler) handler) | 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. | /// Peek at the incoming data on the stream. Returns the number of bytes read. | ||||
| @@ -2,7 +2,7 @@ | |||||
| // buffered_write_stream_fwd.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -2,7 +2,7 @@ | |||||
| // buffers_iterator.hpp | // 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 | // 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) | // 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 buffers_iterator_types_helper<is_mutable> helper; | ||||
| typedef typename helper::buffer_type buffer_type; | typedef typename helper::buffer_type buffer_type; | ||||
| typedef typename helper::template byte_type<ByteType>::type byte_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. | /// A random access iterator over the bytes in a buffer sequence. | ||||
| @@ -76,6 +113,9 @@ private: | |||||
| typedef typename detail::buffers_iterator_types< | typedef typename detail::buffers_iterator_types< | ||||
| BufferSequence, ByteType>::buffer_type buffer_type; | BufferSequence, ByteType>::buffer_type buffer_type; | ||||
| typedef typename detail::buffers_iterator_types<BufferSequence, | |||||
| ByteType>::const_iterator buffer_sequence_iterator_type; | |||||
| public: | public: | ||||
| /// The type used for the distance between two iterators. | /// The type used for the distance between two iterators. | ||||
| typedef std::ptrdiff_t difference_type; | typedef std::ptrdiff_t difference_type; | ||||
| @@ -130,9 +170,9 @@ public: | |||||
| #endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) | #endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) | ||||
| { | { | ||||
| buffers_iterator new_iter; | 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_) | while (new_iter.current_ != new_iter.end_) | ||||
| { | { | ||||
| new_iter.current_buffer_ = *new_iter.current_; | new_iter.current_buffer_ = *new_iter.current_; | ||||
| @@ -150,9 +190,9 @@ public: | |||||
| #endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) | #endif // defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) | ||||
| { | { | ||||
| buffers_iterator new_iter; | 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_) | while (new_iter.current_ != new_iter.end_) | ||||
| { | { | ||||
| buffer_type buffer = *new_iter.current_; | buffer_type buffer = *new_iter.current_; | ||||
| @@ -347,7 +387,7 @@ private: | |||||
| } | } | ||||
| // Find the previous non-empty buffer. | // Find the previous non-empty buffer. | ||||
| typename BufferSequence::const_iterator iter = current_; | |||||
| buffer_sequence_iterator_type iter = current_; | |||||
| while (iter != begin_) | while (iter != begin_) | ||||
| { | { | ||||
| --iter; | --iter; | ||||
| @@ -426,7 +466,7 @@ private: | |||||
| } | } | ||||
| // Find the previous non-empty buffer. | // Find the previous non-empty buffer. | ||||
| typename BufferSequence::const_iterator iter = current_; | |||||
| buffer_sequence_iterator_type iter = current_; | |||||
| while (iter != begin_) | while (iter != begin_) | ||||
| { | { | ||||
| --iter; | --iter; | ||||
| @@ -452,9 +492,9 @@ private: | |||||
| buffer_type current_buffer_; | buffer_type current_buffer_; | ||||
| std::size_t current_buffer_position_; | 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_; | 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 | // 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 | // 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) | // 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 | // 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 | // 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) | // 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 | * @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. | * Otherwise, contains the error from the last connection attempt. | ||||
| * | * | ||||
| * @par Example | * @par Example | ||||
| * @code tcp::resolver r(io_context); | |||||
| * @code tcp::resolver r(my_context); | |||||
| * tcp::resolver::query q("host", "service"); | * tcp::resolver::query q("host", "service"); | ||||
| * tcp::socket s(io_context); | |||||
| * tcp::socket s(my_context); | |||||
| * asio::connect(s, r.resolve(q)); @endcode | * 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, | const EndpointSequence& endpoints, | ||||
| typename enable_if<is_endpoint_sequence< | typename enable_if<is_endpoint_sequence< | ||||
| EndpointSequence>::value>::type* = 0); | EndpointSequence>::value>::type* = 0); | ||||
| @@ -112,9 +113,9 @@ typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||||
| * default-constructed endpoint. | * default-constructed endpoint. | ||||
| * | * | ||||
| * @par Example | * @par Example | ||||
| * @code tcp::resolver r(io_context); | |||||
| * @code tcp::resolver r(my_context); | |||||
| * tcp::resolver::query q("host", "service"); | * tcp::resolver::query q("host", "service"); | ||||
| * tcp::socket s(io_context); | |||||
| * tcp::socket s(my_context); | |||||
| * asio::error_code ec; | * asio::error_code ec; | ||||
| * asio::connect(s, r.resolve(q), ec); | * asio::connect(s, r.resolve(q), ec); | ||||
| * if (ec) | * if (ec) | ||||
| @@ -122,15 +123,15 @@ typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||||
| * // An error occurred. | * // An error occurred. | ||||
| * } @endcode | * } @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, | const EndpointSequence& endpoints, asio::error_code& ec, | ||||
| typename enable_if<is_endpoint_sequence< | typename enable_if<is_endpoint_sequence< | ||||
| EndpointSequence>::value>::type* = 0); | EndpointSequence>::value>::type* = 0); | ||||
| #if !defined(ASIO_NO_DEPRECATED) | #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 | * 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 | * 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 represents the end of the sequence. This is a valid assumption for | ||||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | * 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); | 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 | * 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 | * 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 represents the end of the sequence. This is a valid assumption for | ||||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | * 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, | Iterator begin, asio::error_code& ec, | ||||
| typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | ||||
| #endif // !defined(ASIO_NO_DEPRECATED) | #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. | * Otherwise, contains the error from the last connection attempt. | ||||
| * | * | ||||
| * @par Example | * @par Example | ||||
| * @code tcp::resolver r(io_context); | |||||
| * @code tcp::resolver r(my_context); | |||||
| * tcp::resolver::query q("host", "service"); | * tcp::resolver::query q("host", "service"); | ||||
| * tcp::resolver::results_type e = r.resolve(q); | * 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 | * 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); | Iterator begin, Iterator end); | ||||
| /// Establishes a socket connection by trying each endpoint in a sequence. | /// 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. | * endpoint. Otherwise, the end iterator. | ||||
| * | * | ||||
| * @par Example | * @par Example | ||||
| * @code tcp::resolver r(io_context); | |||||
| * @code tcp::resolver r(my_context); | |||||
| * tcp::resolver::query q("host", "service"); | * tcp::resolver::query q("host", "service"); | ||||
| * tcp::resolver::results_type e = r.resolve(q); | * tcp::resolver::results_type e = r.resolve(q); | ||||
| * tcp::socket s(io_context); | |||||
| * tcp::socket s(my_context); | |||||
| * asio::error_code ec; | * asio::error_code ec; | ||||
| * asio::connect(s, e.begin(), e.end(), ec); | * asio::connect(s, e.begin(), e.end(), ec); | ||||
| * if (ec) | * if (ec) | ||||
| @@ -251,8 +252,8 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||||
| * // An error occurred. | * // An error occurred. | ||||
| * } @endcode | * } @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); | Iterator begin, Iterator end, asio::error_code& ec); | ||||
| /// Establishes a socket connection by trying each endpoint in a sequence. | /// Establishes a socket connection by trying each endpoint in a sequence. | ||||
| @@ -299,16 +300,16 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||||
| * } | * } | ||||
| * }; @endcode | * }; @endcode | ||||
| * It would be used with the asio::connect function as follows: | * 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::query q("host", "service"); | ||||
| * tcp::socket s(io_context); | |||||
| * tcp::socket s(my_context); | |||||
| * tcp::endpoint e = asio::connect(s, | * tcp::endpoint e = asio::connect(s, | ||||
| * r.resolve(q), my_connect_condition()); | * r.resolve(q), my_connect_condition()); | ||||
| * std::cout << "Connected to: " << e << std::endl; @endcode | * std::cout << "Connected to: " << e << std::endl; @endcode | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, | |||||
| template <typename Protocol, typename Executor, | |||||
| typename EndpointSequence, typename ConnectCondition> | 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, | const EndpointSequence& endpoints, ConnectCondition connect_condition, | ||||
| typename enable_if<is_endpoint_sequence< | typename enable_if<is_endpoint_sequence< | ||||
| EndpointSequence>::value>::type* = 0); | EndpointSequence>::value>::type* = 0); | ||||
| @@ -358,9 +359,9 @@ typename Protocol::endpoint connect(basic_socket<Protocol, SocketService>& s, | |||||
| * } | * } | ||||
| * }; @endcode | * }; @endcode | ||||
| * It would be used with the asio::connect function as follows: | * 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::query q("host", "service"); | ||||
| * tcp::socket s(io_context); | |||||
| * tcp::socket s(my_context); | |||||
| * asio::error_code ec; | * asio::error_code ec; | ||||
| * tcp::endpoint e = asio::connect(s, | * tcp::endpoint e = asio::connect(s, | ||||
| * r.resolve(q), my_connect_condition(), ec); | * 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; | * std::cout << "Connected to: " << e << std::endl; | ||||
| * } @endcode | * } @endcode | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, | |||||
| template <typename Protocol, typename Executor, | |||||
| typename EndpointSequence, typename ConnectCondition> | 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, | const EndpointSequence& endpoints, ConnectCondition connect_condition, | ||||
| asio::error_code& ec, | asio::error_code& ec, | ||||
| typename enable_if<is_endpoint_sequence< | typename enable_if<is_endpoint_sequence< | ||||
| EndpointSequence>::value>::type* = 0); | EndpointSequence>::value>::type* = 0); | ||||
| #if !defined(ASIO_NO_DEPRECATED) | #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 | * 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 | * 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 represents the end of the sequence. This is a valid assumption for | ||||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | * iterator types such as @c asio::ip::tcp::resolver::iterator. | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, | |||||
| template <typename Protocol, typename Executor, | |||||
| typename Iterator, typename ConnectCondition> | typename Iterator, typename ConnectCondition> | ||||
| Iterator connect(basic_socket<Protocol, SocketService>& s, | |||||
| Iterator connect(basic_socket<Protocol, Executor>& s, | |||||
| Iterator begin, ConnectCondition connect_condition, | Iterator begin, ConnectCondition connect_condition, | ||||
| typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | 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 | * 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 | * 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 represents the end of the sequence. This is a valid assumption for | ||||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | * iterator types such as @c asio::ip::tcp::resolver::iterator. | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, | |||||
| template <typename Protocol, typename Executor, | |||||
| typename Iterator, typename ConnectCondition> | 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, | ConnectCondition connect_condition, asio::error_code& ec, | ||||
| typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | ||||
| #endif // !defined(ASIO_NO_DEPRECATED) | #endif // !defined(ASIO_NO_DEPRECATED) | ||||
| @@ -511,17 +512,17 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, Iterator begin, | |||||
| * } | * } | ||||
| * }; @endcode | * }; @endcode | ||||
| * It would be used with the asio::connect function as follows: | * 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::query q("host", "service"); | ||||
| * tcp::resolver::results_type e = r.resolve(q); | * 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( | * tcp::resolver::results_type::iterator i = asio::connect( | ||||
| * s, e.begin(), e.end(), my_connect_condition()); | * s, e.begin(), e.end(), my_connect_condition()); | ||||
| * std::cout << "Connected to: " << i->endpoint() << std::endl; @endcode | * std::cout << "Connected to: " << i->endpoint() << std::endl; @endcode | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, | |||||
| template <typename Protocol, typename Executor, | |||||
| typename Iterator, typename ConnectCondition> | 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); | Iterator end, ConnectCondition connect_condition); | ||||
| /// Establishes a socket connection by trying each endpoint in a sequence. | /// 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 | * }; @endcode | ||||
| * It would be used with the asio::connect function as follows: | * 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::query q("host", "service"); | ||||
| * tcp::resolver::results_type e = r.resolve(q); | * tcp::resolver::results_type e = r.resolve(q); | ||||
| * tcp::socket s(io_context); | |||||
| * tcp::socket s(my_context); | |||||
| * asio::error_code ec; | * asio::error_code ec; | ||||
| * tcp::resolver::results_type::iterator i = asio::connect( | * tcp::resolver::results_type::iterator i = asio::connect( | ||||
| * s, e.begin(), e.end(), my_connect_condition()); | * 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; | * std::cout << "Connected to: " << i->endpoint() << std::endl; | ||||
| * } @endcode | * } @endcode | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, | |||||
| template <typename Protocol, typename Executor, | |||||
| typename Iterator, typename ConnectCondition> | 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, | Iterator begin, Iterator end, ConnectCondition connect_condition, | ||||
| asio::error_code& ec); | asio::error_code& ec); | ||||
| @@ -598,8 +599,8 @@ Iterator connect(basic_socket<Protocol, SocketService>& s, | |||||
| /** | /** | ||||
| * @defgroup async_connect asio::async_connect | * @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 | * const typename Protocol::endpoint& endpoint | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * @code tcp::resolver r(io_context); | |||||
| * @code tcp::resolver r(my_context); | |||||
| * tcp::resolver::query q("host", "service"); | * 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 | * } @endcode | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, | |||||
| template <typename Protocol, typename Executor, | |||||
| typename EndpointSequence, typename RangeConnectHandler> | typename EndpointSequence, typename RangeConnectHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, | ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, | ||||
| void (asio::error_code, typename Protocol::endpoint)) | void (asio::error_code, typename Protocol::endpoint)) | ||||
| async_connect(basic_socket<Protocol, SocketService>& s, | |||||
| async_connect(basic_socket<Protocol, Executor>& s, | |||||
| const EndpointSequence& endpoints, | const EndpointSequence& endpoints, | ||||
| ASIO_MOVE_ARG(RangeConnectHandler) handler, | ASIO_MOVE_ARG(RangeConnectHandler) handler, | ||||
| typename enable_if<is_endpoint_sequence< | typename enable_if<is_endpoint_sequence< | ||||
| EndpointSequence>::value>::type* = 0); | EndpointSequence>::value>::type* = 0); | ||||
| #if !defined(ASIO_NO_DEPRECATED) | #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 | * 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 | * 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 | * Iterator iterator | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @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 represents the end of the sequence. This is a valid assumption for | ||||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | * iterator types such as @c asio::ip::tcp::resolver::iterator. | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, | |||||
| template <typename Protocol, typename Executor, | |||||
| typename Iterator, typename IteratorConnectHandler> | typename Iterator, typename IteratorConnectHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | ||||
| void (asio::error_code, Iterator)) | 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); | typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | ||||
| #endif // !defined(ASIO_NO_DEPRECATED) | #endif // !defined(ASIO_NO_DEPRECATED) | ||||
| @@ -748,13 +749,13 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||||
| * Iterator iterator | * Iterator iterator | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * @code std::vector<tcp::endpoint> endpoints = ...; | * @code std::vector<tcp::endpoint> endpoints = ...; | ||||
| * tcp::socket s(io_context); | |||||
| * tcp::socket s(my_context); | |||||
| * asio::async_connect(s, | * asio::async_connect(s, | ||||
| * endpoints.begin(), endpoints.end(), | * endpoints.begin(), endpoints.end(), | ||||
| * connect_handler); | * connect_handler); | ||||
| @@ -768,12 +769,11 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||||
| * // ... | * // ... | ||||
| * } @endcode | * } @endcode | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, | |||||
| template <typename Protocol, typename Executor, | |||||
| typename Iterator, typename IteratorConnectHandler> | typename Iterator, typename IteratorConnectHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | ||||
| void (asio::error_code, Iterator)) | 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); | ASIO_MOVE_ARG(IteratorConnectHandler) handler); | ||||
| /// Asynchronously establishes a socket connection by trying each endpoint in a | /// Asynchronously establishes a socket connection by trying each endpoint in a | ||||
| @@ -814,9 +814,9 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||||
| * Iterator iterator | * Iterator iterator | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * The following connect condition function object can be used to output | * The following connect condition function object can be used to output | ||||
| @@ -833,9 +833,9 @@ async_connect(basic_socket<Protocol, SocketService>& s, | |||||
| * } | * } | ||||
| * }; @endcode | * }; @endcode | ||||
| * It would be used with the asio::connect function as follows: | * 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::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 | * } @endcode | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, typename EndpointSequence, | |||||
| template <typename Protocol, typename Executor, typename EndpointSequence, | |||||
| typename ConnectCondition, typename RangeConnectHandler> | typename ConnectCondition, typename RangeConnectHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, | ASIO_INITFN_RESULT_TYPE(RangeConnectHandler, | ||||
| void (asio::error_code, typename Protocol::endpoint)) | 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, | const EndpointSequence& endpoints, ConnectCondition connect_condition, | ||||
| ASIO_MOVE_ARG(RangeConnectHandler) handler, | ASIO_MOVE_ARG(RangeConnectHandler) handler, | ||||
| typename enable_if<is_endpoint_sequence< | typename enable_if<is_endpoint_sequence< | ||||
| EndpointSequence>::value>::type* = 0); | EndpointSequence>::value>::type* = 0); | ||||
| #if !defined(ASIO_NO_DEPRECATED) | #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 | * 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 | * 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 | * Iterator iterator | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @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 represents the end of the sequence. This is a valid assumption for | ||||
| * iterator types such as @c asio::ip::tcp::resolver::iterator. | * 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> | typename ConnectCondition, typename IteratorConnectHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | ||||
| void (asio::error_code, Iterator)) | 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, | ConnectCondition connect_condition, | ||||
| ASIO_MOVE_ARG(IteratorConnectHandler) handler, | ASIO_MOVE_ARG(IteratorConnectHandler) handler, | ||||
| typename enable_if<!is_endpoint_sequence<Iterator>::value>::type* = 0); | 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 | * Iterator iterator | ||||
| * ); @endcode | * ); @endcode | ||||
| * Regardless of whether the asynchronous operation completes immediately or | * 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 | * @par Example | ||||
| * The following connect condition function object can be used to output | * 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 | * }; @endcode | ||||
| * It would be used with the asio::connect function as follows: | * 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::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 | * } @endcode | ||||
| */ | */ | ||||
| template <typename Protocol, typename SocketService, typename Iterator, | |||||
| template <typename Protocol, typename Executor, typename Iterator, | |||||
| typename ConnectCondition, typename IteratorConnectHandler> | typename ConnectCondition, typename IteratorConnectHandler> | ||||
| ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler, | ||||
| void (asio::error_code, Iterator)) | 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); | ASIO_MOVE_ARG(IteratorConnectHandler) handler); | ||||
| /*@}*/ | /*@}*/ | ||||
| @@ -2,7 +2,7 @@ | |||||
| // coroutine.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -205,7 +205,7 @@ class coroutine_ref; | |||||
| * { | * { | ||||
| * do | * do | ||||
| * { | * { | ||||
| * socket_.reset(new tcp::socket(io_context_)); | |||||
| * socket_.reset(new tcp::socket(my_context_)); | |||||
| * yield acceptor->async_accept(*socket_, *this); | * yield acceptor->async_accept(*socket_, *this); | ||||
| * fork server(*this)(); | * fork server(*this)(); | ||||
| * } while (is_parent()); | * } 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 | * 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. | * 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 | * 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 | * @par Alternate macro names | ||||
| * | * | ||||
| @@ -289,7 +289,7 @@ private: | |||||
| bail_out_of_coroutine: \ | bail_out_of_coroutine: \ | ||||
| break; \ | break; \ | ||||
| } \ | } \ | ||||
| else case 0: | |||||
| else /* fall-through */ case 0: | |||||
| #define ASIO_CORO_YIELD_IMPL(n) \ | #define ASIO_CORO_YIELD_IMPL(n) \ | ||||
| for (_coro_value = (n);;) \ | for (_coro_value = (n);;) \ | ||||
| @@ -301,12 +301,12 @@ private: | |||||
| else \ | else \ | ||||
| switch (_coro_value ? 0 : 1) \ | switch (_coro_value ? 0 : 1) \ | ||||
| for (;;) \ | for (;;) \ | ||||
| case -1: if (_coro_value) \ | |||||
| /* fall-through */ case -1: if (_coro_value) \ | |||||
| goto terminate_coroutine; \ | goto terminate_coroutine; \ | ||||
| else for (;;) \ | else for (;;) \ | ||||
| case 1: if (_coro_value) \ | |||||
| /* fall-through */ case 1: if (_coro_value) \ | |||||
| goto bail_out_of_coroutine; \ | goto bail_out_of_coroutine; \ | ||||
| else case 0: | |||||
| else /* fall-through */ case 0: | |||||
| #define ASIO_CORO_FORK_IMPL(n) \ | #define ASIO_CORO_FORK_IMPL(n) \ | ||||
| for (_coro_value = -(n);; _coro_value = (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 | // 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 | // 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) | // 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 | // 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 | // 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) | // 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 | * executor. The function object is queued for execution, and is never called | ||||
| * from the current thread prior to returning from <tt>defer()</tt>. | * 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: | * This function has the following effects: | ||||
| * | * | ||||
| * @li Constructs a function object handler of type @c Handler, initialized | * @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 | * The function object is queued for execution, and is never called from the | ||||
| * current thread prior to returning from <tt>defer()</tt>. | * 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: | * This function has the following effects: | ||||
| * | * | ||||
| * @li Constructs a function object handler of type @c Handler, initialized | * @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 | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -2,7 +2,7 @@ | |||||
| // detail/array_fwd.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -2,7 +2,7 @@ | |||||
| // detail/assert.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -2,7 +2,7 @@ | |||||
| // detail/atomic_count.hpp | // 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 | // 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) | // 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 | // 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 | // 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) | // 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 | class base_from_completion_cond | ||||
| { | { | ||||
| protected: | 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 | // 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 | // 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) | // 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); | 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) | #if defined(ASIO_HAS_MOVE) | ||||
| template <typename Handler, typename Arg1> | template <typename Handler, typename Arg1> | ||||
| @@ -2,7 +2,7 @@ | |||||
| // detail/buffer_resize_guard.hpp | // 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 | // 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) | // 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 | // 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 | // 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) | // 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 | class buffer_sequence_adapter_base | ||||
| { | { | ||||
| protected: | |||||
| #if defined(ASIO_WINDOWS_RUNTIME) | #if defined(ASIO_WINDOWS_RUNTIME) | ||||
| public: | |||||
| // The maximum number of buffers to support in a single operation. | // The maximum number of buffers to support in a single operation. | ||||
| enum { max_buffers = 1 }; | enum { max_buffers = 1 }; | ||||
| protected: | |||||
| typedef Windows::Storage::Streams::IBuffer^ native_buffer_type; | typedef Windows::Storage::Streams::IBuffer^ native_buffer_type; | ||||
| ASIO_DECL static void init_native_buffer( | ASIO_DECL static void init_native_buffer( | ||||
| @@ -42,9 +43,11 @@ protected: | |||||
| native_buffer_type& buf, | native_buffer_type& buf, | ||||
| const asio::const_buffer& buffer); | const asio::const_buffer& buffer); | ||||
| #elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) | #elif defined(ASIO_WINDOWS) || defined(__CYGWIN__) | ||||
| public: | |||||
| // The maximum number of buffers to support in a single operation. | // The maximum number of buffers to support in a single operation. | ||||
| enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; | enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; | ||||
| protected: | |||||
| typedef WSABUF native_buffer_type; | typedef WSABUF native_buffer_type; | ||||
| static void init_native_buffer(WSABUF& buf, | static void init_native_buffer(WSABUF& buf, | ||||
| @@ -61,9 +64,11 @@ protected: | |||||
| buf.len = static_cast<ULONG>(buffer.size()); | buf.len = static_cast<ULONG>(buffer.size()); | ||||
| } | } | ||||
| #else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) | #else // defined(ASIO_WINDOWS) || defined(__CYGWIN__) | ||||
| public: | |||||
| // The maximum number of buffers to support in a single operation. | // The maximum number of buffers to support in a single operation. | ||||
| enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; | enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; | ||||
| protected: | |||||
| typedef iovec native_buffer_type; | typedef iovec native_buffer_type; | ||||
| static void init_iov_base(void*& base, void* addr) | static void init_iov_base(void*& base, void* addr) | ||||
| @@ -102,14 +107,9 @@ public: | |||||
| explicit buffer_sequence_adapter(const Buffers& buffer_sequence) | explicit buffer_sequence_adapter(const Buffers& buffer_sequence) | ||||
| : count_(0), total_buffer_size_(0) | : 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() | native_buffer_type* buffers() | ||||
| @@ -122,6 +122,11 @@ public: | |||||
| return count_; | return count_; | ||||
| } | } | ||||
| std::size_t total_size() const | |||||
| { | |||||
| return total_buffer_size_; | |||||
| } | |||||
| bool all_empty() const | bool all_empty() const | ||||
| { | { | ||||
| return total_buffer_size_ == 0; | return total_buffer_size_ == 0; | ||||
| @@ -129,8 +134,42 @@ public: | |||||
| static bool all_empty(const Buffers& buffer_sequence) | 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; | std::size_t i = 0; | ||||
| for (; iter != end && i < max_buffers; ++iter, ++i) | for (; iter != end && i < max_buffers; ++iter, ++i) | ||||
| if (Buffer(*iter).size() > 0) | if (Buffer(*iter).size() > 0) | ||||
| @@ -138,10 +177,10 @@ public: | |||||
| return true; | 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) | for (; iter != end; ++iter) | ||||
| { | { | ||||
| Buffer buffer(*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) | for (; iter != end; ++iter) | ||||
| { | { | ||||
| Buffer buffer(*iter); | Buffer buffer(*iter); | ||||
| @@ -162,12 +201,117 @@ public: | |||||
| return Buffer(); | return Buffer(); | ||||
| } | } | ||||
| private: | |||||
| native_buffer_type buffers_[max_buffers]; | native_buffer_type buffers_[max_buffers]; | ||||
| std::size_t count_; | std::size_t count_; | ||||
| std::size_t total_buffer_size_; | 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> | template <typename Buffer> | ||||
| class buffer_sequence_adapter<Buffer, asio::mutable_buffers_1> | class buffer_sequence_adapter<Buffer, asio::mutable_buffers_1> | ||||
| : buffer_sequence_adapter_base | : buffer_sequence_adapter_base | ||||
| @@ -190,6 +334,11 @@ public: | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| std::size_t total_size() const | |||||
| { | |||||
| return total_buffer_size_; | |||||
| } | |||||
| bool all_empty() const | bool all_empty() const | ||||
| { | { | ||||
| return total_buffer_size_ == 0; | return total_buffer_size_ == 0; | ||||
| @@ -237,6 +386,11 @@ public: | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| std::size_t total_size() const | |||||
| { | |||||
| return total_buffer_size_; | |||||
| } | |||||
| bool all_empty() const | bool all_empty() const | ||||
| { | { | ||||
| return total_buffer_size_ == 0; | return total_buffer_size_ == 0; | ||||
| @@ -262,6 +416,8 @@ private: | |||||
| std::size_t total_buffer_size_; | std::size_t total_buffer_size_; | ||||
| }; | }; | ||||
| #endif // !defined(ASIO_NO_DEPRECATED) | |||||
| template <typename Buffer, typename Elem> | template <typename Buffer, typename Elem> | ||||
| class buffer_sequence_adapter<Buffer, boost::array<Elem, 2> > | class buffer_sequence_adapter<Buffer, boost::array<Elem, 2> > | ||||
| : buffer_sequence_adapter_base | : buffer_sequence_adapter_base | ||||
| @@ -285,6 +441,11 @@ public: | |||||
| return 2; | return 2; | ||||
| } | } | ||||
| std::size_t total_size() const | |||||
| { | |||||
| return total_buffer_size_; | |||||
| } | |||||
| bool all_empty() const | bool all_empty() const | ||||
| { | { | ||||
| return total_buffer_size_ == 0; | return total_buffer_size_ == 0; | ||||
| @@ -337,6 +498,11 @@ public: | |||||
| return 2; | return 2; | ||||
| } | } | ||||
| std::size_t total_size() const | |||||
| { | |||||
| return total_buffer_size_; | |||||
| } | |||||
| bool all_empty() const | bool all_empty() const | ||||
| { | { | ||||
| return total_buffer_size_ == 0; | return total_buffer_size_ == 0; | ||||
| @@ -2,7 +2,7 @@ | |||||
| // detail/buffered_stream_storage.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -2,7 +2,7 @@ | |||||
| // detail/call_stack.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -2,7 +2,7 @@ | |||||
| // detail/chrono.hpp | // 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 | // 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) | // 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::hours; | ||||
| using std::chrono::time_point_cast; | using std::chrono::time_point_cast; | ||||
| #if defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) | #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) | #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; | using std::chrono::steady_clock; | ||||
| #endif // defined(ASIO_HAS_STD_CHRONO_MONOTONIC_CLOCK) | |||||
| using std::chrono::system_clock; | |||||
| using std::chrono::high_resolution_clock; | using std::chrono::high_resolution_clock; | ||||
| #elif defined(ASIO_HAS_BOOST_CHRONO) | #elif defined(ASIO_HAS_BOOST_CHRONO) | ||||
| using boost::chrono::duration; | using boost::chrono::duration; | ||||
| @@ -2,7 +2,7 @@ | |||||
| // detail/chrono_time_traits.hpp | // 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 | // 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) | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||||
| @@ -2,7 +2,7 @@ | |||||
| // detail/completion_handler.hpp | // 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 | // 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) | // 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 | |||||