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/location.h"
11 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/task_runner.h"
14 #include "base/thread_task_runner_handle.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 DCHECK(file_
.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
, base::ThreadTaskRunnerHandle::Get()));
91 return ERR_IO_PENDING
;
94 int FileStream::Context::Write(IOBuffer
* buf
,
96 const CompletionCallback
& callback
) {
97 CHECK(!async_in_progress_
);
101 DWORD bytes_written
= 0;
102 if (!WriteFile(file_
.GetPlatformFile(), buf
->data(), buf_len
,
103 &bytes_written
, &io_context_
.overlapped
)) {
104 IOResult error
= IOResult::FromOSError(GetLastError());
105 if (error
.os_error
== ERROR_IO_PENDING
)
106 IOCompletionIsPending(callback
, buf
);
108 LOG(WARNING
) << "WriteFile failed: " << error
.os_error
;
109 return static_cast<int>(error
.result
);
112 IOCompletionIsPending(callback
, buf
);
113 return ERR_IO_PENDING
;
116 FileStream::Context::IOResult
FileStream::Context::SeekFileImpl(
118 LARGE_INTEGER result
;
119 result
.QuadPart
= offset
;
120 SetOffset(&io_context_
.overlapped
, result
);
121 return IOResult(result
.QuadPart
, 0);
124 void FileStream::Context::OnFileOpened() {
125 base::MessageLoopForIO::current()->RegisterIOHandler(file_
.GetPlatformFile(),
129 void FileStream::Context::IOCompletionIsPending(
130 const CompletionCallback
& callback
,
132 DCHECK(callback_
.is_null());
133 callback_
= callback
;
134 in_flight_buf_
= buf
; // Hold until the async operation ends.
135 async_in_progress_
= true;
138 void FileStream::Context::OnIOCompleted(
139 base::MessageLoopForIO::IOContext
* context
,
142 DCHECK_EQ(&io_context_
, context
);
143 DCHECK(!callback_
.is_null());
144 DCHECK(async_in_progress_
);
146 if (!async_read_initiated_
)
147 async_in_progress_
= false;
150 io_complete_for_read_received_
= true;
151 // If we are called due to a pending read and the asynchronous read task
152 // has not completed we have to keep the context around until it completes.
153 if (async_read_initiated_
&& !async_read_completed_
)
155 DeleteOrphanedContext();
159 if (error
== ERROR_HANDLE_EOF
) {
162 IOResult error_result
= IOResult::FromOSError(error
);
163 result_
= static_cast<int>(error_result
.result
);
166 DCHECK_EQ(result_
, static_cast<int>(bytes_read
));
167 result_
= bytes_read
;
168 IncrementOffset(&io_context_
.overlapped
, bytes_read
);
171 if (async_read_initiated_
)
172 io_complete_for_read_received_
= true;
174 InvokeUserCallback();
177 void FileStream::Context::InvokeUserCallback() {
178 // For an asynchonous Read operation don't invoke the user callback until
179 // we receive the IO completion notification and the asynchronous Read
180 // completion notification.
181 if (async_read_initiated_
) {
182 if (!io_complete_for_read_received_
|| !async_read_completed_
)
184 async_read_initiated_
= false;
185 io_complete_for_read_received_
= false;
186 async_read_completed_
= false;
187 async_in_progress_
= false;
189 CompletionCallback temp_callback
= callback_
;
191 scoped_refptr
<IOBuffer
> temp_buf
= in_flight_buf_
;
192 in_flight_buf_
= NULL
;
193 temp_callback
.Run(result_
);
196 void FileStream::Context::DeleteOrphanedContext() {
197 async_in_progress_
= false;
199 in_flight_buf_
= NULL
;
204 void FileStream::Context::ReadAsync(
205 FileStream::Context
* context
,
207 scoped_refptr
<IOBuffer
> buf
,
209 OVERLAPPED
* overlapped
,
210 scoped_refptr
<base::SingleThreadTaskRunner
> origin_thread_task_runner
) {
211 DWORD bytes_read
= 0;
212 BOOL ret
= ::ReadFile(file
, buf
->data(), buf_len
, &bytes_read
, overlapped
);
213 origin_thread_task_runner
->PostTask(
215 base::Bind(&FileStream::Context::ReadAsyncResult
,
216 base::Unretained(context
), ret
, bytes_read
, ::GetLastError()));
219 void FileStream::Context::ReadAsyncResult(BOOL read_file_ret
,
222 // If the context is orphaned and we already received the io completion
223 // notification then we should delete the context and get out.
224 if (orphaned_
&& io_complete_for_read_received_
) {
225 DeleteOrphanedContext();
229 async_read_completed_
= true;
231 result_
= bytes_read
;
232 InvokeUserCallback();
236 IOResult error
= IOResult::FromOSError(os_error
);
237 if (error
.os_error
== ERROR_IO_PENDING
) {
238 InvokeUserCallback();
240 OnIOCompleted(&io_context_
, 0, error
.os_error
);