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