RESTinio
Loading...
Searching...
No Matches
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
15namespace restinio
16{
17
18namespace impl
19{
20
21//
22// sendfile_operation_runner_t
23//
24
25//! A runner of sendfile operation
26template < typename Socket >
29{
30 public:
32
37
38 // Reuse construstors from base.
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 {
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
62 /*!
63 * @note
64 * This method is noexcept since v.0.6.0.
65 */
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(
78 this->m_file_descriptor,
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
88 this->m_after_sendfile_cb(
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 {
96 this->m_after_sendfile_cb(
97 asio_ns::error_code{
98 asio_ec::eof,
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,
109 asio_ns::const_buffer{
110 this->m_buffer.get(),
111 static_cast< std::size_t >( n ) },
112 asio_ns::bind_executor(
113 this->m_executor,
114 make_async_write_handler() ) );
115 }
116 catch( ... )
117 {
118 this->m_after_sendfile_cb(
119 make_asio_compaible_error(
120 asio_convertible_error_t::async_write_call_failed ),
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
132 //! Helper method for making a lambda for async_write completion handler.
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 {
146 this->m_after_sendfile_cb(
147 ec,
148 this->m_transfered_size );
149 }
150 else
151 {
153 }
154 }
155 else
156 {
157 this->m_after_sendfile_cb(
158 ec,
159 this->m_transfered_size );
160 }
161 };
162 }
163};
164
165//! A specialization for plain tcp-socket using
166//! linux sendfile() (http://man7.org/linux/man-pages/man2/sendfile.2.html).
167template <>
170{
171 private:
172
173 [[nodiscard]]
174 bool
176 {
177 bool result = true;
178
180 {
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 )
195 [[nodiscard]]
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 =
206 ::sendfile(
210 static_cast< size_t >(
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
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 )
237 [[nodiscard]]
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 >(
248
249 auto rc =
250 ::sendfile(
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
273 [[nodiscard]]
274 auto
283#endif
284
285 [[nodiscard]]
286 bool
288 {
289 bool result = true;
290
291 try
292 {
293 // We have to wait for the socket to become ready again.
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.
333
334 virtual void
336 {
338 }
339
340 /*!
341 * @note
342 * This method is noexcept since v.0.6.0.
343 */
344 void
346 {
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.
361 break;
362 }
363
364 const auto n = call_native_sendfile();
365
366 if( -1 == n )
367 {
368 if( errno == EAGAIN || errno == EINTR )
369 {
371 return;
372 }
373 else
374 {
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.
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 */
auto make_async_write_handler() noexcept
Helper method for making a lambda for async_write completion handler.
sendfile_operation_runner_t & operator=(sendfile_operation_runner_t &&)=delete
sendfile_operation_runner_t(const sendfile_operation_runner_t &)=delete
sendfile_operation_runner_base_t< Socket > base_type_t
sendfile_operation_runner_t & operator=(const sendfile_operation_runner_t &)=delete
sendfile_operation_runner_t(sendfile_operation_runner_t &&)=delete