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