OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |