RESTinio
sendfile_operation_posix.ipp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
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
15namespace restinio
16{
17
18namespace impl
19{
20
21//
22// sendfile_operation_runner_t
23//
24
26template < typename Socket >
27class sendfile_operation_runner_t final
28 : public sendfile_operation_runner_base_t< 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 {
56 const asio_ns::error_code ec{ errno, asio_ns::error::get_system_category() };
57 this->m_after_sendfile_cb( ec, this->m_transfered_size );
58 return;
59 }
60 }
61
66 void
67 init_next_write() noexcept
68 {
69 // A note about noexcept for that method.
70 // It seems that there is no exceptions thrown by the method itself.
71 // The only dangerous place is a call to m_after_sendfile_cb.
72 // But the main code behind m_after_sendfile_cb is going from
73 // connection_t class and that code is noexcept since v.0.6.0.
74 //
75 while( true )
76 {
77 auto const n = ::read(
79 this->m_buffer.get(),
80 std::min< file_size_t >(
81 this->m_remained_size, this->m_chunk_size ) );
82
83 if( -1 == n )
84 {
85 if( errno == EINTR )
86 continue;
87
89 asio_ns::error_code{
90 errno,
91 asio_ns::error::get_system_category() },
92 this->m_transfered_size );
93 }
94 else if( 0 == n )
95 {
97 asio_ns::error_code{
99 asio_ns::error::get_system_category() },
100 this->m_transfered_size );
101 }
102 else
103 {
104 // If asio_ns::async_write fails we'll call m_after_sendfile_cb.
105 try
106 {
107 asio_ns::async_write(
108 this->m_socket,
110 this->m_buffer.get(),
111 static_cast< std::size_t >( n ) },
112 asio_ns::bind_executor(
113 this->m_executor,
115 }
116 catch( ... )
117 {
121 this->m_transfered_size );
122 }
123 }
124
125 break;
126 }
127 }
128
129 private:
130 std::unique_ptr< char[] > m_buffer{ new char [ this->m_chunk_size ] };
131
133 auto
135 {
136 return [this, ctx = this->shared_from_this()]
137 // NOTE: this lambda is noexcept since v.0.6.0.
138 ( const asio_ns::error_code & ec, std::size_t written ) noexcept
139 {
140 if( !ec )
141 {
142 this->m_remained_size -= written;
143 this->m_transfered_size += written;
144 if( 0 == this->m_remained_size )
145 {
147 ec,
148 this->m_transfered_size );
149 }
150 else
151 {
152 this->init_next_write();
153 }
154 }
155 else
156 {
158 ec,
159 this->m_transfered_size );
160 }
161 };
162 }
163};
164
167template <>
168class sendfile_operation_runner_t< asio_ns::ip::tcp::socket > final
169 : public sendfile_operation_runner_base_t< asio_ns::ip::tcp::socket >
170{
171 private:
172
174 bool
176 {
177 bool result = true;
178
179 if( !m_socket.native_non_blocking() )
180 {
181 asio_ns::error_code ec;
182 m_socket.native_non_blocking( true, ec );
183 if( ec )
184 {
185 // We assume that m_after_sendfile_cb doesn't throw;
187 result = false;
188 }
189 }
190
191 return result;
192 }
193
194#if defined( RESTINIO_FREEBSD_TARGET )
196 auto
197 call_native_sendfile() noexcept
198 {
199 // FreeBSD sendfile signature:
200 // int sendfile(int fd, int s, off_t offset, size_t nbytes,
201 // struct sf_hdtr *hdtr, off_t *sbytes, int flags);
202 // https://www.freebsd.org/cgi/man.cgi?query=sendfile
203
204 off_t n{ 0 };
205 auto rc =
208 m_socket.native_handle(),
210 static_cast< size_t >(
211 std::min< file_size_t >( m_remained_size, m_chunk_size ) ),
212 nullptr, // struct sf_hdtr *hdtr
213 &n, // sbytes
214 // Is 16 a reasonable constant here.
215#if __FreeBSD__ >= 11
216 SF_FLAGS( 16, SF_NOCACHE )
217#else
218 SF_MNOWAIT
219#endif
220 );
221
222 // Shift the number of bytes successfully sent.
224
225 if( -1 == rc )
226 {
227 // It is still possible that some bytes had been sent.
228 m_remained_size -= static_cast< file_size_t >( n );
229 m_transfered_size += static_cast< file_size_t >( n );
230
231 n = -1;
232 }
233
234 return n;
235 }
236#elif defined( RESTINIO_MACOS_TARGET )
238 auto
239 call_native_sendfile() noexcept
240 {
241 // macOS sendfile signature:
242 // in sendfile(int fd, int s, off_t offset,
243 // off_t *len, struct sf_hdtr *hdtr, int flags);
244
245 off_t n =
246 static_cast< off_t >(
247 std::min< file_size_t >( m_remained_size, m_chunk_size ) );
248
249 auto rc =
252 m_socket.native_handle(),
254 &n,
255 nullptr, // struct sf_hdtr *hdtr
256 0 );
257
258 // Shift the number of bytes successfully sent.
260
261 if( -1 == rc )
262 {
263 // It is still possible that some bytes had been sent.
264 m_remained_size -= static_cast< file_size_t >( n );
265 m_transfered_size += static_cast< file_size_t >( n );
266
267 n = -1;
268 }
269
270 return n;
271 }
272#else
274 auto
276 {
277 return ::sendfile64(
278 m_socket.native_handle(),
281 std::min< file_size_t >( m_remained_size, m_chunk_size ) );
282 }
283#endif
284
286 bool
288 {
289 bool result = true;
290
291 try
292 {
293 // We have to wait for the socket to become ready again.
294 m_socket.async_wait(
295 asio_ns::ip::tcp::socket::wait_write,
296 asio_ns::bind_executor(
298 [ this, ctx = this->shared_from_this() ]
299 // NOTE: this lambda is noexcept since v.0.6.0.
300 ( const asio_ns::error_code & ec ) noexcept {
301 if( ec || 0 == m_remained_size )
302 {
304 }
305 else
306 {
308 }
309 } ) );
310 }
311 catch( ... )
312 {
317 result = false;
318 }
319
320 return result;
321 }
322
323 public:
325
330
331 // Reuse construstors from base.
332 using base_type_t::base_type_t;
333
334 virtual void
335 start() override
336 {
338 }
339
344 void
346 {
347 if( !try_turn_non_blocking_mode() )
348 return;
349
350 while( true )
351 {
352 // Try the system call.
353 errno = 0;
354
355 if( 0 == m_remained_size )
356 {
357 // We are done.
358 // Result of try_initiate_waiting_for_write_readiness can
359 // be ignored here.
360 (void)try_initiate_waiting_for_write_readiness();
361 break;
362 }
363
364 const auto n = call_native_sendfile();
365
366 if( -1 == n )
367 {
368 if( errno == EAGAIN || errno == EINTR )
369 {
370 if( !try_initiate_waiting_for_write_readiness() )
371 return;
372 }
373 else
374 {
376 asio_ns::error_code{
377 errno, asio_ns::error::get_system_category() },
379 }
380
381 break;
382 }
383 else if( 0 == n )
384 {
385 // Result of try_initiate_waiting_for_write_readiness can
386 // be ignored here.
387 (void)try_initiate_waiting_for_write_readiness();
388 break;
389 }
390 else
391 {
392 m_remained_size -= static_cast< file_size_t >( n );
393 m_transfered_size += static_cast< file_size_t >( n );
394 }
395
396 // Loop around to try calling sendfile again.
397 }
398 }
399};
400
401} /* namespace impl */
402
403} /* namespace restinio */
404
A base runner of sendfile operation (keeps all the data).
sendfile_operation_runner_t(const sendfile_operation_runner_t &)=delete
auto make_async_write_handler() noexcept
Helper method for making a lambda for async_write completion handler.
sendfile_operation_runner_t(const sendfile_operation_runner_t &)=delete
sendfile_operation_runner_t & operator=(const sendfile_operation_runner_t &)=delete
sendfile_operation_runner_t(sendfile_operation_runner_t &&)=delete
#define RESTINIO_NODISCARD
constexpr auto eof
sendfile_t sendfile(file_descriptor_holder_t fd, file_meta_t meta, file_size_t chunk_size=sendfile_default_chunk_size) noexcept
Definition: sendfile.hpp:468
std::uint64_t file_size_t
@ async_write_call_failed
A call to async_write failed. The corresponding write operation wasn't done.
constexpr const_buffer_t const_buffer(const void *str, std::size_t size) noexcept
Definition: buffers.hpp:424
asio_ns::error_code make_asio_compaible_error(asio_convertible_error_t err) noexcept
Make restinio error_code compatible with asio_ns::error_code.
#define SEEK_SET
Definition: zconf.h:498