RESTinio
sendfile_operation_posix.ipp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  sendfile routine.
7 */
8 
9 #if defined( RESTINIO_FREEBSD_TARGET ) || defined( RESTINIO_MACOS_TARGET )
10  #include <sys/uio.h>
11 #else
12  #include <sys/sendfile.h>
13 #endif
14 
15 namespace restinio
16 {
17 
18 namespace impl
19 {
20 
21 //
22 // sendfile_operation_runner_t
23 //
24 
25 //! A runner of sendfile operation
26 template < typename Socket >
29 {
30  public:
32 
37 
38  // Reuse construstors from base.
39  using base_type_t::base_type_t;
40 
41  virtual void
42  start() override
43  {
44 #if defined( RESTINIO_FREEBSD_TARGET ) || defined( RESTINIO_MACOS_TARGET )
45  auto const n = ::lseek( this->m_file_descriptor, this->m_next_write_offset, SEEK_SET );
46 #else
47  auto const n = ::lseek64( this->m_file_descriptor, this->m_next_write_offset, SEEK_SET );
48 #endif
49 
50  if( static_cast< off_t >( -1 ) != n )
51  {
52  this->init_next_write();
53  }
54  else
55  {
58  return;
59  }
60  }
61 
62  void
64  {
65  while( true )
66  {
67  auto const n =
68  ::read(
69  this->m_file_descriptor,
70  this->m_buffer.get(),
71  std::min< file_size_t >( this->m_remained_size, this->m_chunk_size ) );
72 
73  if( -1 == n )
74  {
75  if( errno == EINTR )
76  continue;
77 
80  }
81  else if( 0 == n )
82  {
85  }
86  else
87  {
89  this->m_socket,
90  asio_ns::const_buffer{ this->m_buffer.get(), static_cast< std::size_t >( n ) },
92  this->m_executor,
93  [ this, ctx = this->shared_from_this() ]
94  ( const asio_ns::error_code & ec, std::size_t written ){
95 
96  if( !ec )
97  {
98  this->m_remained_size -= written;
99  this->m_transfered_size += written;
100  if( 0 == this->m_remained_size )
101  {
102  this->m_after_sendfile_cb( ec, this->m_transfered_size );
103  }
104  else
105  {
106  this->init_next_write();
107  }
108  }
109  else
110  {
111  this->m_after_sendfile_cb( ec, this->m_transfered_size );
112  }
113  }
114  ) );
115  }
116  break;
117  }
118  }
119 
120  private:
121  std::unique_ptr< char[] > m_buffer{ new char [ this->m_chunk_size ] };
122 };
123 
124 //! A specialization for plain tcp-socket using
125 //! linux sendfile() (http://man7.org/linux/man-pages/man2/sendfile.2.html).
126 template <>
129 {
130  public:
132 
137 
138  // Reuse construstors from base.
139  using base_type_t::base_type_t;
140 
141  virtual void
142  start() override
143  {
145  }
146 
147  void
149  {
150  {
151  asio_ns::error_code ec;
152 
153  if( !m_socket.native_non_blocking() )
154  {
155  m_socket.native_non_blocking( true, ec );
156  }
157 
158  if( ec )
159  {
160  m_after_sendfile_cb( ec, m_transfered_size );
161  return;
162  }
163  }
164 
165  while( true )
166  {
167  // Try the system call.
168  errno = 0;
169  auto wait_write_is_possible =
170  [&]{
171  // We have to wait for the socket to become ready again.
172  m_socket.async_wait(
173  asio_ns::ip::tcp::socket::wait_write,
174  asio_ns::bind_executor(
175  m_executor,
176  [ this, ctx = this->shared_from_this() ]
177  ( const asio_ns::error_code & ec ){
178  if( ec || 0 == m_remained_size )
179  {
180  m_after_sendfile_cb( ec, m_transfered_size );
181  }
182  else
183  {
184  init_next_write();
185  }
186  } ) );
187  };
188 
189  if( 0 == m_remained_size )
190  {
191  // We are done.
192  wait_write_is_possible();
193  break;
194  }
195 
196 #if defined( RESTINIO_FREEBSD_TARGET )
197  // FreeBSD sendfile signature:
198  // int sendfile(int fd, int s, off_t offset, size_t nbytes,
199  // struct sf_hdtr *hdtr, off_t *sbytes, int flags);
200  // https://www.freebsd.org/cgi/man.cgi?query=sendfile
201 
202  off_t n{ 0 };
203  auto rc =
204  ::sendfile(
205  m_file_descriptor,
206  m_socket.native_handle(),
207  m_next_write_offset,
208  static_cast< size_t >(
209  std::min< file_size_t >( m_remained_size, m_chunk_size ) ),
210  nullptr, // struct sf_hdtr *hdtr
211  &n, // sbytes
212  // Is 16 a reasonable constant here.
213 #if __FreeBSD__ >= 11
214  SF_FLAGS( 16, SF_NOCACHE )
215 #else
216  SF_MNOWAIT
217 #endif
218  );
219 
220  // Shift the number of bytes successfully sent.
221  m_next_write_offset += n;
222 
223  if( -1 == rc )
224  {
225  // It is still possible that some bytes had been sent.
226  m_remained_size -= static_cast< file_size_t >( n );
227  m_transfered_size += static_cast< file_size_t >( n );
228 
229  n = -1;
230  }
231 #elif defined( RESTINIO_MACOS_TARGET )
232  // macOS sendfile signature:
233  // in sendfile(int fd, int s, off_t offset,
234  // off_t *len, struct sf_hdtr *hdtr, int flags);
235 
236  off_t n =
237  static_cast< off_t >(
238  std::min< file_size_t >( m_remained_size, m_chunk_size ) );
239 
240  auto rc =
241  ::sendfile(
242  m_file_descriptor,
243  m_socket.native_handle(),
244  m_next_write_offset,
245  &n,
246  nullptr, // struct sf_hdtr *hdtr
247  0 );
248 
249  // Shift the number of bytes successfully sent.
250  m_next_write_offset += n;
251 
252  if( -1 == rc )
253  {
254  // It is still possible that some bytes had been sent.
255  m_remained_size -= static_cast< file_size_t >( n );
256  m_transfered_size += static_cast< file_size_t >( n );
257 
258  n = -1;
259  }
260 #else
261  auto n =
262  ::sendfile64(
263  m_socket.native_handle(),
264  m_file_descriptor,
265  &m_next_write_offset,
266  std::min< file_size_t >( m_remained_size, m_chunk_size ) );
267 #endif
268 
269  if( -1 == n )
270  {
271  if( errno == EAGAIN || errno == EINTR )
272  {
273  wait_write_is_possible();
274  }
275  else
276  {
277  const asio_ns::error_code ec{ errno, asio_ns::error::get_system_category() };
278  m_after_sendfile_cb( ec, m_transfered_size );
279  }
280 
281  break;
282  }
283  else if( 0 == n )
284  {
285  wait_write_is_possible();
286  break;
287  }
288  else
289  {
290  m_remained_size -= static_cast< file_size_t >( n );
291  m_transfered_size += static_cast< file_size_t >( n );
292  }
293 
294  // Loop around to try calling sendfile again.
295  }
296  }
297 };
298 
299 } /* namespace impl */
300 
301 } /* namespace restinio */
sendfile_operation_runner_t(const sendfile_operation_runner_t &)=delete
sendfile_operation_runner_t(sendfile_operation_runner_t &&)=delete
sendfile_operation_runner_t(const sendfile_operation_runner_t &)=delete
sendfile_operation_runner_t & operator=(sendfile_operation_runner_t &&)=delete
sendfile_operation_runner_t & operator=(sendfile_operation_runner_t &&)=delete
A specialization for plain tcp-socket using linux sendfile() (http://man7.org/linux/man-pages/man2/se...
sendfile_operation_runner_t & operator=(const sendfile_operation_runner_t &)=delete
std::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, optional_t< Value_Type > >::type opt_value(const Parameter_Container &params, string_view_t key)
Gets the value of a parameter specified by key wrapped in optional_t<Value_Type> if parameter exists ...
Definition: value_or.hpp:64
sendfile_operation_runner_t & operator=(const sendfile_operation_runner_t &)=delete