MIRA
SyncTimedRead.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012 by
3  * MetraLabs GmbH (MLAB), GERMANY
4  * and
5  * Neuroinformatics and Cognitive Robotics Labs (NICR) at TU Ilmenau, GERMANY
6  * All rights reserved.
7  *
8  * Contact: info@mira-project.org
9  *
10  * Commercial Usage:
11  * Licensees holding valid commercial licenses may use this file in
12  * accordance with the commercial license agreement provided with the
13  * software or, alternatively, in accordance with the terms contained in
14  * a written agreement between you and MLAB or NICR.
15  *
16  * GNU General Public License Usage:
17  * Alternatively, this file may be used under the terms of the GNU
18  * General Public License version 3.0 as published by the Free Software
19  * Foundation and appearing in the file LICENSE.GPL3 included in the
20  * packaging of this file. Please review the following information to
21  * ensure the GNU General Public License version 3.0 requirements will be
22  * met: http://www.gnu.org/copyleft/gpl.html.
23  * Alternatively you may (at your option) use any later version of the GNU
24  * General Public License if such license has been publicly approved by
25  * MLAB and NICR (or its successors, if any).
26  *
27  * IN NO EVENT SHALL "MLAB" OR "NICR" BE LIABLE TO ANY PARTY FOR DIRECT,
28  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
29  * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF "MLAB" OR
30  * "NICR" HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * "MLAB" AND "NICR" SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
33  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
34  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
35  * ON AN "AS IS" BASIS, AND "MLAB" AND "NICR" HAVE NO OBLIGATION TO
36  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR MODIFICATIONS.
37  */
38 
47 #ifndef _MIRA_SYNCTIMEDREAD_H_
48 #define _MIRA_SYNCTIMEDREAD_H_
49 
50 #ifndef Q_MOC_RUN
51 #include <boost/asio/deadline_timer.hpp>
52 #include <boost/optional.hpp>
53 #if BOOST_VERSION >= 106600
54 # include <boost/asio/io_context.hpp>
55 # include <boost/asio/system_timer.hpp>
56 #endif
57 #endif
58 
59 #include <utils/Bind.h>
60 #include <utils/Time.h>
61 #include <error/Exceptions.h>
62 
63 namespace boost { namespace asio {
64 
66 
68 
69 namespace Private {
70 
71 inline void setTimerResult(optional<system::error_code>* codeStorage,
72  system::error_code code)
73 {
74  // If we got a boost::asio::error::operation_aborted, ignore it.
75  if (code == error::operation_aborted)
76  return;
77 
78  codeStorage->reset(code);
79 }
80 
81 inline void setResult(optional<system::error_code>* codeStorage,
82  std::size_t* bytesReceivedStorage,
83  system::error_code code, std::size_t bytesReceived)
84 {
85  // If we got a boost::asio::error::operation_aborted, ignore it.
86  if (code == error::operation_aborted)
87  return;
88 
89  codeStorage->reset(code);
90  *bytesReceivedStorage = bytesReceived;
91 }
92 
93 }
95 
97 
107 template<typename SyncReadStream, typename MutableBufferSequence>
108 std::size_t read_some(SyncReadStream& s, const MutableBufferSequence& buffers,
109  mira::Duration timeout)
110 {
111  system::error_code ec;
112  std::size_t bytesRead = read_some(s, buffers, timeout, ec);
113  detail::throw_error(ec);
114  return bytesRead;
115 }
116 
123 template<typename SyncReadStream, typename MutableBufferSequence>
124 std::size_t read_some(SyncReadStream& s, const MutableBufferSequence& buffers,
125  mira::Duration timeout, system::error_code& ec)
126 {
127  ec = boost::system::error_code();
128  optional<system::error_code> timerResult;
129  optional<system::error_code> readResult;
130  std::size_t bytesRead = 0;
131 
132 #if BOOST_VERSION >= 106600
133  boost::asio::io_context& s_io_service = static_cast<boost::asio::io_context&>(s.get_executor().context());
134  system_timer timer(s_io_service);
135  timer.expires_after(std::chrono::microseconds(timeout.total_microseconds()));
136 #else
137  auto& s_io_service = s.get_io_service();
138  deadline_timer timer(s_io_service);
139  timer.expires_from_now(timeout);
140 #endif
141 
142 #if BOOST_VERSION >= 107400
143  using namespace mira::placeholders;
144 #endif
145  timer.async_wait(bind(&Private::setTimerResult, &timerResult, _1));
146 
147  s.async_read_some(buffers, bind(&Private::setResult,
148  &readResult, &bytesRead, _1, _2));
149 
150 #if BOOST_VERSION < 106600
151  s_io_service.reset();
152 #else
153  s_io_service.restart();
154 #endif
155 
156  // For more details on the following code, please look here:
157  // http://stackoverflow.com/questions/10858719/cancel-async-read-due-to-timeout
158 
159  // It is important to use a loop here, to ensure, that all handlers are
160  // remove from the io_service, before leaving this method. Otherwise a
161  // subsequent call of read_some will return immediately caused by still
162  // outstanding handlers.
163  // The io_service::reset() method only allows the io_service to resume
164  // running from a stopped state, it does not remove any handlers already
165  // queued into the io_service.
166 
167  while (s_io_service.run_one())
168  {
169  if (readResult)
170  {
171  // Data received, so cancel the timer.
172  // This will result in a call of the handler Private::setTimerResult
173  // with boost::asio::error::operation_aborted as the error.
174  timer.cancel();
175  ec = readResult.get();
176  } else
177  if (timerResult)
178  {
179  // Timeout occurred, so cancel the read operation. This will result
180  // in a call of the handler Private::setResult with the error code
181  // boost::asio::error::operation_aborted.
182  s.cancel();
183  ec = system::error_code(system::errc::timed_out,
184  system::generic_category());
185  //return bytesRead;
186  }
187  }
188 
189  // Reset service, guaranteeing it is in a good state for subsequential runs.
190 #if BOOST_VERSION < 106600
191  s_io_service.reset();
192 #else
193  s_io_service.restart();
194 #endif
195 
196  return bytesRead;
197 }
198 
200 
201 }}
202 
203 #endif
Definition: SyncTimedRead.h:63
Time and Duration wrapper class.
std::size_t read_some(SyncReadStream &s, const MutableBufferSequence &buffers, mira::Duration timeout)
Read some data from the specified stream &#39;s&#39; into the given buffer &#39;buffers&#39;.
Definition: SyncTimedRead.h:108
Commonly used exception classes.
Use this class to represent time durations.
Definition: Time.h:106
Wrapper for boost/bind.