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

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 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
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
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
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
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
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 }
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