1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/base/file_stream_context.h"
9 #include "base/files/file_path.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/metrics/histogram.h"
14 #include "base/task_runner.h"
15 #include "base/threading/worker_pool.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
23 void SetOffset(OVERLAPPED
* overlapped
, const LARGE_INTEGER
& offset
) {
24 overlapped
->Offset
= offset
.LowPart
;
25 overlapped
->OffsetHigh
= offset
.HighPart
;
28 void IncrementOffset(OVERLAPPED
* overlapped
, DWORD count
) {
30 offset
.LowPart
= overlapped
->Offset
;
31 offset
.HighPart
= overlapped
->OffsetHigh
;
32 offset
.QuadPart
+= static_cast<LONGLONG
>(count
);
33 SetOffset(overlapped
, offset
);
38 FileStream::Context::Context(const scoped_refptr
<base::TaskRunner
>& task_runner
)
40 async_in_progress_(false),
42 task_runner_(task_runner
),
43 async_read_initiated_(false),
44 async_read_completed_(false),
45 io_complete_for_read_received_(false),
47 io_context_
.handler
= this;
48 memset(&io_context_
.overlapped
, 0, sizeof(io_context_
.overlapped
));
51 FileStream::Context::Context(base::File file
,
52 const scoped_refptr
<base::TaskRunner
>& task_runner
)
55 async_in_progress_(false),
57 task_runner_(task_runner
),
58 async_read_initiated_(false),
59 async_read_completed_(false),
60 io_complete_for_read_received_(false),
62 io_context_
.handler
= this;
63 memset(&io_context_
.overlapped
, 0, sizeof(io_context_
.overlapped
));
64 if (file_
.IsValid()) {
65 // TODO(hashimoto): Check that file_ is async.
70 FileStream::Context::~Context() {
73 int FileStream::Context::Read(IOBuffer
* buf
,
75 const CompletionCallback
& callback
) {
76 CHECK(!async_in_progress_
);
77 DCHECK(!async_read_initiated_
);
78 DCHECK(!async_read_completed_
);
79 DCHECK(!io_complete_for_read_received_
);
81 IOCompletionIsPending(callback
, buf
);
83 async_read_initiated_
= true;
85 task_runner_
->PostTask(
87 base::Bind(&FileStream::Context::ReadAsync
, base::Unretained(this),
88 file_
.GetPlatformFile(), make_scoped_refptr(buf
), buf_len
,
89 &io_context_
.overlapped
,
90 base::MessageLoop::current()->message_loop_proxy()));
91 return ERR_IO_PENDING
;
94 int FileStream::Context::Write(IOBuffer
* buf
,
96 const CompletionCallback
& callback
) {
97 CHECK(!async_in_progress_
);
99 DWORD bytes_written
= 0;
100 if (!WriteFile(file_
.GetPlatformFile(), buf
->data(), buf_len
,
101 &bytes_written
, &io_context_
.overlapped
)) {
102 IOResult error
= IOResult::FromOSError(GetLastError());
103 if (error
.os_error
== ERROR_IO_PENDING
)
104 IOCompletionIsPending(callback
, buf
);
106 LOG(WARNING
) << "WriteFile failed: " << error
.os_error
;
107 return static_cast<int>(error
.result
);
110 IOCompletionIsPending(callback
, buf
);
111 return ERR_IO_PENDING
;
114 FileStream::Context::IOResult
FileStream::Context::SeekFileImpl(
115 base::File::Whence whence
,
117 LARGE_INTEGER result
;
118 result
.QuadPart
= file_
.Seek(whence
, offset
);
119 if (result
.QuadPart
>= 0) {
120 SetOffset(&io_context_
.overlapped
, result
);
121 return IOResult(result
.QuadPart
, 0);
124 return IOResult::FromOSError(GetLastError());
127 void FileStream::Context::OnFileOpened() {
128 base::MessageLoopForIO::current()->RegisterIOHandler(file_
.GetPlatformFile(),
132 void FileStream::Context::IOCompletionIsPending(
133 const CompletionCallback
& callback
,
135 DCHECK(callback_
.is_null());
136 callback_
= callback
;
137 in_flight_buf_
= buf
; // Hold until the async operation ends.
138 async_in_progress_
= true;
141 void FileStream::Context::OnIOCompleted(
142 base::MessageLoopForIO::IOContext
* context
,
145 DCHECK_EQ(&io_context_
, context
);
146 DCHECK(!callback_
.is_null());
147 DCHECK(async_in_progress_
);
149 if (!async_read_initiated_
)
150 async_in_progress_
= false;
153 async_in_progress_
= false;
155 in_flight_buf_
= NULL
;
160 if (error
== ERROR_HANDLE_EOF
) {
163 IOResult error_result
= IOResult::FromOSError(error
);
164 result_
= static_cast<int>(error_result
.result
);
166 result_
= bytes_read
;
167 IncrementOffset(&io_context_
.overlapped
, bytes_read
);
170 if (async_read_initiated_
)
171 io_complete_for_read_received_
= true;
173 InvokeUserCallback();
176 void FileStream::Context::InvokeUserCallback() {
177 // For an asynchonous Read operation don't invoke the user callback until
178 // we receive the IO completion notification and the asynchronous Read
179 // completion notification.
180 if (async_read_initiated_
) {
181 if (!io_complete_for_read_received_
|| !async_read_completed_
)
183 async_read_initiated_
= false;
184 io_complete_for_read_received_
= false;
185 async_read_completed_
= false;
186 async_in_progress_
= false;
188 CompletionCallback temp_callback
= callback_
;
190 scoped_refptr
<IOBuffer
> temp_buf
= in_flight_buf_
;
191 in_flight_buf_
= NULL
;
192 temp_callback
.Run(result_
);
196 void FileStream::Context::ReadAsync(
197 FileStream::Context
* context
,
199 scoped_refptr
<net::IOBuffer
> buf
,
201 OVERLAPPED
* overlapped
,
202 scoped_refptr
<base::MessageLoopProxy
> origin_thread_loop
) {
203 DWORD bytes_read
= 0;
204 BOOL ret
= ::ReadFile(file
, buf
->data(), buf_len
, &bytes_read
, overlapped
);
205 origin_thread_loop
->PostTask(
206 FROM_HERE
, base::Bind(&FileStream::Context::ReadAsyncResult
,
207 base::Unretained(context
), ret
? bytes_read
: 0,
208 ret
? 0 : ::GetLastError()));
211 void FileStream::Context::ReadAsyncResult(DWORD bytes_read
, DWORD os_error
) {
213 result_
= bytes_read
;
215 IOResult error
= IOResult::FromOSError(os_error
);
216 if (error
.os_error
== ERROR_HANDLE_EOF
) {
217 // Report EOF by returning 0 bytes read.
218 OnIOCompleted(&io_context_
, 0, error
.os_error
);
219 } else if (error
.os_error
!= ERROR_IO_PENDING
) {
220 // We don't need to inform the caller about ERROR_PENDING_IO as that was
221 // already done when the ReadFile call was queued to the worker pool.
222 if (error
.os_error
) {
223 LOG(WARNING
) << "ReadFile failed: " << error
.os_error
;
224 OnIOCompleted(&io_context_
, 0, error
.os_error
);
227 async_read_completed_
= true;
228 InvokeUserCallback();