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;
86 task_runner_
->PostTask(
88 base::Bind(&FileStream::Context::ReadAsync
, base::Unretained(this),
89 file_
.GetPlatformFile(), make_scoped_refptr(buf
), buf_len
,
90 &io_context_
.overlapped
,
91 base::MessageLoop::current()->message_loop_proxy()));
92 return ERR_IO_PENDING
;
95 int FileStream::Context::Write(IOBuffer
* buf
,
97 const CompletionCallback
& callback
) {
98 CHECK(!async_in_progress_
);
102 DWORD bytes_written
= 0;
103 if (!WriteFile(file_
.GetPlatformFile(), buf
->data(), buf_len
,
104 &bytes_written
, &io_context_
.overlapped
)) {
105 IOResult error
= IOResult::FromOSError(GetLastError());
106 if (error
.os_error
== ERROR_IO_PENDING
)
107 IOCompletionIsPending(callback
, buf
);
109 LOG(WARNING
) << "WriteFile failed: " << error
.os_error
;
110 return static_cast<int>(error
.result
);
113 IOCompletionIsPending(callback
, buf
);
114 return ERR_IO_PENDING
;
117 FileStream::Context::IOResult
FileStream::Context::SeekFileImpl(
118 base::File::Whence whence
,
120 LARGE_INTEGER result
;
121 result
.QuadPart
= file_
.Seek(whence
, offset
);
122 if (result
.QuadPart
>= 0) {
123 SetOffset(&io_context_
.overlapped
, result
);
124 return IOResult(result
.QuadPart
, 0);
127 return IOResult::FromOSError(GetLastError());
130 void FileStream::Context::OnFileOpened() {
131 base::MessageLoopForIO::current()->RegisterIOHandler(file_
.GetPlatformFile(),
135 void FileStream::Context::IOCompletionIsPending(
136 const CompletionCallback
& callback
,
138 DCHECK(callback_
.is_null());
139 callback_
= callback
;
140 in_flight_buf_
= buf
; // Hold until the async operation ends.
141 async_in_progress_
= true;
144 void FileStream::Context::OnIOCompleted(
145 base::MessageLoopForIO::IOContext
* context
,
148 DCHECK_EQ(&io_context_
, context
);
149 DCHECK(!callback_
.is_null());
150 DCHECK(async_in_progress_
);
152 if (!async_read_initiated_
)
153 async_in_progress_
= false;
156 io_complete_for_read_received_
= true;
157 // If we are called due to a pending read and the asynchronous read task
158 // has not completed we have to keep the context around until it completes.
159 if (async_read_initiated_
&& !async_read_completed_
)
161 DeleteOrphanedContext();
165 if (error
== ERROR_HANDLE_EOF
) {
168 IOResult error_result
= IOResult::FromOSError(error
);
169 result_
= static_cast<int>(error_result
.result
);
172 DCHECK_EQ(result_
, static_cast<int>(bytes_read
));
173 result_
= bytes_read
;
174 IncrementOffset(&io_context_
.overlapped
, bytes_read
);
177 if (async_read_initiated_
)
178 io_complete_for_read_received_
= true;
180 InvokeUserCallback();
183 void FileStream::Context::InvokeUserCallback() {
184 // For an asynchonous Read operation don't invoke the user callback until
185 // we receive the IO completion notification and the asynchronous Read
186 // completion notification.
187 if (async_read_initiated_
) {
188 if (!io_complete_for_read_received_
|| !async_read_completed_
)
190 async_read_initiated_
= false;
191 io_complete_for_read_received_
= false;
192 async_read_completed_
= false;
193 async_in_progress_
= false;
195 CompletionCallback temp_callback
= callback_
;
197 scoped_refptr
<IOBuffer
> temp_buf
= in_flight_buf_
;
198 in_flight_buf_
= NULL
;
199 temp_callback
.Run(result_
);
202 void FileStream::Context::DeleteOrphanedContext() {
203 async_in_progress_
= false;
205 in_flight_buf_
= NULL
;
210 void FileStream::Context::ReadAsync(
211 FileStream::Context
* context
,
213 scoped_refptr
<IOBuffer
> buf
,
215 OVERLAPPED
* overlapped
,
216 scoped_refptr
<base::MessageLoopProxy
> origin_thread_loop
) {
217 DWORD bytes_read
= 0;
218 BOOL ret
= ::ReadFile(file
, buf
->data(), buf_len
, &bytes_read
, overlapped
);
219 origin_thread_loop
->PostTask(
221 base::Bind(&FileStream::Context::ReadAsyncResult
,
222 base::Unretained(context
), ret
, bytes_read
, ::GetLastError()));
225 void FileStream::Context::ReadAsyncResult(BOOL read_file_ret
,
228 // If the context is orphaned and we already received the io completion
229 // notification then we should delete the context and get out.
230 if (orphaned_
&& io_complete_for_read_received_
) {
231 DeleteOrphanedContext();
235 async_read_completed_
= true;
237 result_
= bytes_read
;
238 InvokeUserCallback();
242 IOResult error
= IOResult::FromOSError(os_error
);
243 if (error
.os_error
== ERROR_IO_PENDING
) {
244 InvokeUserCallback();
246 OnIOCompleted(&io_context_
, 0, error
.os_error
);