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/disk_cache/blockfile/file.h"
6
7#include "base/bind.h"
8#include "base/lazy_instance.h"
9#include "base/location.h"
10#include "base/logging.h"
11#include "base/run_loop.h"
12#include "base/task_runner_util.h"
13#include "base/threading/sequenced_worker_pool.h"
14#include "net/base/net_errors.h"
15#include "net/disk_cache/disk_cache.h"
16
17namespace {
18
19// The maximum number of threads for this pool.
20const int kMaxThreads = 5;
21
22class FileWorkerPool : public base::SequencedWorkerPool {
23 public:
24  FileWorkerPool() : base::SequencedWorkerPool(kMaxThreads, "CachePool") {}
25
26 protected:
27  virtual ~FileWorkerPool() {}
28};
29
30base::LazyInstance<FileWorkerPool>::Leaky s_worker_pool =
31    LAZY_INSTANCE_INITIALIZER;
32
33}  // namespace
34
35namespace disk_cache {
36
37File::File(base::File file)
38    : init_(true),
39      mixed_(true),
40      base_file_(file.Pass()) {
41}
42
43bool File::Init(const base::FilePath& name) {
44  if (base_file_.IsValid())
45    return false;
46
47  int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
48              base::File::FLAG_WRITE;
49  base_file_.Initialize(name, flags);
50  return base_file_.IsValid();
51}
52
53bool File::IsValid() const {
54  return base_file_.IsValid();
55}
56
57bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
58  DCHECK(base_file_.IsValid());
59  if (buffer_len > static_cast<size_t>(kint32max) ||
60      offset > static_cast<size_t>(kint32max)) {
61    return false;
62  }
63
64  int ret = base_file_.Read(offset, static_cast<char*>(buffer), buffer_len);
65  return (static_cast<size_t>(ret) == buffer_len);
66}
67
68bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
69  DCHECK(base_file_.IsValid());
70  if (buffer_len > static_cast<size_t>(kint32max) ||
71      offset > static_cast<size_t>(kint32max)) {
72    return false;
73  }
74
75  int ret = base_file_.Write(offset, static_cast<const char*>(buffer),
76                             buffer_len);
77  return (static_cast<size_t>(ret) == buffer_len);
78}
79
80bool File::Read(void* buffer, size_t buffer_len, size_t offset,
81                FileIOCallback* callback, bool* completed) {
82  DCHECK(base_file_.IsValid());
83  if (!callback) {
84    if (completed)
85      *completed = true;
86    return Read(buffer, buffer_len, offset);
87  }
88
89  if (buffer_len > static_cast<size_t>(kint32max) ||
90      offset > static_cast<size_t>(kint32max)) {
91    return false;
92  }
93
94  base::PostTaskAndReplyWithResult(
95      s_worker_pool.Pointer(), FROM_HERE,
96      base::Bind(&File::DoRead, this, buffer, buffer_len, offset),
97      base::Bind(&File::OnOperationComplete, this, callback));
98
99  *completed = false;
100  return true;
101}
102
103bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
104                 FileIOCallback* callback, bool* completed) {
105  DCHECK(base_file_.IsValid());
106  if (!callback) {
107    if (completed)
108      *completed = true;
109    return Write(buffer, buffer_len, offset);
110  }
111
112  if (buffer_len > static_cast<size_t>(kint32max) ||
113      offset > static_cast<size_t>(kint32max)) {
114    return false;
115  }
116
117  base::PostTaskAndReplyWithResult(
118      s_worker_pool.Pointer(), FROM_HERE,
119      base::Bind(&File::DoWrite, this, buffer, buffer_len, offset),
120      base::Bind(&File::OnOperationComplete, this, callback));
121
122  *completed = false;
123  return true;
124}
125
126bool File::SetLength(size_t length) {
127  DCHECK(base_file_.IsValid());
128  if (length > kuint32max)
129    return false;
130
131  return base_file_.SetLength(length);
132}
133
134size_t File::GetLength() {
135  DCHECK(base_file_.IsValid());
136  int64 len = base_file_.GetLength();
137
138  if (len > static_cast<int64>(kuint32max))
139    return kuint32max;
140
141  return static_cast<size_t>(len);
142}
143
144// Static.
145void File::WaitForPendingIO(int* num_pending_io) {
146  // We are running unit tests so we should wait for all callbacks. Sadly, the
147  // worker pool only waits for tasks on the worker pool, not the "Reply" tasks
148  // so we have to let the current message loop to run.
149  s_worker_pool.Get().FlushForTesting();
150  base::RunLoop().RunUntilIdle();
151}
152
153// Static.
154void File::DropPendingIO() {
155}
156
157
158File::~File() {
159}
160
161base::PlatformFile File::platform_file() const {
162  return base_file_.GetPlatformFile();
163}
164
165// Runs on a worker thread.
166int File::DoRead(void* buffer, size_t buffer_len, size_t offset) {
167  if (Read(const_cast<void*>(buffer), buffer_len, offset))
168    return static_cast<int>(buffer_len);
169
170  return net::ERR_CACHE_READ_FAILURE;
171}
172
173// Runs on a worker thread.
174int File::DoWrite(const void* buffer, size_t buffer_len, size_t offset) {
175  if (Write(const_cast<void*>(buffer), buffer_len, offset))
176    return static_cast<int>(buffer_len);
177
178  return net::ERR_CACHE_WRITE_FAILURE;
179}
180
181// This method actually makes sure that the last reference to the file doesn't
182// go away on the worker pool.
183void File::OnOperationComplete(FileIOCallback* callback, int result) {
184  callback->OnFileIOComplete(result);
185}
186
187}  // namespace disk_cache
188