Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Side by Side Diff: chrome/browser/visitedlink/visitedlink_master.cc

Issue 10800005: Do not open file on UI thread in VisitedLinkMaster (Closed) Base URL: https://src.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/visitedlink/visitedlink_master.h" 5 #include "chrome/browser/visitedlink/visitedlink_master.h"
6 6
7 #if defined(OS_WIN) 7 #if defined(OS_WIN)
8 #include <windows.h> 8 #include <windows.h>
9 #include <io.h> 9 #include <io.h>
10 #include <shlobj.h> 10 #include <shlobj.h>
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 57
58 // Fills the given salt structure with some quasi-random values 58 // Fills the given salt structure with some quasi-random values
59 // It is not necessary to generate a cryptographically strong random string, 59 // It is not necessary to generate a cryptographically strong random string,
60 // only that it be reasonably different for different users. 60 // only that it be reasonably different for different users.
61 void GenerateSalt(uint8 salt[LINK_SALT_LENGTH]) { 61 void GenerateSalt(uint8 salt[LINK_SALT_LENGTH]) {
62 DCHECK_EQ(LINK_SALT_LENGTH, 8) << "This code assumes the length of the salt"; 62 DCHECK_EQ(LINK_SALT_LENGTH, 8) << "This code assumes the length of the salt";
63 uint64 randval = base::RandUint64(); 63 uint64 randval = base::RandUint64();
64 memcpy(salt, &randval, 8); 64 memcpy(salt, &randval, 8);
65 } 65 }
66 66
67 // Opens file on a background thread to not block UI thread.
68 void AsyncOpen(FILE** file, FilePath filename) {
69 *file = OpenFile(filename, "wb+");
70 if (!(*file)) {
71 DLOG(ERROR) << "Failed to open file " << filename.value();
72 }
73 }
74
67 // Returns true if the write was complete. 75 // Returns true if the write was complete.
68 static bool WriteToFile(FILE* file, 76 static bool WriteToFile(FILE* file,
69 off_t offset, 77 off_t offset,
70 const void* data, 78 const void* data,
71 size_t data_len) { 79 size_t data_len) {
72 if (fseek(file, offset, SEEK_SET) != 0) 80 if (fseek(file, offset, SEEK_SET) != 0)
73 return false; // Don't write to an invalid part of the file. 81 return false; // Don't write to an invalid part of the file.
74 82
75 size_t num_written = fwrite(data, 1, data_len, file); 83 size_t num_written = fwrite(data, 1, data_len, file);
76 84
77 // The write may not make it to the kernel (stdlib may buffer the write) 85 // The write may not make it to the kernel (stdlib may buffer the write)
78 // until the next fseek/fclose call. If we crash, it's easy for our used 86 // until the next fseek/fclose call. If we crash, it's easy for our used
79 // item count to be out of sync with the number of hashes we write. 87 // item count to be out of sync with the number of hashes we write.
80 // Protect against this by calling fflush. 88 // Protect against this by calling fflush.
81 int ret = fflush(file); 89 int ret = fflush(file);
82 DCHECK_EQ(0, ret); 90 DCHECK_EQ(0, ret);
83 return num_written == data_len; 91 return num_written == data_len;
84 } 92 }
85 93
86 // This task executes on a background thread and executes a write. This 94 // This task executes on a background thread and executes a write. This
87 // prevents us from blocking the UI thread doing I/O. 95 // prevents us from blocking the UI thread doing I/O. Double pointer to FILE
88 void AsyncWrite(FILE* file, int32 offset, const std::string& data) { 96 // is used because file may still not be opened by the time of scheduling
89 WriteToFile(file, offset, data.data(), data.size()); 97 // the task for execution.
98 void AsyncWrite(FILE** file, int32 offset, const std::string& data) {
99 WriteToFile(*file, offset, data.data(), data.size());
100 }
101
102 // Truncates the file to the current position asynchronously on a background
103 // thread. Double pointer to FILE is used because file may still not be opened
104 // by the time of scheduling the task for execution.
105 void AsyncTruncate(FILE** file) {
106 base::IgnoreResult(TruncateFile(*file));
107 }
108
109 // Closes the file on a background thread and releases memory used for storage
110 // of FILE* value. Double pointer to FILE is used because file may still not
111 // be opened by the time of scheduling the task for execution.
112 void AsyncClose(FILE** file) {
113 base::IgnoreResult(fclose(*file));
114 delete file;
90 } 115 }
91 116
92 } // namespace 117 } // namespace
93 118
94 // TableBuilder --------------------------------------------------------------- 119 // TableBuilder ---------------------------------------------------------------
95 120
96 // How rebuilding from history works 121 // How rebuilding from history works
97 // --------------------------------- 122 // ---------------------------------
98 // 123 //
99 // We mark that we're rebuilding from history by setting the table_builder_ 124 // We mark that we're rebuilding from history by setting the table_builder_
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
170 } 195 }
171 196
172 VisitedLinkMaster::~VisitedLinkMaster() { 197 VisitedLinkMaster::~VisitedLinkMaster() {
173 if (table_builder_.get()) { 198 if (table_builder_.get()) {
174 // Prevent the table builder from calling us back now that we're being 199 // Prevent the table builder from calling us back now that we're being
175 // destroyed. Note that we DON'T delete the object, since the history 200 // destroyed. Note that we DON'T delete the object, since the history
176 // system is still writing into it. When that is complete, the table 201 // system is still writing into it. When that is complete, the table
177 // builder will destroy itself when it finds we are gone. 202 // builder will destroy itself when it finds we are gone.
178 table_builder_->DisownMaster(); 203 table_builder_->DisownMaster();
179 } 204 }
180 FreeURLTable(); 205 FreeURLTable();
brettw 2012/07/27 22:11:14 Can you add a comment here that the file_ pointer
181 } 206 }
182 207
183 void VisitedLinkMaster::InitMembers(Listener* listener, Profile* profile) { 208 void VisitedLinkMaster::InitMembers(Listener* listener, Profile* profile) {
184 DCHECK(listener); 209 DCHECK(listener);
185 210
186 listener_ = listener; 211 listener_ = listener;
187 file_ = NULL; 212 file_ = NULL;
188 shared_memory_ = NULL; 213 shared_memory_ = NULL;
189 shared_memory_serial_ = 0; 214 shared_memory_serial_ = 0;
190 used_items_ = 0; 215 used_items_ = 0;
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after
448 AddFingerprint(shuffled_fingerprints[i], false); 473 AddFingerprint(shuffled_fingerprints[i], false);
449 } 474 }
450 475
451 // Write the affected range to disk [deleted_hash, end_range]. 476 // Write the affected range to disk [deleted_hash, end_range].
452 if (update_file) 477 if (update_file)
453 WriteHashRangeToFile(deleted_hash, end_range); 478 WriteHashRangeToFile(deleted_hash, end_range);
454 479
455 return true; 480 return true;
456 } 481 }
457 482
458 bool VisitedLinkMaster::WriteFullTable() { 483 void VisitedLinkMaster::WriteFullTable() {
459 // This function can get called when the file is open, for example, when we 484 // This function can get called when the file is open, for example, when we
460 // resize the table. We must handle this case and not try to reopen the file, 485 // resize the table. We must handle this case and not try to reopen the file,
461 // since there may be write operations pending on the file I/O thread. 486 // since there may be write operations pending on the file I/O thread.
462 // 487 //
463 // Note that once we start writing, we do not delete on error. This means 488 // Note that once we start writing, we do not delete on error. This means
464 // there can be a partial file, but the short file will be detected next time 489 // there can be a partial file, but the short file will be detected next time
465 // we start, and will be replaced. 490 // we start, and will be replaced.
466 // 491 //
467 // This might possibly get corrupted if we crash in the middle of writing. 492 // This might possibly get corrupted if we crash in the middle of writing.
468 // We should pick up the most common types of these failures when we notice 493 // We should pick up the most common types of these failures when we notice
469 // that the file size is different when we load it back in, and then we will 494 // that the file size is different when we load it back in, and then we will
470 // regenerate the table. 495 // regenerate the table.
471 if (!file_) { 496 if (!file_) {
497 file_ = new FilePtr();
472 FilePath filename; 498 FilePath filename;
473 GetDatabaseFileName(&filename); 499 GetDatabaseFileName(&filename);
474 file_ = OpenFile(filename, "wb+"); 500 PostIOTask(FROM_HERE, base::Bind(&AsyncOpen, file_, filename));
475 if (!file_) {
476 DLOG(ERROR) << "Failed to open file " << filename.value();
477 return false;
478 }
479 } 501 }
480 502
481 // Write the new header. 503 // Write the new header.
482 int32 header[4]; 504 int32 header[4];
483 header[0] = kFileSignature; 505 header[0] = kFileSignature;
484 header[1] = kFileCurrentVersion; 506 header[1] = kFileCurrentVersion;
485 header[2] = table_length_; 507 header[2] = table_length_;
486 header[3] = used_items_; 508 header[3] = used_items_;
487 WriteToFile(file_, 0, header, sizeof(header)); 509 WriteToFile(file_, 0, header, sizeof(header));
488 WriteToFile(file_, sizeof(header), salt_, LINK_SALT_LENGTH); 510 WriteToFile(file_, sizeof(header), salt_, LINK_SALT_LENGTH);
489 511
490 // Write the hash data. 512 // Write the hash data.
491 WriteToFile(file_, kFileHeaderSize, 513 WriteToFile(file_, kFileHeaderSize,
492 hash_table_, table_length_ * sizeof(Fingerprint)); 514 hash_table_, table_length_ * sizeof(Fingerprint));
493 515
494 // The hash table may have shrunk, so make sure this is the end. 516 // The hash table may have shrunk, so make sure this is the end.
495 PostIOTask(FROM_HERE, base::Bind(base::IgnoreResult(&TruncateFile), file_)); 517 PostIOTask(FROM_HERE, base::Bind(&AsyncTruncate, file_));
496 return true;
497 } 518 }
498 519
499 bool VisitedLinkMaster::InitFromFile() { 520 bool VisitedLinkMaster::InitFromFile() {
500 DCHECK(file_ == NULL); 521 DCHECK(file_ == NULL);
501 522
502 FilePath filename; 523 FilePath filename;
503 GetDatabaseFileName(&filename); 524 GetDatabaseFileName(&filename);
504 ScopedFILE file_closer(OpenFile(filename, "rb+")); 525 ScopedFILE file_closer(OpenFile(filename, "rb+"));
505 if (!file_closer.get()) 526 if (!file_closer.get())
506 return false; 527 return false;
507 528
508 int32 num_entries, used_count; 529 int32 num_entries, used_count;
509 if (!ReadFileHeader(file_closer.get(), &num_entries, &used_count, salt_)) 530 if (!ReadFileHeader(file_closer.get(), &num_entries, &used_count, salt_))
510 return false; // Header isn't valid. 531 return false; // Header isn't valid.
511 532
512 // Allocate and read the table. 533 // Allocate and read the table.
513 if (!CreateURLTable(num_entries, false)) 534 if (!CreateURLTable(num_entries, false))
514 return false; 535 return false;
515 if (!ReadFromFile(file_closer.get(), kFileHeaderSize, 536 if (!ReadFromFile(file_closer.get(), kFileHeaderSize,
516 hash_table_, num_entries * sizeof(Fingerprint))) { 537 hash_table_, num_entries * sizeof(Fingerprint))) {
517 FreeURLTable(); 538 FreeURLTable();
518 return false; 539 return false;
519 } 540 }
520 used_items_ = used_count; 541 used_items_ = used_count;
521 542
522 #ifndef NDEBUG 543 #ifndef NDEBUG
523 DebugValidate(); 544 DebugValidate();
524 #endif 545 #endif
525 546
526 file_ = file_closer.release(); 547 file_ = new FilePtr(file_closer.release());
527 return true; 548 return true;
528 } 549 }
529 550
530 bool VisitedLinkMaster::InitFromScratch(bool suppress_rebuild) { 551 bool VisitedLinkMaster::InitFromScratch(bool suppress_rebuild) {
531 int32 table_size = kDefaultTableSize; 552 int32 table_size = kDefaultTableSize;
532 if (table_size_override_) 553 if (table_size_override_)
533 table_size = table_size_override_; 554 table_size = table_size_override_;
534 555
535 // The salt must be generated before the table so that it can be copied to 556 // The salt must be generated before the table so that it can be copied to
536 // the shared memory. 557 // the shared memory.
537 GenerateSalt(salt_); 558 GenerateSalt(salt_);
538 if (!CreateURLTable(table_size, true)) 559 if (!CreateURLTable(table_size, true))
539 return false; 560 return false;
540 561
541 #ifndef NDEBUG 562 #ifndef NDEBUG
542 DebugValidate(); 563 DebugValidate();
543 #endif 564 #endif
544 565
545 if (suppress_rebuild) { 566 if (suppress_rebuild) {
546 // When we disallow rebuilds (normally just unit tests), just use the 567 // When we disallow rebuilds (normally just unit tests), just use the
547 // current empty table. 568 // current empty table.
548 return WriteFullTable(); 569 WriteFullTable();
570 return true;
549 } 571 }
550 572
551 // This will build the table from history. On the first run, history will 573 // This will build the table from history. On the first run, history will
552 // be empty, so this will be correct. This will also write the new table 574 // be empty, so this will be correct. This will also write the new table
553 // to disk. We don't want to save explicitly here, since the rebuild may 575 // to disk. We don't want to save explicitly here, since the rebuild may
554 // not complete, leaving us with an empty but valid visited link database. 576 // not complete, leaving us with an empty but valid visited link database.
555 // In the future, we won't know we need to try rebuilding again. 577 // In the future, we won't know we need to try rebuilding again.
556 return RebuildTableFromHistory(); 578 return RebuildTableFromHistory();
557 } 579 }
558 580
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
675 return true; 697 return true;
676 } 698 }
677 699
678 void VisitedLinkMaster::FreeURLTable() { 700 void VisitedLinkMaster::FreeURLTable() {
679 if (shared_memory_) { 701 if (shared_memory_) {
680 delete shared_memory_; 702 delete shared_memory_;
681 shared_memory_ = NULL; 703 shared_memory_ = NULL;
682 } 704 }
683 if (!file_) 705 if (!file_)
684 return; 706 return;
685 PostIOTask(FROM_HERE, base::Bind(base::IgnoreResult(&fclose), file_)); 707 PostIOTask(FROM_HERE, base::Bind(&AsyncClose, file_));
brettw 2012/07/27 22:11:14 After this call can you null out the file_ pointer
686 } 708 }
687 709
688 bool VisitedLinkMaster::ResizeTableIfNecessary() { 710 bool VisitedLinkMaster::ResizeTableIfNecessary() {
689 DCHECK(table_length_ > 0) << "Must have a table"; 711 DCHECK(table_length_ > 0) << "Must have a table";
690 712
691 // Load limits for good performance/space. We are pretty conservative about 713 // Load limits for good performance/space. We are pretty conservative about
692 // keeping the table not very full. This is because we use linear probing 714 // keeping the table not very full. This is because we use linear probing
693 // which increases the likelihood of clumps of entries which will reduce 715 // which increases the likelihood of clumps of entries which will reduce
694 // performance. 716 // performance.
695 const float max_table_load = 0.5f; // Grow when we're > this full. 717 const float max_table_load = 0.5f; // Grow when we're > this full.
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
830 for (size_t i = 0; i < fingerprints.size(); i++) 852 for (size_t i = 0; i < fingerprints.size(); i++)
831 AddFingerprint(fingerprints[i], false); 853 AddFingerprint(fingerprints[i], false);
832 854
833 // Also add anything that was added while we were asynchronously 855 // Also add anything that was added while we were asynchronously
834 // generating the new table. 856 // generating the new table.
835 for (std::set<Fingerprint>::iterator i = added_since_rebuild_.begin(); 857 for (std::set<Fingerprint>::iterator i = added_since_rebuild_.begin();
836 i != added_since_rebuild_.end(); ++i) 858 i != added_since_rebuild_.end(); ++i)
837 AddFingerprint(*i, false); 859 AddFingerprint(*i, false);
838 added_since_rebuild_.clear(); 860 added_since_rebuild_.clear();
839 861
840 // We shouldn't be writing the table from the main thread!
841 // http://code.google.com/p/chromium/issues/detail?id=24163
842 base::ThreadRestrictions::ScopedAllowIO allow_io;
843
844 // Now handle deletions. 862 // Now handle deletions.
845 DeleteFingerprintsFromCurrentTable(deleted_since_rebuild_); 863 DeleteFingerprintsFromCurrentTable(deleted_since_rebuild_);
846 deleted_since_rebuild_.clear(); 864 deleted_since_rebuild_.clear();
847 865
848 // Send an update notification to all child processes. 866 // Send an update notification to all child processes.
849 listener_->NewTable(shared_memory_); 867 listener_->NewTable(shared_memory_);
850 868
851 WriteFullTable(); 869 WriteFullTable();
852 } 870 }
853 } 871 }
854 table_builder_ = NULL; // Will release our reference to the builder. 872 table_builder_ = NULL; // Will release our reference to the builder.
855 873
856 // Notify the unit test that the rebuild is complete (will be NULL in prod.) 874 // Notify the unit test that the rebuild is complete (will be NULL in prod.)
857 if (!rebuild_complete_task_.is_null()) { 875 if (!rebuild_complete_task_.is_null()) {
858 rebuild_complete_task_.Run(); 876 rebuild_complete_task_.Run();
859 rebuild_complete_task_.Reset(); 877 rebuild_complete_task_.Reset();
860 } 878 }
861 } 879 }
862 880
863 void VisitedLinkMaster::WriteToFile(FILE* file, 881 void VisitedLinkMaster::WriteToFile(FilePtr* file,
864 off_t offset, 882 off_t offset,
865 void* data, 883 void* data,
866 int32 data_size) { 884 int32 data_size) {
867 #ifndef NDEBUG 885 #ifndef NDEBUG
868 posted_asynchronous_operation_ = true; 886 posted_asynchronous_operation_ = true;
869 #endif 887 #endif
870 PostIOTask(FROM_HERE, 888 PostIOTask(FROM_HERE,
871 base::Bind(&AsyncWrite, file, offset, 889 base::Bind(&AsyncWrite, file, offset,
872 std::string(static_cast<const char*>(data), data_size))); 890 std::string(static_cast<const char*>(data), data_size)));
873 } 891 }
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
951 } 969 }
952 970
953 void VisitedLinkMaster::TableBuilder::OnCompleteMainThread() { 971 void VisitedLinkMaster::TableBuilder::OnCompleteMainThread() {
954 if (master_) 972 if (master_)
955 master_->OnTableRebuildComplete(success_, fingerprints_); 973 master_->OnTableRebuildComplete(success_, fingerprints_);
956 974
957 // WILL (generally) DELETE THIS! This balances the AddRef in 975 // WILL (generally) DELETE THIS! This balances the AddRef in
958 // VisitedLinkMaster::RebuildTableFromHistory. 976 // VisitedLinkMaster::RebuildTableFromHistory.
959 Release(); 977 Release();
960 } 978 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698