RESTinio
sendfile_operation_win.ipp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  sendfile routine.
7 */
8 
9 #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
10 
11 #include <cstdio>
12 
13 namespace restinio
14 {
15 
16 namespace impl
17 {
18 
19 //
20 // sendfile_operation_runner_t
21 //
22 
23 //! A runner of sendfile operation.
24 template < typename Socket >
25 class sendfile_operation_runner_t final
26  : public sendfile_operation_runner_base_t< Socket >
27 {
28  public:
29  using base_type_t = sendfile_operation_runner_base_t< Socket >;
30 
31  sendfile_operation_runner_t( const sendfile_operation_runner_t & ) = delete;
32  sendfile_operation_runner_t( sendfile_operation_runner_t && ) = delete;
33  sendfile_operation_runner_t & operator = ( const sendfile_operation_runner_t & ) = delete;
34  sendfile_operation_runner_t & operator = ( sendfile_operation_runner_t && ) = delete;
35 
36  sendfile_operation_runner_t(
37  sendfile_t & sf,
38  asio_ns::executor executor,
39  Socket & socket,
40  after_sendfile_cb_t after_sendfile_cb )
41  : base_type_t{ sf, std::move( executor), socket, std::move( after_sendfile_cb ) }
42  {
43  // We have passed sf.file_descriptor() to m_file_handle object.
44  // It means that file description will be closed automatically
45  // by m_file_handle.
46  // But sf still holds the same file_descriptor. Because of that
47  // we should tell sf to release this file_descriptor.
48  takeaway_file_descriptor(sf).release();
49  }
50 
51  virtual void
52  start() override
53  {
54  init_next_read_some_from_file();
55  }
56 
57  void
58  init_next_read_some_from_file()
59  {
60  const auto desired_size =
61  std::min< file_size_t >( this->m_remained_size, this->m_chunk_size );
62 
63  this->m_file_handle.async_read_some_at(
64  this->m_next_write_offset,
65  asio_ns::buffer(
66  this->m_buffer.get(),
67  static_cast< std::size_t >( desired_size ) ),
68  asio_ns::bind_executor(
69  this->m_executor,
70  [ this, ctx = this->shared_from_this() ]
71  ( const asio_ns::error_code & ec, std::size_t len ){
72 
73  if( ec || 0 == this->m_remained_size )
74  {
75  this->m_after_sendfile_cb( ec, this->m_transfered_size );
76  }
77  if( !ec )
78  {
79  if( 0 != len )
80  init_next_write( len );
81  else
82  {
83  this->m_after_sendfile_cb(
84  make_error_code( asio_ec::eof ),
85  this->m_transfered_size );
86  }
87  }
88  else
89  {
90  this->m_after_sendfile_cb( ec, this->m_transfered_size );
91  }
92  } ) );
93  }
94 
95  void
96  init_next_write( std::size_t len )
97  {
98  asio_ns::async_write(
99  this->m_socket,
100  asio_ns::const_buffer{
101  this->m_buffer.get(),
102  static_cast< std::size_t >( len ) },
103  asio_ns::bind_executor(
104  this->m_executor,
105  [ this, ctx = this->shared_from_this() ]
106  ( const asio_ns::error_code & ec, std::size_t written ){
107 
108  if( !ec )
109  {
110  this->m_remained_size -= written;
111  this->m_transfered_size += written;
112  this->m_next_write_offset += written;
113 
114  if( 0 == this->m_remained_size )
115  {
116  this->m_after_sendfile_cb( ec, this->m_transfered_size );
117  }
118  else
119  {
120  this->init_next_read_some_from_file();
121  }
122  }
123  else
124  {
125  this->m_after_sendfile_cb( ec, this->m_transfered_size );
126  }
127  }
128  ) );
129  }
130 
131  private:
132  std::unique_ptr< char[] > m_buffer{ new char [ this->m_chunk_size ] };
133  asio_ns::windows::random_access_handle
134  m_file_handle{ this->m_socket.get_executor().context(), this->m_file_descriptor };
135 };
136 
137 //! A runner of sendfile operation for raw socket.
138 template <>
139 class sendfile_operation_runner_t < asio_ns::ip::tcp::socket > final
140  : public sendfile_operation_runner_base_t< asio_ns::ip::tcp::socket >
141 {
142  public:
143  using base_type_t = sendfile_operation_runner_base_t< asio_ns::ip::tcp::socket >;
144 
145  sendfile_operation_runner_t( const sendfile_operation_runner_t & ) = delete;
146  sendfile_operation_runner_t( sendfile_operation_runner_t && ) = delete;
147  sendfile_operation_runner_t & operator = ( const sendfile_operation_runner_t & ) = delete;
148  sendfile_operation_runner_t & operator = ( sendfile_operation_runner_t && ) = delete;
149 
150  sendfile_operation_runner_t(
151  sendfile_t & sf,
152  asio_ns::executor executor,
153  asio_ns::ip::tcp::socket & socket,
154  after_sendfile_cb_t after_sendfile_cb )
155  : base_type_t{ sf, std::move( executor), socket, std::move( after_sendfile_cb ) }
156  {
157  // We have passed sf.file_descriptor() to m_file_handle object.
158  // It means that file description will be closed automatically
159  // by m_file_handle.
160  // But sf still holds the same file_descriptor. Because of that
161  // we should tell sf to release this file_descriptor.
162  takeaway_file_descriptor(sf).release();
163  }
164 
165  virtual void
166  start() override
167  {
168  init_next_write();
169  }
170 
171  void
172  init_next_write()
173  {
174 
175  asio_ns::windows::overlapped_ptr overlapped{
176  m_socket.get_executor().context(),
177  asio_ns::bind_executor(
178  m_executor,
179  [this, ctx = shared_from_this() ]
180  ( const asio_ns::error_code & ec, std::size_t written ){
181  if( !ec )
182  {
183  m_remained_size -= written;
184  m_transfered_size += written;
185  m_next_write_offset += written;
186 
187  if( 0 == m_remained_size )
188  {
189  m_after_sendfile_cb( ec, m_transfered_size );
190  }
191  else
192  {
193  init_next_write();
194  }
195  }
196  else
197  {
198  m_after_sendfile_cb( ec, m_transfered_size );
199  }
200  } ) };
201 
202  // Set offset.
203  overlapped.get()->Offset =
204  static_cast< DWORD >( m_next_write_offset & 0xFFFFFFFFULL );
205  overlapped.get()->OffsetHigh =
206  static_cast< DWORD >( (m_next_write_offset>>32) & 0xFFFFFFFFULL );
207 
208  // Amount of data to transfer.
209  const auto desired_size =
210  std::min< file_size_t >( this->m_remained_size, this->m_chunk_size );
211 
212  // Initiate the TransmitFile operation.
213  BOOL ok =
214  ::TransmitFile(
215  m_socket.native_handle(),
216  m_file_handle.native_handle(),
217  static_cast< DWORD >( desired_size ),
218  0,
219  overlapped.get(),
220  0,
221  0 );
222 
223  DWORD last_error = ::GetLastError();
224 
225  // Check if the operation completed immediately.
226  if( !ok && last_error != ERROR_IO_PENDING )
227  {
228  // The operation completed immediately, so a completion notification needs
229  // to be posted. When complete() is called, ownership of the OVERLAPPED-
230  // derived object passes to the io_context.
231  overlapped.complete( make_error_code( last_error ) , 0 );
232  }
233  else
234  {
235  // The operation was successfully initiated, so ownership of the
236  // OVERLAPPED-derived object has passed to the io_context.
237  overlapped.release();
238  }
239  }
240 
241  private:
242  std::unique_ptr< char[] > m_buffer{ new char [ m_chunk_size ] };
243  asio_ns::windows::random_access_handle
244  m_file_handle{ m_socket.get_executor().context(), m_file_descriptor };
245 };
246 
247 } /* namespace impl */
248 
249 } /* namespace restinio */
250 
251 #else // #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
252 
254 
255 #endif // #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)