[SyncFS] Build indexes from FileTracker entries on disk.
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blob17b50972474dedc0fbad39883997d535a2f73217
1 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. See the AUTHORS file for names of contributors.
5 #include "base/debug/trace_event.h"
6 #include "base/file_util.h"
7 #include "base/lazy_instance.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "env_chromium_stdio.h"
11 #include "third_party/re2/re2/re2.h"
13 #if defined(OS_WIN)
14 #include <io.h>
15 #include "base/command_line.h"
16 #include "base/win/win_util.h"
17 #include "env_chromium_win.h"
18 #endif
20 using namespace leveldb;
22 namespace leveldb_env {
24 namespace {
26 const base::FilePath::CharType backup_table_extension[] =
27 FILE_PATH_LITERAL(".bak");
28 const base::FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb");
30 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[]
31 = FILE_PATH_LITERAL("leveldb-test-");
33 class ChromiumFileLock : public FileLock {
34 public:
35 ::base::File file_;
36 std::string name_;
39 class Retrier {
40 public:
41 Retrier(MethodID method, RetrierProvider* provider)
42 : start_(base::TimeTicks::Now()),
43 limit_(start_ + base::TimeDelta::FromMilliseconds(
44 provider->MaxRetryTimeMillis())),
45 last_(start_),
46 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
47 success_(true),
48 method_(method),
49 last_error_(base::File::FILE_OK),
50 provider_(provider) {}
51 ~Retrier() {
52 if (success_) {
53 provider_->GetRetryTimeHistogram(method_)->AddTime(last_ - start_);
54 if (last_error_ != base::File::FILE_OK) {
55 DCHECK(last_error_ < 0);
56 provider_->GetRecoveredFromErrorHistogram(method_)->Add(-last_error_);
60 bool ShouldKeepTrying(base::File::Error last_error) {
61 DCHECK_NE(last_error, base::File::FILE_OK);
62 last_error_ = last_error;
63 if (last_ < limit_) {
64 base::PlatformThread::Sleep(time_to_sleep_);
65 last_ = base::TimeTicks::Now();
66 return true;
68 success_ = false;
69 return false;
72 private:
73 base::TimeTicks start_;
74 base::TimeTicks limit_;
75 base::TimeTicks last_;
76 base::TimeDelta time_to_sleep_;
77 bool success_;
78 MethodID method_;
79 base::File::Error last_error_;
80 RetrierProvider* provider_;
83 class IDBEnvStdio : public ChromiumEnvStdio {
84 public:
85 IDBEnvStdio() : ChromiumEnvStdio() {
86 name_ = "LevelDBEnv.IDB";
87 make_backup_ = true;
91 #if defined(OS_WIN)
92 class IDBEnvWin : public ChromiumEnvWin {
93 public:
94 IDBEnvWin() : ChromiumEnvWin() {
95 name_ = "LevelDBEnv.IDB";
96 make_backup_ = true;
99 #endif
101 #if defined(OS_WIN)
102 ::base::LazyInstance<IDBEnvWin>::Leaky idb_env =
103 LAZY_INSTANCE_INITIALIZER;
104 #else
105 ::base::LazyInstance<IDBEnvStdio>::Leaky idb_env =
106 LAZY_INSTANCE_INITIALIZER;
107 #endif
109 ::base::LazyInstance<ChromiumEnvStdio>::Leaky default_env =
110 LAZY_INSTANCE_INITIALIZER;
112 } // unnamed namespace
114 const char* MethodIDToString(MethodID method) {
115 switch (method) {
116 case kSequentialFileRead:
117 return "SequentialFileRead";
118 case kSequentialFileSkip:
119 return "SequentialFileSkip";
120 case kRandomAccessFileRead:
121 return "RandomAccessFileRead";
122 case kWritableFileAppend:
123 return "WritableFileAppend";
124 case kWritableFileClose:
125 return "WritableFileClose";
126 case kWritableFileFlush:
127 return "WritableFileFlush";
128 case kWritableFileSync:
129 return "WritableFileSync";
130 case kNewSequentialFile:
131 return "NewSequentialFile";
132 case kNewRandomAccessFile:
133 return "NewRandomAccessFile";
134 case kNewWritableFile:
135 return "NewWritableFile";
136 case kDeleteFile:
137 return "DeleteFile";
138 case kCreateDir:
139 return "CreateDir";
140 case kDeleteDir:
141 return "DeleteDir";
142 case kGetFileSize:
143 return "GetFileSize";
144 case kRenameFile:
145 return "RenameFile";
146 case kLockFile:
147 return "LockFile";
148 case kUnlockFile:
149 return "UnlockFile";
150 case kGetTestDirectory:
151 return "GetTestDirectory";
152 case kNewLogger:
153 return "NewLogger";
154 case kSyncParent:
155 return "SyncParent";
156 case kGetChildren:
157 return "GetChildren";
158 case kNumEntries:
159 NOTREACHED();
160 return "kNumEntries";
162 NOTREACHED();
163 return "Unknown";
166 Status MakeIOError(Slice filename,
167 const char* message,
168 MethodID method,
169 int saved_errno) {
170 char buf[512];
171 snprintf(buf,
172 sizeof(buf),
173 "%s (ChromeMethodErrno: %d::%s::%d)",
174 message,
175 method,
176 MethodIDToString(method),
177 saved_errno);
178 return Status::IOError(filename, buf);
181 Status MakeIOError(Slice filename,
182 const char* message,
183 MethodID method,
184 base::File::Error error) {
185 DCHECK(error < 0);
186 char buf[512];
187 snprintf(buf,
188 sizeof(buf),
189 "%s (ChromeMethodPFE: %d::%s::%d)",
190 message,
191 method,
192 MethodIDToString(method),
193 -error);
194 return Status::IOError(filename, buf);
197 Status MakeIOError(Slice filename, const char* message, MethodID method) {
198 char buf[512];
199 snprintf(buf,
200 sizeof(buf),
201 "%s (ChromeMethodOnly: %d::%s)",
202 message,
203 method,
204 MethodIDToString(method));
205 return Status::IOError(filename, buf);
208 ErrorParsingResult ParseMethodAndError(const char* string,
209 MethodID* method_param,
210 int* error) {
211 int method;
212 if (RE2::PartialMatch(string, "ChromeMethodOnly: (\\d+)", &method)) {
213 *method_param = static_cast<MethodID>(method);
214 return METHOD_ONLY;
216 if (RE2::PartialMatch(
217 string, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method, error)) {
218 *error = -*error;
219 *method_param = static_cast<MethodID>(method);
220 return METHOD_AND_PFE;
222 if (RE2::PartialMatch(
223 string, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method, error)) {
224 *method_param = static_cast<MethodID>(method);
225 return METHOD_AND_ERRNO;
227 return NONE;
230 // Keep in sync with LevelDBCorruptionTypes in histograms.xml. Also, don't
231 // change the order because indices into this array have been recorded in uma
232 // histograms.
233 const char* patterns[] = {
234 "missing files",
235 "log record too small",
236 "corrupted internal key",
237 "partial record",
238 "missing start of fragmented record",
239 "error in middle of record",
240 "unknown record type",
241 "truncated record at end",
242 "bad record length",
243 "VersionEdit",
244 "FileReader invoked with unexpected value",
245 "corrupted key",
246 "CURRENT file does not end with newline",
247 "no meta-nextfile entry",
248 "no meta-lognumber entry",
249 "no last-sequence-number entry",
250 "malformed WriteBatch",
251 "bad WriteBatch Put",
252 "bad WriteBatch Delete",
253 "unknown WriteBatch tag",
254 "WriteBatch has wrong count",
255 "bad entry in block",
256 "bad block contents",
257 "bad block handle",
258 "truncated block read",
259 "block checksum mismatch",
260 "checksum mismatch",
261 "corrupted compressed block contents",
262 "bad block type",
263 "bad magic number",
264 "file is too short",
267 // Returns 1-based index into the above array or 0 if nothing matches.
268 int GetCorruptionCode(const leveldb::Status& status) {
269 DCHECK(!status.IsIOError());
270 DCHECK(!status.ok());
271 const int kOtherError = 0;
272 int error = kOtherError;
273 const std::string& str_error = status.ToString();
274 const size_t kNumPatterns = arraysize(patterns);
275 for (size_t i = 0; i < kNumPatterns; ++i) {
276 if (str_error.find(patterns[i]) != std::string::npos) {
277 error = i + 1;
278 break;
281 return error;
284 int GetNumCorruptionCodes() {
285 // + 1 for the "other" error that is returned when a corruption message
286 // doesn't match any of the patterns.
287 return arraysize(patterns) + 1;
290 std::string GetCorruptionMessage(const leveldb::Status& status) {
291 int code = GetCorruptionCode(status);
292 if (code == 0)
293 return "Unknown corruption";
294 return patterns[code - 1];
297 bool IndicatesDiskFull(const leveldb::Status& status) {
298 if (status.ok())
299 return false;
300 leveldb_env::MethodID method;
301 int error = -1;
302 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
303 status.ToString().c_str(), &method, &error);
304 return (result == leveldb_env::METHOD_AND_PFE &&
305 static_cast<base::File::Error>(error) ==
306 base::File::FILE_ERROR_NO_SPACE) ||
307 (result == leveldb_env::METHOD_AND_ERRNO && error == ENOSPC);
310 bool IsIOError(const leveldb::Status& status) {
311 leveldb_env::MethodID method;
312 int error = -1;
313 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
314 status.ToString().c_str(), &method, &error);
315 return result != leveldb_env::NONE;
318 bool IsCorruption(const leveldb::Status& status) {
319 // LevelDB returns InvalidArgument when an sst file is truncated but there is
320 // no IsInvalidArgument() accessor defined.
321 return status.IsCorruption() || (!status.ok() && !IsIOError(status));
324 std::string FilePathToString(const base::FilePath& file_path) {
325 #if defined(OS_WIN)
326 return base::UTF16ToUTF8(file_path.value());
327 #else
328 return file_path.value();
329 #endif
332 base::FilePath ChromiumEnv::CreateFilePath(const std::string& file_path) {
333 #if defined(OS_WIN)
334 return base::FilePath(base::UTF8ToUTF16(file_path));
335 #else
336 return base::FilePath(file_path);
337 #endif
340 bool ChromiumEnv::MakeBackup(const std::string& fname) {
341 base::FilePath original_table_name = CreateFilePath(fname);
342 base::FilePath backup_table_name =
343 original_table_name.ReplaceExtension(backup_table_extension);
344 return base::CopyFile(original_table_name, backup_table_name);
347 bool ChromiumEnv::HasTableExtension(const base::FilePath& path)
349 return path.MatchesExtension(table_extension);
352 ChromiumEnv::ChromiumEnv()
353 : name_("LevelDBEnv"),
354 make_backup_(false),
355 bgsignal_(&mu_),
356 started_bgthread_(false),
357 kMaxRetryTimeMillis(1000) {
360 ChromiumEnv::~ChromiumEnv() {
361 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
362 // ensure that behavior isn't accidentally changed, but there's an instance in
363 // a unit test that is deleted.
366 bool ChromiumEnv::FileExists(const std::string& fname) {
367 return ::base::PathExists(CreateFilePath(fname));
370 const char* ChromiumEnv::FileErrorString(::base::File::Error error) {
371 switch (error) {
372 case ::base::File::FILE_ERROR_FAILED:
373 return "No further details.";
374 case ::base::File::FILE_ERROR_IN_USE:
375 return "File currently in use.";
376 case ::base::File::FILE_ERROR_EXISTS:
377 return "File already exists.";
378 case ::base::File::FILE_ERROR_NOT_FOUND:
379 return "File not found.";
380 case ::base::File::FILE_ERROR_ACCESS_DENIED:
381 return "Access denied.";
382 case ::base::File::FILE_ERROR_TOO_MANY_OPENED:
383 return "Too many files open.";
384 case ::base::File::FILE_ERROR_NO_MEMORY:
385 return "Out of memory.";
386 case ::base::File::FILE_ERROR_NO_SPACE:
387 return "No space left on drive.";
388 case ::base::File::FILE_ERROR_NOT_A_DIRECTORY:
389 return "Not a directory.";
390 case ::base::File::FILE_ERROR_INVALID_OPERATION:
391 return "Invalid operation.";
392 case ::base::File::FILE_ERROR_SECURITY:
393 return "Security error.";
394 case ::base::File::FILE_ERROR_ABORT:
395 return "File operation aborted.";
396 case ::base::File::FILE_ERROR_NOT_A_FILE:
397 return "The supplied path was not a file.";
398 case ::base::File::FILE_ERROR_NOT_EMPTY:
399 return "The file was not empty.";
400 case ::base::File::FILE_ERROR_INVALID_URL:
401 return "Invalid URL.";
402 case ::base::File::FILE_ERROR_IO:
403 return "OS or hardware error.";
404 case ::base::File::FILE_OK:
405 return "OK.";
406 case ::base::File::FILE_ERROR_MAX:
407 NOTREACHED();
409 NOTIMPLEMENTED();
410 return "Unknown error.";
413 base::FilePath ChromiumEnv::RestoreFromBackup(const base::FilePath& base_name) {
414 base::FilePath table_name =
415 base_name.AddExtension(table_extension);
416 bool result = base::CopyFile(base_name.AddExtension(backup_table_extension),
417 table_name);
418 std::string uma_name(name_);
419 uma_name.append(".TableRestore");
420 base::BooleanHistogram::FactoryGet(
421 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
422 return table_name;
425 void ChromiumEnv::RestoreIfNecessary(const std::string& dir,
426 std::vector<std::string>* result) {
427 std::set<base::FilePath> tables_found;
428 std::set<base::FilePath> backups_found;
429 for (std::vector<std::string>::iterator it = result->begin();
430 it != result->end();
431 ++it) {
432 base::FilePath current = CreateFilePath(*it);
433 if (current.MatchesExtension(table_extension))
434 tables_found.insert(current.RemoveExtension());
435 if (current.MatchesExtension(backup_table_extension))
436 backups_found.insert(current.RemoveExtension());
438 std::set<base::FilePath> backups_only;
439 std::set_difference(backups_found.begin(),
440 backups_found.end(),
441 tables_found.begin(),
442 tables_found.end(),
443 std::inserter(backups_only, backups_only.begin()));
444 if (backups_only.size()) {
445 std::string uma_name(name_);
446 uma_name.append(".MissingFiles");
447 int num_missing_files =
448 backups_only.size() > INT_MAX ? INT_MAX : backups_only.size();
449 base::Histogram::FactoryGet(uma_name,
450 1 /*min*/,
451 100 /*max*/,
452 8 /*num_buckets*/,
453 base::Histogram::kUmaTargetedHistogramFlag)
454 ->Add(num_missing_files);
456 base::FilePath dir_filepath = base::FilePath::FromUTF8Unsafe(dir);
457 for (std::set<base::FilePath>::iterator it = backups_only.begin();
458 it != backups_only.end();
459 ++it) {
460 base::FilePath restored_table_name =
461 RestoreFromBackup(dir_filepath.Append(*it));
462 result->push_back(FilePathToString(restored_table_name.BaseName()));
466 Status ChromiumEnv::GetChildren(const std::string& dir_string,
467 std::vector<std::string>* result) {
468 std::vector<base::FilePath> entries;
469 base::File::Error error =
470 GetDirectoryEntries(CreateFilePath(dir_string), &entries);
471 if (error != base::File::FILE_OK) {
472 RecordOSError(kGetChildren, error);
473 return MakeIOError(
474 dir_string, "Could not open/read directory", kGetChildren, error);
476 result->clear();
477 for (std::vector<base::FilePath>::iterator it = entries.begin();
478 it != entries.end();
479 ++it) {
480 result->push_back(FilePathToString(*it));
483 if (make_backup_)
484 RestoreIfNecessary(dir_string, result);
485 return Status::OK();
488 Status ChromiumEnv::DeleteFile(const std::string& fname) {
489 Status result;
490 base::FilePath fname_filepath = CreateFilePath(fname);
491 // TODO(jorlow): Should we assert this is a file?
492 if (!::base::DeleteFile(fname_filepath, false)) {
493 result = MakeIOError(fname, "Could not delete file.", kDeleteFile);
494 RecordErrorAt(kDeleteFile);
496 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
497 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
498 false);
500 return result;
503 Status ChromiumEnv::CreateDir(const std::string& name) {
504 Status result;
505 base::File::Error error = base::File::FILE_OK;
506 Retrier retrier(kCreateDir, this);
507 do {
508 if (base::CreateDirectoryAndGetError(CreateFilePath(name), &error))
509 return result;
510 } while (retrier.ShouldKeepTrying(error));
511 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
512 RecordOSError(kCreateDir, error);
513 return result;
516 Status ChromiumEnv::DeleteDir(const std::string& name) {
517 Status result;
518 // TODO(jorlow): Should we assert this is a directory?
519 if (!::base::DeleteFile(CreateFilePath(name), false)) {
520 result = MakeIOError(name, "Could not delete directory.", kDeleteDir);
521 RecordErrorAt(kDeleteDir);
523 return result;
526 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
527 Status s;
528 int64_t signed_size;
529 if (!::base::GetFileSize(CreateFilePath(fname), &signed_size)) {
530 *size = 0;
531 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
532 RecordErrorAt(kGetFileSize);
533 } else {
534 *size = static_cast<uint64_t>(signed_size);
536 return s;
539 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
540 Status result;
541 base::FilePath src_file_path = CreateFilePath(src);
542 if (!::base::PathExists(src_file_path))
543 return result;
544 base::FilePath destination = CreateFilePath(dst);
546 Retrier retrier(kRenameFile, this);
547 base::File::Error error = base::File::FILE_OK;
548 do {
549 if (base::ReplaceFile(src_file_path, destination, &error))
550 return result;
551 } while (retrier.ShouldKeepTrying(error));
553 DCHECK(error != base::File::FILE_OK);
554 RecordOSError(kRenameFile, error);
555 char buf[100];
556 snprintf(buf,
557 sizeof(buf),
558 "Could not rename file: %s",
559 FileErrorString(error));
560 return MakeIOError(src, buf, kRenameFile, error);
563 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
564 *lock = NULL;
565 Status result;
566 int flags = ::base::File::FLAG_OPEN_ALWAYS |
567 ::base::File::FLAG_READ |
568 ::base::File::FLAG_WRITE;
569 ::base::File::Error error_code;
570 ::base::File file;
571 Retrier retrier(kLockFile, this);
572 do {
573 file.Initialize(CreateFilePath(fname), flags);
574 if (!file.IsValid())
575 error_code = file.error_details();
576 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
578 if (!file.IsValid()) {
579 if (error_code == ::base::File::FILE_ERROR_NOT_FOUND) {
580 ::base::FilePath parent = CreateFilePath(fname).DirName();
581 ::base::FilePath last_parent;
582 int num_missing_ancestors = 0;
583 do {
584 if (base::DirectoryExists(parent))
585 break;
586 ++num_missing_ancestors;
587 last_parent = parent;
588 parent = parent.DirName();
589 } while (parent != last_parent);
590 RecordLockFileAncestors(num_missing_ancestors);
593 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
594 error_code);
595 RecordOSError(kLockFile, error_code);
596 return result;
599 if (!locks_.Insert(fname)) {
600 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
601 return result;
604 Retrier lock_retrier = Retrier(kLockFile, this);
605 do {
606 error_code = file.Lock();
607 } while (error_code != ::base::File::FILE_OK &&
608 retrier.ShouldKeepTrying(error_code));
610 if (error_code != ::base::File::FILE_OK) {
611 locks_.Remove(fname);
612 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
613 error_code);
614 RecordOSError(kLockFile, error_code);
615 return result;
618 ChromiumFileLock* my_lock = new ChromiumFileLock;
619 my_lock->file_ = file.Pass();
620 my_lock->name_ = fname;
621 *lock = my_lock;
622 return result;
625 Status ChromiumEnv::UnlockFile(FileLock* lock) {
626 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
627 Status result;
629 ::base::File::Error error_code = my_lock->file_.Unlock();
630 if (error_code != ::base::File::FILE_OK) {
631 result =
632 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
633 RecordOSError(kUnlockFile, error_code);
635 bool removed = locks_.Remove(my_lock->name_);
636 DCHECK(removed);
637 delete my_lock;
638 return result;
641 Status ChromiumEnv::GetTestDirectory(std::string* path) {
642 mu_.Acquire();
643 if (test_directory_.empty()) {
644 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
645 &test_directory_)) {
646 mu_.Release();
647 RecordErrorAt(kGetTestDirectory);
648 return MakeIOError(
649 "Could not create temp directory.", "", kGetTestDirectory);
652 *path = FilePathToString(test_directory_);
653 mu_.Release();
654 return Status::OK();
657 uint64_t ChromiumEnv::NowMicros() {
658 return ::base::TimeTicks::Now().ToInternalValue();
661 void ChromiumEnv::SleepForMicroseconds(int micros) {
662 // Round up to the next millisecond.
663 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros));
666 void ChromiumEnv::RecordErrorAt(MethodID method) const {
667 GetMethodIOErrorHistogram()->Add(method);
670 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
671 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
674 void ChromiumEnv::RecordOSError(MethodID method,
675 base::File::Error error) const {
676 DCHECK(error < 0);
677 RecordErrorAt(method);
678 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
681 void ChromiumEnv::RecordOSError(MethodID method, int error) const {
682 DCHECK(error > 0);
683 RecordErrorAt(method);
684 GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
687 void ChromiumEnv::RecordBackupResult(bool result) const {
688 std::string uma_name(name_);
689 uma_name.append(".TableBackup");
690 base::BooleanHistogram::FactoryGet(
691 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
694 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
695 int limit) const {
696 std::string uma_name(name_);
697 // TODO(dgrogan): This is probably not the best way to concatenate strings.
698 uma_name.append(".IOError.").append(MethodIDToString(method));
699 return base::LinearHistogram::FactoryGet(uma_name, 1, limit, limit + 1,
700 base::Histogram::kUmaTargetedHistogramFlag);
703 base::HistogramBase* ChromiumEnv::GetMethodIOErrorHistogram() const {
704 std::string uma_name(name_);
705 uma_name.append(".IOError");
706 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
707 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
710 base::HistogramBase* ChromiumEnv::GetMaxFDHistogram(
711 const std::string& type) const {
712 std::string uma_name(name_);
713 uma_name.append(".MaxFDs.").append(type);
714 // These numbers make each bucket twice as large as the previous bucket.
715 const int kFirstEntry = 1;
716 const int kLastEntry = 65536;
717 const int kNumBuckets = 18;
718 return base::Histogram::FactoryGet(
719 uma_name, kFirstEntry, kLastEntry, kNumBuckets,
720 base::Histogram::kUmaTargetedHistogramFlag);
723 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
724 std::string uma_name(name_);
725 uma_name.append(".LockFileAncestorsNotFound");
726 const int kMin = 1;
727 const int kMax = 10;
728 const int kNumBuckets = 11;
729 return base::LinearHistogram::FactoryGet(
730 uma_name, kMin, kMax, kNumBuckets,
731 base::Histogram::kUmaTargetedHistogramFlag);
734 base::HistogramBase* ChromiumEnv::GetRetryTimeHistogram(MethodID method) const {
735 std::string uma_name(name_);
736 // TODO(dgrogan): This is probably not the best way to concatenate strings.
737 uma_name.append(".TimeUntilSuccessFor").append(MethodIDToString(method));
739 const int kBucketSizeMillis = 25;
740 // Add 2, 1 for each of the buckets <1 and >max.
741 const int kNumBuckets = kMaxRetryTimeMillis / kBucketSizeMillis + 2;
742 return base::Histogram::FactoryTimeGet(
743 uma_name, base::TimeDelta::FromMilliseconds(1),
744 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis + 1),
745 kNumBuckets,
746 base::Histogram::kUmaTargetedHistogramFlag);
749 base::HistogramBase* ChromiumEnv::GetRecoveredFromErrorHistogram(
750 MethodID method) const {
751 std::string uma_name(name_);
752 uma_name.append(".RetryRecoveredFromErrorIn")
753 .append(MethodIDToString(method));
754 return base::LinearHistogram::FactoryGet(uma_name, 1, kNumEntries,
755 kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
758 class Thread : public ::base::PlatformThread::Delegate {
759 public:
760 Thread(void (*function)(void* arg), void* arg)
761 : function_(function), arg_(arg) {
762 ::base::PlatformThreadHandle handle;
763 bool success = ::base::PlatformThread::Create(0, this, &handle);
764 DCHECK(success);
766 virtual ~Thread() {}
767 virtual void ThreadMain() {
768 (*function_)(arg_);
769 delete this;
772 private:
773 void (*function_)(void* arg);
774 void* arg_;
777 void ChromiumEnv::Schedule(void (*function)(void*), void* arg) {
778 mu_.Acquire();
780 // Start background thread if necessary
781 if (!started_bgthread_) {
782 started_bgthread_ = true;
783 StartThread(&ChromiumEnv::BGThreadWrapper, this);
786 // If the queue is currently empty, the background thread may currently be
787 // waiting.
788 if (queue_.empty()) {
789 bgsignal_.Signal();
792 // Add to priority queue
793 queue_.push_back(BGItem());
794 queue_.back().function = function;
795 queue_.back().arg = arg;
797 mu_.Release();
800 void ChromiumEnv::BGThread() {
801 base::PlatformThread::SetName(name_.c_str());
803 while (true) {
804 // Wait until there is an item that is ready to run
805 mu_.Acquire();
806 while (queue_.empty()) {
807 bgsignal_.Wait();
810 void (*function)(void*) = queue_.front().function;
811 void* arg = queue_.front().arg;
812 queue_.pop_front();
814 mu_.Release();
815 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
816 (*function)(arg);
820 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
821 new Thread(function, arg); // Will self-delete.
824 static std::string GetDirName(const std::string& filename) {
825 base::FilePath file = base::FilePath::FromUTF8Unsafe(filename);
826 return FilePathToString(file.DirName());
829 void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
830 base::AutoLock auto_lock(map_lock_);
831 needs_sync_map_[GetDirName(filename)] = true;
834 bool ChromiumEnv::DoesDirNeedSync(const std::string& filename) {
835 base::AutoLock auto_lock(map_lock_);
836 return needs_sync_map_.find(GetDirName(filename)) != needs_sync_map_.end();
839 void ChromiumEnv::DidSyncDir(const std::string& filename) {
840 base::AutoLock auto_lock(map_lock_);
841 needs_sync_map_.erase(GetDirName(filename));
844 } // namespace leveldb_env
846 namespace leveldb {
848 Env* IDBEnv() {
849 return leveldb_env::idb_env.Pointer();
852 Env* Env::Default() {
853 return leveldb_env::default_env.Pointer();
856 } // namespace leveldb