Home | History | Annotate | Download | only in base
      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.
      4 
      5 #include "net/base/file_stream_context.h"
      6 
      7 #include <windows.h>
      8 
      9 #include "base/files/file_path.h"
     10 #include "base/logging.h"
     11 #include "base/metrics/histogram.h"
     12 #include "base/task_runner.h"
     13 #include "net/base/io_buffer.h"
     14 #include "net/base/net_errors.h"
     15 
     16 namespace net {
     17 
     18 namespace {
     19 
     20 void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
     21   overlapped->Offset = offset.LowPart;
     22   overlapped->OffsetHigh = offset.HighPart;
     23 }
     24 
     25 void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
     26   LARGE_INTEGER offset;
     27   offset.LowPart = overlapped->Offset;
     28   offset.HighPart = overlapped->OffsetHigh;
     29   offset.QuadPart += static_cast<LONGLONG>(count);
     30   SetOffset(overlapped, offset);
     31 }
     32 
     33 }  // namespace
     34 
     35 FileStream::Context::Context(const scoped_refptr<base::TaskRunner>& task_runner)
     36     : io_context_(),
     37       async_in_progress_(false),
     38       orphaned_(false),
     39       task_runner_(task_runner) {
     40   io_context_.handler = this;
     41   memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
     42 }
     43 
     44 FileStream::Context::Context(base::File file,
     45                              const scoped_refptr<base::TaskRunner>& task_runner)
     46     : io_context_(),
     47       file_(file.Pass()),
     48       async_in_progress_(false),
     49       orphaned_(false),
     50       task_runner_(task_runner) {
     51   io_context_.handler = this;
     52   memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
     53   if (file_.IsValid()) {
     54     // TODO(hashimoto): Check that file_ is async.
     55     OnFileOpened();
     56   }
     57 }
     58 
     59 FileStream::Context::~Context() {
     60 }
     61 
     62 int FileStream::Context::Read(IOBuffer* buf,
     63                               int buf_len,
     64                               const CompletionCallback& callback) {
     65   DCHECK(!async_in_progress_);
     66 
     67   DWORD bytes_read;
     68   if (!ReadFile(file_.GetPlatformFile(), buf->data(), buf_len,
     69                 &bytes_read, &io_context_.overlapped)) {
     70     IOResult error = IOResult::FromOSError(GetLastError());
     71     if (error.os_error == ERROR_IO_PENDING) {
     72       IOCompletionIsPending(callback, buf);
     73     } else if (error.os_error == ERROR_HANDLE_EOF) {
     74       return 0;  // Report EOF by returning 0 bytes read.
     75     } else {
     76       LOG(WARNING) << "ReadFile failed: " << error.os_error;
     77     }
     78     return error.result;
     79   }
     80 
     81   IOCompletionIsPending(callback, buf);
     82   return ERR_IO_PENDING;
     83 }
     84 
     85 int FileStream::Context::Write(IOBuffer* buf,
     86                                int buf_len,
     87                                const CompletionCallback& callback) {
     88   DWORD bytes_written = 0;
     89   if (!WriteFile(file_.GetPlatformFile(), buf->data(), buf_len,
     90                  &bytes_written, &io_context_.overlapped)) {
     91     IOResult error = IOResult::FromOSError(GetLastError());
     92     if (error.os_error == ERROR_IO_PENDING) {
     93       IOCompletionIsPending(callback, buf);
     94     } else {
     95       LOG(WARNING) << "WriteFile failed: " << error.os_error;
     96     }
     97     return error.result;
     98   }
     99 
    100   IOCompletionIsPending(callback, buf);
    101   return ERR_IO_PENDING;
    102 }
    103 
    104 FileStream::Context::IOResult FileStream::Context::SeekFileImpl(
    105     base::File::Whence whence,
    106     int64 offset) {
    107   LARGE_INTEGER result;
    108   result.QuadPart = file_.Seek(whence, offset);
    109   if (result.QuadPart >= 0) {
    110     SetOffset(&io_context_.overlapped, result);
    111     return IOResult(result.QuadPart, 0);
    112   }
    113 
    114   return IOResult::FromOSError(GetLastError());
    115 }
    116 
    117 void FileStream::Context::OnFileOpened() {
    118   base::MessageLoopForIO::current()->RegisterIOHandler(file_.GetPlatformFile(),
    119                                                        this);
    120 }
    121 
    122 void FileStream::Context::IOCompletionIsPending(
    123     const CompletionCallback& callback,
    124     IOBuffer* buf) {
    125   DCHECK(callback_.is_null());
    126   callback_ = callback;
    127   in_flight_buf_ = buf;  // Hold until the async operation ends.
    128   async_in_progress_ = true;
    129 }
    130 
    131 void FileStream::Context::OnIOCompleted(
    132     base::MessageLoopForIO::IOContext* context,
    133     DWORD bytes_read,
    134     DWORD error) {
    135   DCHECK_EQ(&io_context_, context);
    136   DCHECK(!callback_.is_null());
    137   DCHECK(async_in_progress_);
    138 
    139   async_in_progress_ = false;
    140   if (orphaned_) {
    141     callback_.Reset();
    142     in_flight_buf_ = NULL;
    143     CloseAndDelete();
    144     return;
    145   }
    146 
    147   int result;
    148   if (error == ERROR_HANDLE_EOF) {
    149     result = 0;
    150   } else if (error) {
    151     IOResult error_result = IOResult::FromOSError(error);
    152     result = error_result.result;
    153   } else {
    154     result = bytes_read;
    155     IncrementOffset(&io_context_.overlapped, bytes_read);
    156   }
    157 
    158   CompletionCallback temp_callback = callback_;
    159   callback_.Reset();
    160   scoped_refptr<IOBuffer> temp_buf = in_flight_buf_;
    161   in_flight_buf_ = NULL;
    162   temp_callback.Run(result);
    163 }
    164 
    165 }  // namespace net
    166