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

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, 4 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
« no previous file with comments | « chrome/browser/visitedlink/visitedlink_master.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 DLOG_IF(ERROR, !(*file)) << "Failed to open file " << filename.value();
71 }
72
67 // Returns true if the write was complete. 73 // Returns true if the write was complete.
68 static bool WriteToFile(FILE* file, 74 static bool WriteToFile(FILE* file,
69 off_t offset, 75 off_t offset,
70 const void* data, 76 const void* data,
71 size_t data_len) { 77 size_t data_len) {
72 if (fseek(file, offset, SEEK_SET) != 0) 78 if (fseek(file, offset, SEEK_SET) != 0)
73 return false; // Don't write to an invalid part of the file. 79 return false; // Don't write to an invalid part of the file.
74 80
75 size_t num_written = fwrite(data, 1, data_len, file); 81 size_t num_written = fwrite(data, 1, data_len, file);
76 82
77 // The write may not make it to the kernel (stdlib may buffer the write) 83 // 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 84 // 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. 85 // item count to be out of sync with the number of hashes we write.
80 // Protect against this by calling fflush. 86 // Protect against this by calling fflush.
81 int ret = fflush(file); 87 int ret = fflush(file);
82 DCHECK_EQ(0, ret); 88 DCHECK_EQ(0, ret);
83 return num_written == data_len; 89 return num_written == data_len;
84 } 90 }
85 91
86 // This task executes on a background thread and executes a write. This 92 // This task executes on a background thread and executes a write. This
87 // prevents us from blocking the UI thread doing I/O. 93 // 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) { 94 // is used because file may still not be opened by the time of scheduling
89 WriteToFile(file, offset, data.data(), data.size()); 95 // the task for execution.
96 void AsyncWrite(FILE** file, int32 offset, const std::string& data) {
97 WriteToFile(*file, offset, data.data(), data.size());
98 }
99
100 // Truncates the file to the current position asynchronously on a background
101 // thread. Double pointer to FILE is used because file may still not be opened
102 // by the time of scheduling the task for execution.
103 void AsyncTruncate(FILE** file) {
104 base::IgnoreResult(TruncateFile(*file));
105 }
106
107 // Closes the file on a background thread and releases memory used for storage
108 // of FILE* value. Double pointer to FILE is used because file may still not
109 // be opened by the time of scheduling the task for execution.
110 void AsyncClose(FILE** file) {
111 base::IgnoreResult(fclose(*file));
112 free(file);
90 } 113 }
91 114
92 } // namespace 115 } // namespace
93 116
94 // TableBuilder --------------------------------------------------------------- 117 // TableBuilder ---------------------------------------------------------------
95 118
96 // How rebuilding from history works 119 // How rebuilding from history works
97 // --------------------------------- 120 // ---------------------------------
98 // 121 //
99 // We mark that we're rebuilding from history by setting the table_builder_ 122 // 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
171 194
172 VisitedLinkMaster::~VisitedLinkMaster() { 195 VisitedLinkMaster::~VisitedLinkMaster() {
173 if (table_builder_.get()) { 196 if (table_builder_.get()) {
174 // Prevent the table builder from calling us back now that we're being 197 // 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 198 // 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 199 // system is still writing into it. When that is complete, the table
177 // builder will destroy itself when it finds we are gone. 200 // builder will destroy itself when it finds we are gone.
178 table_builder_->DisownMaster(); 201 table_builder_->DisownMaster();
179 } 202 }
180 FreeURLTable(); 203 FreeURLTable();
204 // FreeURLTable() will schedule closing of the file and deletion of |file_|.
205 // So nothing should be done here.
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_ = static_cast<FILE**>(calloc(1, sizeof(*file_)));
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_ = static_cast<FILE**>(malloc(sizeof(*file_)));
548 *file_ = file_closer.release();
527 return true; 549 return true;
528 } 550 }
529 551
530 bool VisitedLinkMaster::InitFromScratch(bool suppress_rebuild) { 552 bool VisitedLinkMaster::InitFromScratch(bool suppress_rebuild) {
531 int32 table_size = kDefaultTableSize; 553 int32 table_size = kDefaultTableSize;
532 if (table_size_override_) 554 if (table_size_override_)
533 table_size = table_size_override_; 555 table_size = table_size_override_;
534 556
535 // The salt must be generated before the table so that it can be copied to 557 // The salt must be generated before the table so that it can be copied to
536 // the shared memory. 558 // the shared memory.
537 GenerateSalt(salt_); 559 GenerateSalt(salt_);
538 if (!CreateURLTable(table_size, true)) 560 if (!CreateURLTable(table_size, true))
539 return false; 561 return false;
540 562
541 #ifndef NDEBUG 563 #ifndef NDEBUG
542 DebugValidate(); 564 DebugValidate();
543 #endif 565 #endif
544 566
545 if (suppress_rebuild) { 567 if (suppress_rebuild) {
546 // When we disallow rebuilds (normally just unit tests), just use the 568 // When we disallow rebuilds (normally just unit tests), just use the
547 // current empty table. 569 // current empty table.
548 return WriteFullTable(); 570 WriteFullTable();
571 return true;
549 } 572 }
550 573
551 // This will build the table from history. On the first run, history will 574 // 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 575 // 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 576 // 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. 577 // 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. 578 // In the future, we won't know we need to try rebuilding again.
556 return RebuildTableFromHistory(); 579 return RebuildTableFromHistory();
557 } 580 }
558 581
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
675 return true; 698 return true;
676 } 699 }
677 700
678 void VisitedLinkMaster::FreeURLTable() { 701 void VisitedLinkMaster::FreeURLTable() {
679 if (shared_memory_) { 702 if (shared_memory_) {
680 delete shared_memory_; 703 delete shared_memory_;
681 shared_memory_ = NULL; 704 shared_memory_ = NULL;
682 } 705 }
683 if (!file_) 706 if (!file_)
684 return; 707 return;
685 PostIOTask(FROM_HERE, base::Bind(base::IgnoreResult(&fclose), file_)); 708 PostIOTask(FROM_HERE, base::Bind(&AsyncClose, file_));
709 // AsyncClose() will close the file and free the memory pointed by |file_|.
710 file_ = NULL;
686 } 711 }
687 712
688 bool VisitedLinkMaster::ResizeTableIfNecessary() { 713 bool VisitedLinkMaster::ResizeTableIfNecessary() {
689 DCHECK(table_length_ > 0) << "Must have a table"; 714 DCHECK(table_length_ > 0) << "Must have a table";
690 715
691 // Load limits for good performance/space. We are pretty conservative about 716 // 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 717 // 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 718 // which increases the likelihood of clumps of entries which will reduce
694 // performance. 719 // performance.
695 const float max_table_load = 0.5f; // Grow when we're > this full. 720 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++) 855 for (size_t i = 0; i < fingerprints.size(); i++)
831 AddFingerprint(fingerprints[i], false); 856 AddFingerprint(fingerprints[i], false);
832 857
833 // Also add anything that was added while we were asynchronously 858 // Also add anything that was added while we were asynchronously
834 // generating the new table. 859 // generating the new table.
835 for (std::set<Fingerprint>::iterator i = added_since_rebuild_.begin(); 860 for (std::set<Fingerprint>::iterator i = added_since_rebuild_.begin();
836 i != added_since_rebuild_.end(); ++i) 861 i != added_since_rebuild_.end(); ++i)
837 AddFingerprint(*i, false); 862 AddFingerprint(*i, false);
838 added_since_rebuild_.clear(); 863 added_since_rebuild_.clear();
839 864
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. 865 // Now handle deletions.
845 DeleteFingerprintsFromCurrentTable(deleted_since_rebuild_); 866 DeleteFingerprintsFromCurrentTable(deleted_since_rebuild_);
846 deleted_since_rebuild_.clear(); 867 deleted_since_rebuild_.clear();
847 868
848 // Send an update notification to all child processes. 869 // Send an update notification to all child processes.
849 listener_->NewTable(shared_memory_); 870 listener_->NewTable(shared_memory_);
850 871
851 WriteFullTable(); 872 WriteFullTable();
852 } 873 }
853 } 874 }
854 table_builder_ = NULL; // Will release our reference to the builder. 875 table_builder_ = NULL; // Will release our reference to the builder.
855 876
856 // Notify the unit test that the rebuild is complete (will be NULL in prod.) 877 // Notify the unit test that the rebuild is complete (will be NULL in prod.)
857 if (!rebuild_complete_task_.is_null()) { 878 if (!rebuild_complete_task_.is_null()) {
858 rebuild_complete_task_.Run(); 879 rebuild_complete_task_.Run();
859 rebuild_complete_task_.Reset(); 880 rebuild_complete_task_.Reset();
860 } 881 }
861 } 882 }
862 883
863 void VisitedLinkMaster::WriteToFile(FILE* file, 884 void VisitedLinkMaster::WriteToFile(FILE** file,
864 off_t offset, 885 off_t offset,
865 void* data, 886 void* data,
866 int32 data_size) { 887 int32 data_size) {
867 #ifndef NDEBUG 888 #ifndef NDEBUG
868 posted_asynchronous_operation_ = true; 889 posted_asynchronous_operation_ = true;
869 #endif 890 #endif
870 PostIOTask(FROM_HERE, 891 PostIOTask(FROM_HERE,
871 base::Bind(&AsyncWrite, file, offset, 892 base::Bind(&AsyncWrite, file, offset,
872 std::string(static_cast<const char*>(data), data_size))); 893 std::string(static_cast<const char*>(data), data_size)));
873 } 894 }
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
951 } 972 }
952 973
953 void VisitedLinkMaster::TableBuilder::OnCompleteMainThread() { 974 void VisitedLinkMaster::TableBuilder::OnCompleteMainThread() {
954 if (master_) 975 if (master_)
955 master_->OnTableRebuildComplete(success_, fingerprints_); 976 master_->OnTableRebuildComplete(success_, fingerprints_);
956 977
957 // WILL (generally) DELETE THIS! This balances the AddRef in 978 // WILL (generally) DELETE THIS! This balances the AddRef in
958 // VisitedLinkMaster::RebuildTableFromHistory. 979 // VisitedLinkMaster::RebuildTableFromHistory.
959 Release(); 980 Release();
960 } 981 }
OLDNEW
« no previous file with comments | « chrome/browser/visitedlink/visitedlink_master.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698