http_cache.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/http/http_cache.h"
6
7#include <algorithm>
8
9#include "base/compiler_specific.h"
10
11#if defined(OS_POSIX)
12#include <unistd.h>
13#endif
14
15#include "base/bind.h"
16#include "base/bind_helpers.h"
17#include "base/callback.h"
18#include "base/file_util.h"
19#include "base/format_macros.h"
20#include "base/location.h"
21#include "base/memory/ref_counted.h"
22#include "base/message_loop.h"
23#include "base/metrics/field_trial.h"
24#include "base/pickle.h"
25#include "base/stl_util.h"
26#include "base/string_number_conversions.h"
27#include "base/string_util.h"
28#include "base/stringprintf.h"
29#include "base/threading/worker_pool.h"
30#include "net/base/cache_type.h"
31#include "net/base/io_buffer.h"
32#include "net/base/load_flags.h"
33#include "net/base/net_errors.h"
34#include "net/base/upload_data_stream.h"
35#include "net/disk_cache/disk_cache.h"
36#include "net/http/http_cache_transaction.h"
37#include "net/http/http_network_layer.h"
38#include "net/http/http_network_session.h"
39#include "net/http/http_request_info.h"
40#include "net/http/http_response_headers.h"
41#include "net/http/http_response_info.h"
42#include "net/http/http_util.h"
43
44namespace {
45
46// Adaptor to delete a file on a worker thread.
47void DeletePath(base::FilePath path) {
48  file_util::Delete(path, false);
49}
50
51}  // namespace
52
53namespace net {
54
55HttpCache::DefaultBackend::DefaultBackend(CacheType type,
56                                          BackendType backend_type,
57                                          const base::FilePath& path,
58                                          int max_bytes,
59                                          base::MessageLoopProxy* thread)
60    : type_(type),
61      backend_type_(backend_type),
62      path_(path),
63      max_bytes_(max_bytes),
64      thread_(thread) {
65}
66
67HttpCache::DefaultBackend::~DefaultBackend() {}
68
69// static
70HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) {
71  return new DefaultBackend(MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT,
72                            base::FilePath(), max_bytes, NULL);
73}
74
75int HttpCache::DefaultBackend::CreateBackend(
76    NetLog* net_log, disk_cache::Backend** backend,
77    const CompletionCallback& callback) {
78  DCHECK_GE(max_bytes_, 0);
79  return disk_cache::CreateCacheBackend(type_, backend_type_, path_, max_bytes_,
80                                        true, thread_, net_log, backend,
81                                        callback);
82}
83
84//-----------------------------------------------------------------------------
85
86HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
87    : disk_entry(entry),
88      writer(NULL),
89      will_process_pending_queue(false),
90      doomed(false) {
91}
92
93HttpCache::ActiveEntry::~ActiveEntry() {
94  if (disk_entry) {
95    disk_entry->Close();
96    disk_entry = NULL;
97  }
98}
99
100//-----------------------------------------------------------------------------
101
102// This structure keeps track of work items that are attempting to create or
103// open cache entries or the backend itself.
104struct HttpCache::PendingOp {
105  PendingOp() : disk_entry(NULL), backend(NULL), writer(NULL) {}
106  ~PendingOp() {}
107
108  disk_cache::Entry* disk_entry;
109  disk_cache::Backend* backend;
110  WorkItem* writer;
111  CompletionCallback callback;  // BackendCallback.
112  WorkItemList pending_queue;
113};
114
115//-----------------------------------------------------------------------------
116
117// The type of operation represented by a work item.
118enum WorkItemOperation {
119  WI_CREATE_BACKEND,
120  WI_OPEN_ENTRY,
121  WI_CREATE_ENTRY,
122  WI_DOOM_ENTRY
123};
124
125// A work item encapsulates a single request to the backend with all the
126// information needed to complete that request.
127class HttpCache::WorkItem {
128 public:
129  WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
130      : operation_(operation),
131        trans_(trans),
132        entry_(entry),
133        backend_(NULL) {}
134  WorkItem(WorkItemOperation operation, Transaction* trans,
135           const net::CompletionCallback& cb, disk_cache::Backend** backend)
136      : operation_(operation),
137        trans_(trans),
138        entry_(NULL),
139        callback_(cb),
140        backend_(backend) {}
141  ~WorkItem() {}
142
143  // Calls back the transaction with the result of the operation.
144  void NotifyTransaction(int result, ActiveEntry* entry) {
145    DCHECK(!entry || entry->disk_entry);
146    if (entry_)
147      *entry_ = entry;
148    if (trans_)
149      trans_->io_callback().Run(result);
150  }
151
152  // Notifies the caller about the operation completion. Returns true if the
153  // callback was invoked.
154  bool DoCallback(int result, disk_cache::Backend* backend) {
155    if (backend_)
156      *backend_ = backend;
157    if (!callback_.is_null()) {
158      callback_.Run(result);
159      return true;
160    }
161    return false;
162  }
163
164  WorkItemOperation operation() { return operation_; }
165  void ClearTransaction() { trans_ = NULL; }
166  void ClearEntry() { entry_ = NULL; }
167  void ClearCallback() { callback_.Reset(); }
168  bool Matches(Transaction* trans) const { return trans == trans_; }
169  bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); }
170
171 private:
172  WorkItemOperation operation_;
173  Transaction* trans_;
174  ActiveEntry** entry_;
175  net::CompletionCallback callback_;  // User callback.
176  disk_cache::Backend** backend_;
177};
178
179//-----------------------------------------------------------------------------
180
181// This class encapsulates a transaction whose only purpose is to write metadata
182// to a given entry.
183class HttpCache::MetadataWriter {
184 public:
185  explicit MetadataWriter(HttpCache::Transaction* trans)
186      : transaction_(trans),
187        verified_(false),
188        buf_len_(0) {
189  }
190
191  ~MetadataWriter() {}
192
193  // Implements the bulk of HttpCache::WriteMetadata.
194  void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
195             int buf_len);
196
197 private:
198  void VerifyResponse(int result);
199  void SelfDestroy();
200  void OnIOComplete(int result);
201
202  scoped_ptr<HttpCache::Transaction> transaction_;
203  bool verified_;
204  scoped_refptr<IOBuffer> buf_;
205  int buf_len_;
206  base::Time expected_response_time_;
207  HttpRequestInfo request_info_;
208  DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
209};
210
211void HttpCache::MetadataWriter::Write(const GURL& url,
212                                      base::Time expected_response_time,
213                                      IOBuffer* buf, int buf_len) {
214  DCHECK_GT(buf_len, 0);
215  DCHECK(buf);
216  DCHECK(buf->data());
217  request_info_.url = url;
218  request_info_.method = "GET";
219  request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
220
221  expected_response_time_ = expected_response_time;
222  buf_ = buf;
223  buf_len_ = buf_len;
224  verified_ = false;
225
226  int rv = transaction_->Start(
227      &request_info_,
228      base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)),
229      BoundNetLog());
230  if (rv != ERR_IO_PENDING)
231    VerifyResponse(rv);
232}
233
234void HttpCache::MetadataWriter::VerifyResponse(int result) {
235  verified_ = true;
236  if (result != OK)
237    return SelfDestroy();
238
239  const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
240  DCHECK(response_info->was_cached);
241  if (response_info->response_time != expected_response_time_)
242    return SelfDestroy();
243
244  result = transaction_->WriteMetadata(
245      buf_, buf_len_,
246      base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)));
247  if (result != ERR_IO_PENDING)
248    SelfDestroy();
249}
250
251void HttpCache::MetadataWriter::SelfDestroy() {
252  delete this;
253}
254
255void HttpCache::MetadataWriter::OnIOComplete(int result) {
256  if (!verified_)
257    return VerifyResponse(result);
258  SelfDestroy();
259}
260
261//-----------------------------------------------------------------------------
262
263HttpCache::HttpCache(const net::HttpNetworkSession::Params& params,
264                     BackendFactory* backend_factory)
265    : net_log_(params.net_log),
266      backend_factory_(backend_factory),
267      building_backend_(false),
268      mode_(NORMAL),
269      network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))) {
270}
271
272
273HttpCache::HttpCache(HttpNetworkSession* session,
274                     BackendFactory* backend_factory)
275    : net_log_(session->net_log()),
276      backend_factory_(backend_factory),
277      building_backend_(false),
278      mode_(NORMAL),
279      network_layer_(new HttpNetworkLayer(session)) {
280}
281
282HttpCache::HttpCache(HttpTransactionFactory* network_layer,
283                     NetLog* net_log,
284                     BackendFactory* backend_factory)
285    : net_log_(net_log),
286      backend_factory_(backend_factory),
287      building_backend_(false),
288      mode_(NORMAL),
289      network_layer_(network_layer) {
290}
291
292HttpCache::~HttpCache() {
293  // If we have any active entries remaining, then we need to deactivate them.
294  // We may have some pending calls to OnProcessPendingQueue, but since those
295  // won't run (due to our destruction), we can simply ignore the corresponding
296  // will_process_pending_queue flag.
297  while (!active_entries_.empty()) {
298    ActiveEntry* entry = active_entries_.begin()->second;
299    entry->will_process_pending_queue = false;
300    entry->pending_queue.clear();
301    entry->readers.clear();
302    entry->writer = NULL;
303    DeactivateEntry(entry);
304  }
305
306  STLDeleteElements(&doomed_entries_);
307
308  // Before deleting pending_ops_, we have to make sure that the disk cache is
309  // done with said operations, or it will attempt to use deleted data.
310  disk_cache_.reset();
311
312  PendingOpsMap::iterator pending_it = pending_ops_.begin();
313  for (; pending_it != pending_ops_.end(); ++pending_it) {
314    // We are not notifying the transactions about the cache going away, even
315    // though they are waiting for a callback that will never fire.
316    PendingOp* pending_op = pending_it->second;
317    delete pending_op->writer;
318    bool delete_pending_op = true;
319    if (building_backend_) {
320      // If we don't have a backend, when its construction finishes it will
321      // deliver the callbacks.
322      if (!pending_op->callback.is_null()) {
323        // If not null, the callback will delete the pending operation later.
324        delete_pending_op = false;
325      }
326    } else {
327      pending_op->callback.Reset();
328    }
329
330    STLDeleteElements(&pending_op->pending_queue);
331    if (delete_pending_op)
332      delete pending_op;
333  }
334}
335
336int HttpCache::GetBackend(disk_cache::Backend** backend,
337                          const CompletionCallback& callback) {
338  DCHECK(!callback.is_null());
339
340  if (disk_cache_.get()) {
341    *backend = disk_cache_.get();
342    return OK;
343  }
344
345  return CreateBackend(backend, callback);
346}
347
348disk_cache::Backend* HttpCache::GetCurrentBackend() const {
349  return disk_cache_.get();
350}
351
352// static
353bool HttpCache::ParseResponseInfo(const char* data, int len,
354                                  HttpResponseInfo* response_info,
355                                  bool* response_truncated) {
356  Pickle pickle(data, len);
357  return response_info->InitFromPickle(pickle, response_truncated);
358}
359
360void HttpCache::WriteMetadata(const GURL& url,
361                              RequestPriority priority,
362                              base::Time expected_response_time,
363                              IOBuffer* buf,
364                              int buf_len) {
365  if (!buf_len)
366    return;
367
368  // Do lazy initialization of disk cache if needed.
369  if (!disk_cache_.get()) {
370    // We don't care about the result.
371    CreateBackend(NULL, net::CompletionCallback());
372  }
373
374  HttpCache::Transaction* trans =
375      new HttpCache::Transaction(priority, this, NULL);
376  MetadataWriter* writer = new MetadataWriter(trans);
377
378  // The writer will self destruct when done.
379  writer->Write(url, expected_response_time, buf, buf_len);
380}
381
382void HttpCache::CloseAllConnections() {
383  net::HttpNetworkLayer* network =
384      static_cast<net::HttpNetworkLayer*>(network_layer_.get());
385  HttpNetworkSession* session = network->GetSession();
386  if (session)
387    session->CloseAllConnections();
388  }
389
390void HttpCache::CloseIdleConnections() {
391  net::HttpNetworkLayer* network =
392      static_cast<net::HttpNetworkLayer*>(network_layer_.get());
393  HttpNetworkSession* session = network->GetSession();
394  if (session)
395    session->CloseIdleConnections();
396}
397
398void HttpCache::OnExternalCacheHit(const GURL& url,
399                                   const std::string& http_method) {
400  if (!disk_cache_.get())
401    return;
402
403  HttpRequestInfo request_info;
404  request_info.url = url;
405  request_info.method = http_method;
406  std::string key = GenerateCacheKey(&request_info);
407  disk_cache_->OnExternalCacheHit(key);
408}
409
410void HttpCache::InitializeInfiniteCache(const base::FilePath& path) {
411  if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
412    return;
413  base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true);
414}
415
416int HttpCache::CreateTransaction(RequestPriority priority,
417                                 scoped_ptr<HttpTransaction>* trans,
418                                 HttpTransactionDelegate* delegate) {
419  // Do lazy initialization of disk cache if needed.
420  if (!disk_cache_.get()) {
421    // We don't care about the result.
422    CreateBackend(NULL, net::CompletionCallback());
423  }
424
425  trans->reset(new HttpCache::Transaction(priority, this, delegate));
426  return OK;
427}
428
429HttpCache* HttpCache::GetCache() {
430  return this;
431}
432
433HttpNetworkSession* HttpCache::GetSession() {
434  net::HttpNetworkLayer* network =
435      static_cast<net::HttpNetworkLayer*>(network_layer_.get());
436  return network->GetSession();
437}
438
439//-----------------------------------------------------------------------------
440
441int HttpCache::CreateBackend(disk_cache::Backend** backend,
442                             const net::CompletionCallback& callback) {
443  if (!backend_factory_.get())
444    return ERR_FAILED;
445
446  building_backend_ = true;
447
448  scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
449                                         backend));
450
451  // This is the only operation that we can do that is not related to any given
452  // entry, so we use an empty key for it.
453  PendingOp* pending_op = GetPendingOp(std::string());
454  if (pending_op->writer) {
455    if (!callback.is_null())
456      pending_op->pending_queue.push_back(item.release());
457    return ERR_IO_PENDING;
458  }
459
460  DCHECK(pending_op->pending_queue.empty());
461
462  pending_op->writer = item.release();
463  pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
464                                    AsWeakPtr(), pending_op);
465
466  int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
467                                           pending_op->callback);
468  if (rv != ERR_IO_PENDING) {
469    pending_op->writer->ClearCallback();
470    pending_op->callback.Run(rv);
471  }
472
473  return rv;
474}
475
476int HttpCache::GetBackendForTransaction(Transaction* trans) {
477  if (disk_cache_.get())
478    return OK;
479
480  if (!building_backend_)
481    return ERR_FAILED;
482
483  WorkItem* item = new WorkItem(
484      WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL);
485  PendingOp* pending_op = GetPendingOp(std::string());
486  DCHECK(pending_op->writer);
487  pending_op->pending_queue.push_back(item);
488  return ERR_IO_PENDING;
489}
490
491// Generate a key that can be used inside the cache.
492std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
493  // Strip out the reference, username, and password sections of the URL.
494  std::string url = HttpUtil::SpecForRequest(request->url);
495
496  DCHECK(mode_ != DISABLE);
497  if (mode_ == NORMAL) {
498    // No valid URL can begin with numerals, so we should not have to worry
499    // about collisions with normal URLs.
500    if (request->upload_data_stream &&
501        request->upload_data_stream->identifier()) {
502      url.insert(0, base::StringPrintf(
503          "%" PRId64 "/", request->upload_data_stream->identifier()));
504    }
505    return url;
506  }
507
508  // In playback and record mode, we cache everything.
509
510  // Lazily initialize.
511  if (playback_cache_map_ == NULL)
512    playback_cache_map_.reset(new PlaybackCacheMap());
513
514  // Each time we request an item from the cache, we tag it with a
515  // generation number.  During playback, multiple fetches for the same
516  // item will use the same generation number and pull the proper
517  // instance of an URL from the cache.
518  int generation = 0;
519  DCHECK(playback_cache_map_ != NULL);
520  if (playback_cache_map_->find(url) != playback_cache_map_->end())
521    generation = (*playback_cache_map_)[url];
522  (*playback_cache_map_)[url] = generation + 1;
523
524  // The key into the cache is GENERATION # + METHOD + URL.
525  std::string result = base::IntToString(generation);
526  result.append(request->method);
527  result.append(url);
528  return result;
529}
530
531void HttpCache::DoomActiveEntry(const std::string& key) {
532  ActiveEntriesMap::iterator it = active_entries_.find(key);
533  if (it == active_entries_.end())
534    return;
535
536  // This is not a performance critical operation, this is handling an error
537  // condition so it is OK to look up the entry again.
538  int rv = DoomEntry(key, NULL);
539  DCHECK_EQ(OK, rv);
540}
541
542int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
543  // Need to abandon the ActiveEntry, but any transaction attached to the entry
544  // should not be impacted.  Dooming an entry only means that it will no
545  // longer be returned by FindActiveEntry (and it will also be destroyed once
546  // all consumers are finished with the entry).
547  ActiveEntriesMap::iterator it = active_entries_.find(key);
548  if (it == active_entries_.end()) {
549    DCHECK(trans);
550    return AsyncDoomEntry(key, trans);
551  }
552
553  ActiveEntry* entry = it->second;
554  active_entries_.erase(it);
555
556  // We keep track of doomed entries so that we can ensure that they are
557  // cleaned up properly when the cache is destroyed.
558  doomed_entries_.insert(entry);
559
560  entry->disk_entry->Doom();
561  entry->doomed = true;
562
563  DCHECK(entry->writer || !entry->readers.empty());
564  return OK;
565}
566
567int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
568  WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
569  PendingOp* pending_op = GetPendingOp(key);
570  if (pending_op->writer) {
571    pending_op->pending_queue.push_back(item);
572    return ERR_IO_PENDING;
573  }
574
575  DCHECK(pending_op->pending_queue.empty());
576
577  pending_op->writer = item;
578  pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
579                                    AsWeakPtr(), pending_op);
580
581  int rv = disk_cache_->DoomEntry(key, pending_op->callback);
582  if (rv != ERR_IO_PENDING) {
583    item->ClearTransaction();
584    pending_op->callback.Run(rv);
585  }
586
587  return rv;
588}
589
590void HttpCache::DoomMainEntryForUrl(const GURL& url) {
591  HttpRequestInfo temp_info;
592  temp_info.url = url;
593  temp_info.method = "GET";
594  std::string key = GenerateCacheKey(&temp_info);
595
596  // Defer to DoomEntry if there is an active entry, otherwise call
597  // AsyncDoomEntry without triggering a callback.
598  if (active_entries_.count(key))
599    DoomEntry(key, NULL);
600  else
601    AsyncDoomEntry(key, NULL);
602}
603
604void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
605  DCHECK(entry->doomed);
606  DCHECK(!entry->writer);
607  DCHECK(entry->readers.empty());
608  DCHECK(entry->pending_queue.empty());
609
610  ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
611  DCHECK(it != doomed_entries_.end());
612  doomed_entries_.erase(it);
613
614  delete entry;
615}
616
617HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
618  ActiveEntriesMap::const_iterator it = active_entries_.find(key);
619  return it != active_entries_.end() ? it->second : NULL;
620}
621
622HttpCache::ActiveEntry* HttpCache::ActivateEntry(
623    disk_cache::Entry* disk_entry) {
624  DCHECK(!FindActiveEntry(disk_entry->GetKey()));
625  ActiveEntry* entry = new ActiveEntry(disk_entry);
626  active_entries_[disk_entry->GetKey()] = entry;
627  return entry;
628}
629
630void HttpCache::DeactivateEntry(ActiveEntry* entry) {
631  DCHECK(!entry->will_process_pending_queue);
632  DCHECK(!entry->doomed);
633  DCHECK(!entry->writer);
634  DCHECK(entry->disk_entry);
635  DCHECK(entry->readers.empty());
636  DCHECK(entry->pending_queue.empty());
637
638  std::string key = entry->disk_entry->GetKey();
639  if (key.empty())
640    return SlowDeactivateEntry(entry);
641
642  ActiveEntriesMap::iterator it = active_entries_.find(key);
643  DCHECK(it != active_entries_.end());
644  DCHECK(it->second == entry);
645
646  active_entries_.erase(it);
647  delete entry;
648}
649
650// We don't know this entry's key so we have to find it without it.
651void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
652  for (ActiveEntriesMap::iterator it = active_entries_.begin();
653       it != active_entries_.end(); ++it) {
654    if (it->second == entry) {
655      active_entries_.erase(it);
656      delete entry;
657      break;
658    }
659  }
660}
661
662HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
663  DCHECK(!FindActiveEntry(key));
664
665  PendingOpsMap::const_iterator it = pending_ops_.find(key);
666  if (it != pending_ops_.end())
667    return it->second;
668
669  PendingOp* operation = new PendingOp();
670  pending_ops_[key] = operation;
671  return operation;
672}
673
674void HttpCache::DeletePendingOp(PendingOp* pending_op) {
675  std::string key;
676  if (pending_op->disk_entry)
677    key = pending_op->disk_entry->GetKey();
678
679  if (!key.empty()) {
680    PendingOpsMap::iterator it = pending_ops_.find(key);
681    DCHECK(it != pending_ops_.end());
682    pending_ops_.erase(it);
683  } else {
684    for (PendingOpsMap::iterator it = pending_ops_.begin();
685         it != pending_ops_.end(); ++it) {
686      if (it->second == pending_op) {
687        pending_ops_.erase(it);
688        break;
689      }
690    }
691  }
692  DCHECK(pending_op->pending_queue.empty());
693
694  delete pending_op;
695}
696
697int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
698                         Transaction* trans) {
699  ActiveEntry* active_entry = FindActiveEntry(key);
700  if (active_entry) {
701    *entry = active_entry;
702    return OK;
703  }
704
705  WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
706  PendingOp* pending_op = GetPendingOp(key);
707  if (pending_op->writer) {
708    pending_op->pending_queue.push_back(item);
709    return ERR_IO_PENDING;
710  }
711
712  DCHECK(pending_op->pending_queue.empty());
713
714  pending_op->writer = item;
715  pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
716                                    AsWeakPtr(), pending_op);
717
718  int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
719                                  pending_op->callback);
720  if (rv != ERR_IO_PENDING) {
721    item->ClearTransaction();
722    pending_op->callback.Run(rv);
723  }
724
725  return rv;
726}
727
728int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
729                           Transaction* trans) {
730  if (FindActiveEntry(key)) {
731    return ERR_CACHE_RACE;
732  }
733
734  WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
735  PendingOp* pending_op = GetPendingOp(key);
736  if (pending_op->writer) {
737    pending_op->pending_queue.push_back(item);
738    return ERR_IO_PENDING;
739  }
740
741  DCHECK(pending_op->pending_queue.empty());
742
743  pending_op->writer = item;
744  pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
745                                    AsWeakPtr(), pending_op);
746
747  int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
748                                    pending_op->callback);
749  if (rv != ERR_IO_PENDING) {
750    item->ClearTransaction();
751    pending_op->callback.Run(rv);
752  }
753
754  return rv;
755}
756
757void HttpCache::DestroyEntry(ActiveEntry* entry) {
758  if (entry->doomed) {
759    FinalizeDoomedEntry(entry);
760  } else {
761    DeactivateEntry(entry);
762  }
763}
764
765int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
766  DCHECK(entry);
767  DCHECK(entry->disk_entry);
768
769  // We implement a basic reader/writer lock for the disk cache entry.  If
770  // there is already a writer, then everyone has to wait for the writer to
771  // finish before they can access the cache entry.  There can be multiple
772  // readers.
773  //
774  // NOTE: If the transaction can only write, then the entry should not be in
775  // use (since any existing entry should have already been doomed).
776
777  if (entry->writer || entry->will_process_pending_queue) {
778    entry->pending_queue.push_back(trans);
779    return ERR_IO_PENDING;
780  }
781
782  if (trans->mode() & Transaction::WRITE) {
783    // transaction needs exclusive access to the entry
784    if (entry->readers.empty()) {
785      entry->writer = trans;
786    } else {
787      entry->pending_queue.push_back(trans);
788      return ERR_IO_PENDING;
789    }
790  } else {
791    // transaction needs read access to the entry
792    entry->readers.push_back(trans);
793  }
794
795  // We do this before calling EntryAvailable to force any further calls to
796  // AddTransactionToEntry to add their transaction to the pending queue, which
797  // ensures FIFO ordering.
798  if (!entry->writer && !entry->pending_queue.empty())
799    ProcessPendingQueue(entry);
800
801  return OK;
802}
803
804void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
805                              bool cancel) {
806  // If we already posted a task to move on to the next transaction and this was
807  // the writer, there is nothing to cancel.
808  if (entry->will_process_pending_queue && entry->readers.empty())
809    return;
810
811  if (entry->writer) {
812    DCHECK(trans == entry->writer);
813
814    // Assume there was a failure.
815    bool success = false;
816    if (cancel) {
817      DCHECK(entry->disk_entry);
818      // This is a successful operation in the sense that we want to keep the
819      // entry.
820      success = trans->AddTruncatedFlag();
821      // The previous operation may have deleted the entry.
822      if (!trans->entry())
823        return;
824    }
825    DoneWritingToEntry(entry, success);
826  } else {
827    DoneReadingFromEntry(entry, trans);
828  }
829}
830
831void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
832  DCHECK(entry->readers.empty());
833
834  entry->writer = NULL;
835
836  if (success) {
837    ProcessPendingQueue(entry);
838  } else {
839    DCHECK(!entry->will_process_pending_queue);
840
841    // We failed to create this entry.
842    TransactionList pending_queue;
843    pending_queue.swap(entry->pending_queue);
844
845    entry->disk_entry->Doom();
846    DestroyEntry(entry);
847
848    // We need to do something about these pending entries, which now need to
849    // be added to a new entry.
850    while (!pending_queue.empty()) {
851      // ERR_CACHE_RACE causes the transaction to restart the whole process.
852      pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
853      pending_queue.pop_front();
854    }
855  }
856}
857
858void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
859  DCHECK(!entry->writer);
860
861  TransactionList::iterator it =
862      std::find(entry->readers.begin(), entry->readers.end(), trans);
863  DCHECK(it != entry->readers.end());
864
865  entry->readers.erase(it);
866
867  ProcessPendingQueue(entry);
868}
869
870void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
871  DCHECK(entry->writer);
872  DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
873  DCHECK(entry->readers.empty());
874
875  Transaction* trans = entry->writer;
876
877  entry->writer = NULL;
878  entry->readers.push_back(trans);
879
880  ProcessPendingQueue(entry);
881}
882
883LoadState HttpCache::GetLoadStateForPendingTransaction(
884      const Transaction* trans) {
885  ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
886  if (i == active_entries_.end()) {
887    // If this is really a pending transaction, and it is not part of
888    // active_entries_, we should be creating the backend or the entry.
889    return LOAD_STATE_WAITING_FOR_CACHE;
890  }
891
892  Transaction* writer = i->second->writer;
893  return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
894}
895
896void HttpCache::RemovePendingTransaction(Transaction* trans) {
897  ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
898  bool found = false;
899  if (i != active_entries_.end())
900    found = RemovePendingTransactionFromEntry(i->second, trans);
901
902  if (found)
903    return;
904
905  if (building_backend_) {
906    PendingOpsMap::const_iterator j = pending_ops_.find(std::string());
907    if (j != pending_ops_.end())
908      found = RemovePendingTransactionFromPendingOp(j->second, trans);
909
910    if (found)
911      return;
912  }
913
914  PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
915  if (j != pending_ops_.end())
916    found = RemovePendingTransactionFromPendingOp(j->second, trans);
917
918  if (found)
919    return;
920
921  ActiveEntriesSet::iterator k = doomed_entries_.begin();
922  for (; k != doomed_entries_.end() && !found; ++k)
923    found = RemovePendingTransactionFromEntry(*k, trans);
924
925  DCHECK(found) << "Pending transaction not found";
926}
927
928bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
929                                                  Transaction* trans) {
930  TransactionList& pending_queue = entry->pending_queue;
931
932  TransactionList::iterator j =
933      find(pending_queue.begin(), pending_queue.end(), trans);
934  if (j == pending_queue.end())
935    return false;
936
937  pending_queue.erase(j);
938  return true;
939}
940
941bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
942                                                      Transaction* trans) {
943  if (pending_op->writer->Matches(trans)) {
944    pending_op->writer->ClearTransaction();
945    pending_op->writer->ClearEntry();
946    return true;
947  }
948  WorkItemList& pending_queue = pending_op->pending_queue;
949
950  WorkItemList::iterator it = pending_queue.begin();
951  for (; it != pending_queue.end(); ++it) {
952    if ((*it)->Matches(trans)) {
953      delete *it;
954      pending_queue.erase(it);
955      return true;
956    }
957  }
958  return false;
959}
960
961void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
962  // Multiple readers may finish with an entry at once, so we want to batch up
963  // calls to OnProcessPendingQueue.  This flag also tells us that we should
964  // not delete the entry before OnProcessPendingQueue runs.
965  if (entry->will_process_pending_queue)
966    return;
967  entry->will_process_pending_queue = true;
968
969  base::MessageLoop::current()->PostTask(
970      FROM_HERE,
971      base::Bind(&HttpCache::OnProcessPendingQueue, AsWeakPtr(), entry));
972}
973
974void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
975  entry->will_process_pending_queue = false;
976  DCHECK(!entry->writer);
977
978  // If no one is interested in this entry, then we can deactivate it.
979  if (entry->pending_queue.empty()) {
980    if (entry->readers.empty())
981      DestroyEntry(entry);
982    return;
983  }
984
985  // Promote next transaction from the pending queue.
986  Transaction* next = entry->pending_queue.front();
987  if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
988    return;  // Have to wait.
989
990  entry->pending_queue.erase(entry->pending_queue.begin());
991
992  int rv = AddTransactionToEntry(entry, next);
993  if (rv != ERR_IO_PENDING) {
994    next->io_callback().Run(rv);
995  }
996}
997
998void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
999  WorkItemOperation op = pending_op->writer->operation();
1000
1001  // Completing the creation of the backend is simpler than the other cases.
1002  if (op == WI_CREATE_BACKEND)
1003    return OnBackendCreated(result, pending_op);
1004
1005  scoped_ptr<WorkItem> item(pending_op->writer);
1006  bool fail_requests = false;
1007
1008  ActiveEntry* entry = NULL;
1009  std::string key;
1010  if (result == OK) {
1011    if (op == WI_DOOM_ENTRY) {
1012      // Anything after a Doom has to be restarted.
1013      fail_requests = true;
1014    } else if (item->IsValid()) {
1015      key = pending_op->disk_entry->GetKey();
1016      entry = ActivateEntry(pending_op->disk_entry);
1017    } else {
1018      // The writer transaction is gone.
1019      if (op == WI_CREATE_ENTRY)
1020        pending_op->disk_entry->Doom();
1021      pending_op->disk_entry->Close();
1022      pending_op->disk_entry = NULL;
1023      fail_requests = true;
1024    }
1025  }
1026
1027  // We are about to notify a bunch of transactions, and they may decide to
1028  // re-issue a request (or send a different one). If we don't delete
1029  // pending_op, the new request will be appended to the end of the list, and
1030  // we'll see it again from this point before it has a chance to complete (and
1031  // we'll be messing out the request order). The down side is that if for some
1032  // reason notifying request A ends up cancelling request B (for the same key),
1033  // we won't find request B anywhere (because it would be in a local variable
1034  // here) and that's bad. If there is a chance for that to happen, we'll have
1035  // to move the callback used to be a CancelableCallback. By the way, for this
1036  // to happen the action (to cancel B) has to be synchronous to the
1037  // notification for request A.
1038  WorkItemList pending_items;
1039  pending_items.swap(pending_op->pending_queue);
1040  DeletePendingOp(pending_op);
1041
1042  item->NotifyTransaction(result, entry);
1043
1044  while (!pending_items.empty()) {
1045    item.reset(pending_items.front());
1046    pending_items.pop_front();
1047
1048    if (item->operation() == WI_DOOM_ENTRY) {
1049      // A queued doom request is always a race.
1050      fail_requests = true;
1051    } else if (result == OK) {
1052      entry = FindActiveEntry(key);
1053      if (!entry)
1054        fail_requests = true;
1055    }
1056
1057    if (fail_requests) {
1058      item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1059      continue;
1060    }
1061
1062    if (item->operation() == WI_CREATE_ENTRY) {
1063      if (result == OK) {
1064        // A second Create request, but the first request succeeded.
1065        item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1066      } else {
1067        if (op != WI_CREATE_ENTRY) {
1068          // Failed Open followed by a Create.
1069          item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1070          fail_requests = true;
1071        } else {
1072          item->NotifyTransaction(result, entry);
1073        }
1074      }
1075    } else {
1076      if (op == WI_CREATE_ENTRY && result != OK) {
1077        // Failed Create followed by an Open.
1078        item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1079        fail_requests = true;
1080      } else {
1081        item->NotifyTransaction(result, entry);
1082      }
1083    }
1084  }
1085}
1086
1087// static
1088void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1089                                    PendingOp* pending_op,
1090                                    int rv) {
1091  if (cache.get()) {
1092    cache->OnIOComplete(rv, pending_op);
1093  } else {
1094    // The callback was cancelled so we should delete the pending_op that
1095    // was used with this callback.
1096    delete pending_op;
1097  }
1098}
1099
1100void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1101  scoped_ptr<WorkItem> item(pending_op->writer);
1102  WorkItemOperation op = item->operation();
1103  DCHECK_EQ(WI_CREATE_BACKEND, op);
1104
1105  // We don't need the callback anymore.
1106  pending_op->callback.Reset();
1107  disk_cache::Backend* backend = pending_op->backend;
1108
1109  if (backend_factory_.get()) {
1110    // We may end up calling OnBackendCreated multiple times if we have pending
1111    // work items. The first call saves the backend and releases the factory,
1112    // and the last call clears building_backend_.
1113    backend_factory_.reset();  // Reclaim memory.
1114    if (result == OK)
1115      disk_cache_.reset(backend);
1116  }
1117
1118  if (!pending_op->pending_queue.empty()) {
1119    WorkItem* pending_item = pending_op->pending_queue.front();
1120    pending_op->pending_queue.pop_front();
1121    DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1122
1123    // We want to process a single callback at a time, because the cache may
1124    // go away from the callback.
1125    pending_op->writer = pending_item;
1126
1127    base::MessageLoop::current()->PostTask(
1128        FROM_HERE,
1129        base::Bind(
1130            &HttpCache::OnBackendCreated, AsWeakPtr(), result, pending_op));
1131  } else {
1132    building_backend_ = false;
1133    DeletePendingOp(pending_op);
1134  }
1135
1136  // The cache may be gone when we return from the callback.
1137  if (!item->DoCallback(result, backend))
1138    item->NotifyTransaction(result, NULL);
1139}
1140
1141}  // namespace net
1142