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)) { | |
brettw
2012/07/31 21:22:46
Can you just write DLOG_IF(ERROR, !(*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 free(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 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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(); |
206 // FreeURLTable() will schedule closing of the file and deletion of |file_|. | |
207 // So nothing should be done here. | |
181 } | 208 } |
182 | 209 |
183 void VisitedLinkMaster::InitMembers(Listener* listener, Profile* profile) { | 210 void VisitedLinkMaster::InitMembers(Listener* listener, Profile* profile) { |
184 DCHECK(listener); | 211 DCHECK(listener); |
185 | 212 |
186 listener_ = listener; | 213 listener_ = listener; |
187 file_ = NULL; | 214 file_ = NULL; |
188 shared_memory_ = NULL; | 215 shared_memory_ = NULL; |
189 shared_memory_serial_ = 0; | 216 shared_memory_serial_ = 0; |
190 used_items_ = 0; | 217 used_items_ = 0; |
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
448 AddFingerprint(shuffled_fingerprints[i], false); | 475 AddFingerprint(shuffled_fingerprints[i], false); |
449 } | 476 } |
450 | 477 |
451 // Write the affected range to disk [deleted_hash, end_range]. | 478 // Write the affected range to disk [deleted_hash, end_range]. |
452 if (update_file) | 479 if (update_file) |
453 WriteHashRangeToFile(deleted_hash, end_range); | 480 WriteHashRangeToFile(deleted_hash, end_range); |
454 | 481 |
455 return true; | 482 return true; |
456 } | 483 } |
457 | 484 |
458 bool VisitedLinkMaster::WriteFullTable() { | 485 void VisitedLinkMaster::WriteFullTable() { |
459 // This function can get called when the file is open, for example, when we | 486 // 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, | 487 // 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. | 488 // since there may be write operations pending on the file I/O thread. |
462 // | 489 // |
463 // Note that once we start writing, we do not delete on error. This means | 490 // 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 | 491 // there can be a partial file, but the short file will be detected next time |
465 // we start, and will be replaced. | 492 // we start, and will be replaced. |
466 // | 493 // |
467 // This might possibly get corrupted if we crash in the middle of writing. | 494 // 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 | 495 // 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 | 496 // that the file size is different when we load it back in, and then we will |
470 // regenerate the table. | 497 // regenerate the table. |
471 if (!file_) { | 498 if (!file_) { |
499 file_ = static_cast<FILE**>(calloc(1, sizeof(*file_))); | |
472 FilePath filename; | 500 FilePath filename; |
473 GetDatabaseFileName(&filename); | 501 GetDatabaseFileName(&filename); |
474 file_ = OpenFile(filename, "wb+"); | 502 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 } | 503 } |
480 | 504 |
481 // Write the new header. | 505 // Write the new header. |
482 int32 header[4]; | 506 int32 header[4]; |
483 header[0] = kFileSignature; | 507 header[0] = kFileSignature; |
484 header[1] = kFileCurrentVersion; | 508 header[1] = kFileCurrentVersion; |
485 header[2] = table_length_; | 509 header[2] = table_length_; |
486 header[3] = used_items_; | 510 header[3] = used_items_; |
487 WriteToFile(file_, 0, header, sizeof(header)); | 511 WriteToFile(file_, 0, header, sizeof(header)); |
488 WriteToFile(file_, sizeof(header), salt_, LINK_SALT_LENGTH); | 512 WriteToFile(file_, sizeof(header), salt_, LINK_SALT_LENGTH); |
489 | 513 |
490 // Write the hash data. | 514 // Write the hash data. |
491 WriteToFile(file_, kFileHeaderSize, | 515 WriteToFile(file_, kFileHeaderSize, |
492 hash_table_, table_length_ * sizeof(Fingerprint)); | 516 hash_table_, table_length_ * sizeof(Fingerprint)); |
493 | 517 |
494 // The hash table may have shrunk, so make sure this is the end. | 518 // The hash table may have shrunk, so make sure this is the end. |
495 PostIOTask(FROM_HERE, base::Bind(base::IgnoreResult(&TruncateFile), file_)); | 519 PostIOTask(FROM_HERE, base::Bind(&AsyncTruncate, file_)); |
496 return true; | |
497 } | 520 } |
498 | 521 |
499 bool VisitedLinkMaster::InitFromFile() { | 522 bool VisitedLinkMaster::InitFromFile() { |
500 DCHECK(file_ == NULL); | 523 DCHECK(file_ == NULL); |
501 | 524 |
502 FilePath filename; | 525 FilePath filename; |
503 GetDatabaseFileName(&filename); | 526 GetDatabaseFileName(&filename); |
504 ScopedFILE file_closer(OpenFile(filename, "rb+")); | 527 ScopedFILE file_closer(OpenFile(filename, "rb+")); |
505 if (!file_closer.get()) | 528 if (!file_closer.get()) |
506 return false; | 529 return false; |
507 | 530 |
508 int32 num_entries, used_count; | 531 int32 num_entries, used_count; |
509 if (!ReadFileHeader(file_closer.get(), &num_entries, &used_count, salt_)) | 532 if (!ReadFileHeader(file_closer.get(), &num_entries, &used_count, salt_)) |
510 return false; // Header isn't valid. | 533 return false; // Header isn't valid. |
511 | 534 |
512 // Allocate and read the table. | 535 // Allocate and read the table. |
513 if (!CreateURLTable(num_entries, false)) | 536 if (!CreateURLTable(num_entries, false)) |
514 return false; | 537 return false; |
515 if (!ReadFromFile(file_closer.get(), kFileHeaderSize, | 538 if (!ReadFromFile(file_closer.get(), kFileHeaderSize, |
516 hash_table_, num_entries * sizeof(Fingerprint))) { | 539 hash_table_, num_entries * sizeof(Fingerprint))) { |
517 FreeURLTable(); | 540 FreeURLTable(); |
518 return false; | 541 return false; |
519 } | 542 } |
520 used_items_ = used_count; | 543 used_items_ = used_count; |
521 | 544 |
522 #ifndef NDEBUG | 545 #ifndef NDEBUG |
523 DebugValidate(); | 546 DebugValidate(); |
524 #endif | 547 #endif |
525 | 548 |
526 file_ = file_closer.release(); | 549 file_ = static_cast<FILE**>(malloc(sizeof(*file_))); |
550 *file_ = file_closer.release(); | |
527 return true; | 551 return true; |
528 } | 552 } |
529 | 553 |
530 bool VisitedLinkMaster::InitFromScratch(bool suppress_rebuild) { | 554 bool VisitedLinkMaster::InitFromScratch(bool suppress_rebuild) { |
531 int32 table_size = kDefaultTableSize; | 555 int32 table_size = kDefaultTableSize; |
532 if (table_size_override_) | 556 if (table_size_override_) |
533 table_size = table_size_override_; | 557 table_size = table_size_override_; |
534 | 558 |
535 // The salt must be generated before the table so that it can be copied to | 559 // The salt must be generated before the table so that it can be copied to |
536 // the shared memory. | 560 // the shared memory. |
537 GenerateSalt(salt_); | 561 GenerateSalt(salt_); |
538 if (!CreateURLTable(table_size, true)) | 562 if (!CreateURLTable(table_size, true)) |
539 return false; | 563 return false; |
540 | 564 |
541 #ifndef NDEBUG | 565 #ifndef NDEBUG |
542 DebugValidate(); | 566 DebugValidate(); |
543 #endif | 567 #endif |
544 | 568 |
545 if (suppress_rebuild) { | 569 if (suppress_rebuild) { |
546 // When we disallow rebuilds (normally just unit tests), just use the | 570 // When we disallow rebuilds (normally just unit tests), just use the |
547 // current empty table. | 571 // current empty table. |
548 return WriteFullTable(); | 572 WriteFullTable(); |
573 return true; | |
549 } | 574 } |
550 | 575 |
551 // This will build the table from history. On the first run, history will | 576 // 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 | 577 // 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 | 578 // 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. | 579 // 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. | 580 // In the future, we won't know we need to try rebuilding again. |
556 return RebuildTableFromHistory(); | 581 return RebuildTableFromHistory(); |
557 } | 582 } |
558 | 583 |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
675 return true; | 700 return true; |
676 } | 701 } |
677 | 702 |
678 void VisitedLinkMaster::FreeURLTable() { | 703 void VisitedLinkMaster::FreeURLTable() { |
679 if (shared_memory_) { | 704 if (shared_memory_) { |
680 delete shared_memory_; | 705 delete shared_memory_; |
681 shared_memory_ = NULL; | 706 shared_memory_ = NULL; |
682 } | 707 } |
683 if (!file_) | 708 if (!file_) |
684 return; | 709 return; |
685 PostIOTask(FROM_HERE, base::Bind(base::IgnoreResult(&fclose), file_)); | 710 PostIOTask(FROM_HERE, base::Bind(&AsyncClose, file_)); |
711 // AsyncClose() will close the file and free the memory pointed by |file_|. | |
712 file_ = NULL; | |
686 } | 713 } |
687 | 714 |
688 bool VisitedLinkMaster::ResizeTableIfNecessary() { | 715 bool VisitedLinkMaster::ResizeTableIfNecessary() { |
689 DCHECK(table_length_ > 0) << "Must have a table"; | 716 DCHECK(table_length_ > 0) << "Must have a table"; |
690 | 717 |
691 // Load limits for good performance/space. We are pretty conservative about | 718 // 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 | 719 // 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 | 720 // which increases the likelihood of clumps of entries which will reduce |
694 // performance. | 721 // performance. |
695 const float max_table_load = 0.5f; // Grow when we're > this full. | 722 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++) | 857 for (size_t i = 0; i < fingerprints.size(); i++) |
831 AddFingerprint(fingerprints[i], false); | 858 AddFingerprint(fingerprints[i], false); |
832 | 859 |
833 // Also add anything that was added while we were asynchronously | 860 // Also add anything that was added while we were asynchronously |
834 // generating the new table. | 861 // generating the new table. |
835 for (std::set<Fingerprint>::iterator i = added_since_rebuild_.begin(); | 862 for (std::set<Fingerprint>::iterator i = added_since_rebuild_.begin(); |
836 i != added_since_rebuild_.end(); ++i) | 863 i != added_since_rebuild_.end(); ++i) |
837 AddFingerprint(*i, false); | 864 AddFingerprint(*i, false); |
838 added_since_rebuild_.clear(); | 865 added_since_rebuild_.clear(); |
839 | 866 |
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. | 867 // Now handle deletions. |
845 DeleteFingerprintsFromCurrentTable(deleted_since_rebuild_); | 868 DeleteFingerprintsFromCurrentTable(deleted_since_rebuild_); |
846 deleted_since_rebuild_.clear(); | 869 deleted_since_rebuild_.clear(); |
847 | 870 |
848 // Send an update notification to all child processes. | 871 // Send an update notification to all child processes. |
849 listener_->NewTable(shared_memory_); | 872 listener_->NewTable(shared_memory_); |
850 | 873 |
851 WriteFullTable(); | 874 WriteFullTable(); |
852 } | 875 } |
853 } | 876 } |
854 table_builder_ = NULL; // Will release our reference to the builder. | 877 table_builder_ = NULL; // Will release our reference to the builder. |
855 | 878 |
856 // Notify the unit test that the rebuild is complete (will be NULL in prod.) | 879 // Notify the unit test that the rebuild is complete (will be NULL in prod.) |
857 if (!rebuild_complete_task_.is_null()) { | 880 if (!rebuild_complete_task_.is_null()) { |
858 rebuild_complete_task_.Run(); | 881 rebuild_complete_task_.Run(); |
859 rebuild_complete_task_.Reset(); | 882 rebuild_complete_task_.Reset(); |
860 } | 883 } |
861 } | 884 } |
862 | 885 |
863 void VisitedLinkMaster::WriteToFile(FILE* file, | 886 void VisitedLinkMaster::WriteToFile(FILE** file, |
864 off_t offset, | 887 off_t offset, |
865 void* data, | 888 void* data, |
866 int32 data_size) { | 889 int32 data_size) { |
867 #ifndef NDEBUG | 890 #ifndef NDEBUG |
868 posted_asynchronous_operation_ = true; | 891 posted_asynchronous_operation_ = true; |
869 #endif | 892 #endif |
870 PostIOTask(FROM_HERE, | 893 PostIOTask(FROM_HERE, |
871 base::Bind(&AsyncWrite, file, offset, | 894 base::Bind(&AsyncWrite, file, offset, |
872 std::string(static_cast<const char*>(data), data_size))); | 895 std::string(static_cast<const char*>(data), data_size))); |
873 } | 896 } |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
951 } | 974 } |
952 | 975 |
953 void VisitedLinkMaster::TableBuilder::OnCompleteMainThread() { | 976 void VisitedLinkMaster::TableBuilder::OnCompleteMainThread() { |
954 if (master_) | 977 if (master_) |
955 master_->OnTableRebuildComplete(success_, fingerprints_); | 978 master_->OnTableRebuildComplete(success_, fingerprints_); |
956 | 979 |
957 // WILL (generally) DELETE THIS! This balances the AddRef in | 980 // WILL (generally) DELETE THIS! This balances the AddRef in |
958 // VisitedLinkMaster::RebuildTableFromHistory. | 981 // VisitedLinkMaster::RebuildTableFromHistory. |
959 Release(); | 982 Release(); |
960 } | 983 } |
OLD | NEW |