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