RESTinio
Toggle main menu visibility
Loading...
Searching...
No Matches
restinio
impl
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 >
27
class
sendfile_operation_runner_t
final
28
:
public
sendfile_operation_runner_base_t
<
Socket
>
29
{
30
public
:
31
using
base_type_t
=
sendfile_operation_runner_base_t
<
Socket
>;
32
33
sendfile_operation_runner_t
(
const
sendfile_operation_runner_t
& ) =
delete
;
34
sendfile_operation_runner_t
(
sendfile_operation_runner_t
&& ) =
delete
;
35
sendfile_operation_runner_t
&
operator
= (
const
sendfile_operation_runner_t
& ) =
delete
;
36
sendfile_operation_runner_t
&
operator
= (
sendfile_operation_runner_t
&& ) =
delete
;
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
(
_LARGEFILE64_SOURCE
)
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
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
134
make_async_write_handler
()
noexcept
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
{
152
this
->
init_next_write
(
)
;
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).
167
template
<>
168
class
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
173
[[
nodiscard
]]
174
bool
175
try_turn_non_blocking_mode
()
noexcept
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;
186
m_after_sendfile_cb
(
ec
,
m_transfered_size
);
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
(
207
m_file_descriptor
,
208
m_socket
.
native_handle
(),
209
m_next_write_offset
,
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.
223
m_next_write_offset
+=
n
;
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
>(
247
std
::
min
<
file_size_t
>(
m_remained_size
,
m_chunk_size
) );
248
249
auto
rc
=
250
::
sendfile
(
251
m_file_descriptor
,
252
m_socket
.
native_handle
(),
253
m_next_write_offset
,
254
&
n
,
255
nullptr
,
// struct sf_hdtr *hdtr
256
0 );
257
258
// Shift the number of bytes successfully sent.
259
m_next_write_offset
+=
n
;
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
275
call_native_sendfile
()
noexcept
276
{
277
#
if
defined
(
_LARGEFILE64_SOURCE
)
278
return
::
sendfile64
(
279
m_socket
.
native_handle
(),
280
m_file_descriptor
,
281
&
m_next_write_offset
,
282
std
::
min
<
file_size_t
>(
m_remained_size
,
m_chunk_size
) );
283
#
else
284
return
::
sendfile
(
285
m_socket
.
native_handle
(),
286
m_file_descriptor
,
287
&
m_next_write_offset
,
288
std
::
min
<
file_size_t
>(
m_remained_size
,
m_chunk_size
) );
289
#
endif
290
}
291
#
endif
292
293
[[
nodiscard
]]
294
bool
295
try_initiate_waiting_for_write_readiness
()
noexcept
296
{
297
bool
result
=
true
;
298
299
try
300
{
301
// We have to wait for the socket to become ready again.
302
m_socket
.
async_wait
(
303
asio_ns
::
ip
::
tcp
::
socket
::
wait_write
,
304
asio_ns
::
bind_executor
(
305
m_executor
,
306
[
this
,
ctx
=
this
->
shared_from_this
() ]
307
// NOTE: this lambda is noexcept since v.0.6.0.
308
(
const
asio_ns
::
error_code
&
ec
)
noexcept
{
309
if
(
ec
|| 0 ==
m_remained_size
)
310
{
311
m_after_sendfile_cb
(
ec
,
m_transfered_size
);
312
}
313
else
314
{
315
init_next_write
();
316
}
317
} ) );
318
}
319
catch
( ... )
320
{
321
m_after_sendfile_cb
(
322
make_asio_compaible_error
(
323
asio_convertible_error_t
::
async_write_call_failed
),
324
m_transfered_size
);
325
result
=
false
;
326
}
327
328
return
result
;
329
}
330
331
public
:
332
using
base_type_t
=
sendfile_operation_runner_base_t
<
asio_ns
::
ip
::
tcp
::
socket
>;
333
334
sendfile_operation_runner_t
(
const
sendfile_operation_runner_t
& ) =
delete
;
335
sendfile_operation_runner_t
(
sendfile_operation_runner_t
&& ) =
delete
;
336
sendfile_operation_runner_t
&
operator
= (
const
sendfile_operation_runner_t
& ) =
delete
;
337
sendfile_operation_runner_t
&
operator
= (
sendfile_operation_runner_t
&& ) =
delete
;
338
339
// Reuse construstors from base.
340
using
base_type_t
::
base_type_t
;
341
342
virtual
void
343
start
()
override
344
{
345
init_next_write
();
346
}
347
348
/*!
349
* @note
350
* This method is noexcept since v.0.6.0.
351
*/
352
void
353
init_next_write
()
noexcept
354
{
355
if
( !
try_turn_non_blocking_mode
() )
356
return
;
357
358
while
(
true
)
359
{
360
// Try the system call.
361
errno
= 0;
362
363
if
( 0 ==
m_remained_size
)
364
{
365
// We are done.
366
// Result of try_initiate_waiting_for_write_readiness can
367
// be ignored here.
368
(
void
)
try_initiate_waiting_for_write_readiness
();
369
break
;
370
}
371
372
const
auto
n
=
call_native_sendfile
();
373
374
if
( -1 ==
n
)
375
{
376
if
(
errno
==
EAGAIN
||
errno
==
EINTR
)
377
{
378
if
( !
try_initiate_waiting_for_write_readiness
() )
379
return
;
380
}
381
else
382
{
383
m_after_sendfile_cb
(
384
asio_ns
::
error_code
{
385
errno
,
asio_ns
::
error
::
get_system_category
() },
386
m_transfered_size
);
387
}
388
389
break
;
390
}
391
else
if
( 0 ==
n
)
392
{
393
// Result of try_initiate_waiting_for_write_readiness can
394
// be ignored here.
395
(
void
)
try_initiate_waiting_for_write_readiness
();
396
break
;
397
}
398
else
399
{
400
m_remained_size
-=
static_cast
<
file_size_t
>(
n
);
401
m_transfered_size
+=
static_cast
<
file_size_t
>(
n
);
402
}
403
404
// Loop around to try calling sendfile again.
405
}
406
}
407
};
408
409
}
/* namespace impl */
410
411
}
/* namespace restinio */
restinio::impl::sendfile_operation_runner_t::m_buffer
std::unique_ptr< char[] > m_buffer
Definition
sendfile_operation_default.ipp:110
restinio::impl::sendfile_operation_runner_t::make_async_write_handler
auto make_async_write_handler() noexcept
Helper method for making a lambda for async_write completion handler.
Definition
sendfile_operation_default.ipp:114
restinio::impl::sendfile_operation_runner_t::start
virtual void start() override
Definition
sendfile_operation_default.ipp:39
restinio::impl::sendfile_operation_runner_t::operator=
sendfile_operation_runner_t & operator=(sendfile_operation_runner_t &&)=delete
restinio::impl::sendfile_operation_runner_t::sendfile_operation_runner_t
sendfile_operation_runner_t(const sendfile_operation_runner_t &)=delete
restinio::impl::sendfile_operation_runner_t::base_type_t
sendfile_operation_runner_base_t< Socket > base_type_t
Definition
sendfile_operation_default.ipp:27
restinio::impl::sendfile_operation_runner_t::operator=
sendfile_operation_runner_t & operator=(const sendfile_operation_runner_t &)=delete
restinio::impl::sendfile_operation_runner_t::init_next_write
void init_next_write() noexcept
Definition
sendfile_operation_default.ipp:67
restinio::impl::sendfile_operation_runner_t::sendfile_operation_runner_t
sendfile_operation_runner_t(sendfile_operation_runner_t &&)=delete
restinio::impl
Definition
sendfile_operation_default.ipp:15
restinio
Definition
sendfile_operation_default.ipp:12
Generated by
1.17.0