Home | History | Annotate | Download | only in leveldatabase
      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.
      4 
      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"
     12 
     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
     19 
     20 using namespace leveldb;
     21 
     22 namespace leveldb_env {
     23 
     24 namespace {
     25 
     26 const base::FilePath::CharType backup_table_extension[] =
     27     FILE_PATH_LITERAL(".bak");
     28 const base::FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb");
     29 
     30 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[]
     31     = FILE_PATH_LITERAL("leveldb-test-");
     32 
     33 class ChromiumFileLock : public FileLock {
     34  public:
     35   ::base::File file_;
     36   std::string name_;
     37 };
     38 
     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_);
     57       }
     58     }
     59   }
     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;
     67     }
     68     success_ = false;
     69     return false;
     70   }
     71 
     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_;
     81 };
     82 
     83 class IDBEnvStdio : public ChromiumEnvStdio {
     84  public:
     85   IDBEnvStdio() : ChromiumEnvStdio() {
     86     name_ = "LevelDBEnv.IDB";
     87     make_backup_ = true;
     88   }
     89 };
     90 
     91 #if defined(OS_WIN)
     92 class IDBEnvWin : public ChromiumEnvWin {
     93  public:
     94   IDBEnvWin() : ChromiumEnvWin() {
     95     name_ = "LevelDBEnv.IDB";
     96     make_backup_ = true;
     97   }
     98 };
     99 #endif
    100 
    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
    108 
    109 ::base::LazyInstance<ChromiumEnvStdio>::Leaky default_env =
    110     LAZY_INSTANCE_INITIALIZER;
    111 
    112 }  // unnamed namespace
    113 
    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";
    161   }
    162   NOTREACHED();
    163   return "Unknown";
    164 }
    165 
    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);
    179 }
    180 
    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);
    195 }
    196 
    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);
    206 }
    207 
    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;
    215   }
    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;
    221   }
    222   if (RE2::PartialMatch(
    223           string, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method, error)) {
    224     *method_param = static_cast<MethodID>(method);
    225     return METHOD_AND_ERRNO;
    226   }
    227   return NONE;
    228 }
    229 
    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",
    265 };
    266 
    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;
    279     }
    280   }
    281   return error;
    282 }
    283 
    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;
    288 }
    289 
    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];
    295 }
    296 
    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);
    308 }
    309 
    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;
    316 }
    317 
    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));
    322 }
    323 
    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
    330 }
    331 
    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
    338 }
    339 
    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);
    345 }
    346 
    347 bool ChromiumEnv::HasTableExtension(const base::FilePath& path)
    348 {
    349   return path.MatchesExtension(table_extension);
    350 }
    351 
    352 ChromiumEnv::ChromiumEnv()
    353     : name_("LevelDBEnv"),
    354       make_backup_(false),
    355       bgsignal_(&mu_),
    356       started_bgthread_(false),
    357       kMaxRetryTimeMillis(1000) {
    358 }
    359 
    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.
    364 }
    365 
    366 bool ChromiumEnv::FileExists(const std::string& fname) {
    367   return ::base::PathExists(CreateFilePath(fname));
    368 }
    369 
    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();
    408   }
    409   NOTIMPLEMENTED();
    410   return "Unknown error.";
    411 }
    412 
    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;
    423 }
    424 
    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());
    437   }
    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);
    455   }
    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()));
    463   }
    464 }
    465 
    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);
    475   }
    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));
    481   }
    482 
    483   if (make_backup_)
    484     RestoreIfNecessary(dir_string, result);
    485   return Status::OK();
    486 }
    487 
    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);
    495   }
    496   if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
    497     base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
    498                      false);
    499   }
    500   return result;
    501 }
    502 
    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;
    514 }
    515 
    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);
    522   }
    523   return result;
    524 }
    525 
    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);
    535   }
    536   return s;
    537 }
    538 
    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);
    545 
    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));
    552 
    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);
    561 }
    562 
    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));
    577 
    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);
    591     }
    592 
    593     result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
    594                          error_code);
    595     RecordOSError(kLockFile, error_code);
    596     return result;
    597   }
    598 
    599   if (!locks_.Insert(fname)) {
    600     result = MakeIOError(fname, "Lock file already locked.", kLockFile);
    601     return result;
    602   }
    603 
    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));
    609 
    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;
    616   }
    617 
    618   ChromiumFileLock* my_lock = new ChromiumFileLock;
    619   my_lock->file_ = file.Pass();
    620   my_lock->name_ = fname;
    621   *lock = my_lock;
    622   return result;
    623 }
    624 
    625 Status ChromiumEnv::UnlockFile(FileLock* lock) {
    626   ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
    627   Status result;
    628 
    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);
    634   }
    635   bool removed = locks_.Remove(my_lock->name_);
    636   DCHECK(removed);
    637   delete my_lock;
    638   return result;
    639 }
    640 
    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);
    650     }
    651   }
    652   *path = FilePathToString(test_directory_);
    653   mu_.Release();
    654   return Status::OK();
    655 }
    656 
    657 uint64_t ChromiumEnv::NowMicros() {
    658   return ::base::TimeTicks::Now().ToInternalValue();
    659 }
    660 
    661 void ChromiumEnv::SleepForMicroseconds(int micros) {
    662   // Round up to the next millisecond.
    663   ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros));
    664 }
    665 
    666 void ChromiumEnv::RecordErrorAt(MethodID method) const {
    667   GetMethodIOErrorHistogram()->Add(method);
    668 }
    669 
    670 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
    671   GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
    672 }
    673 
    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);
    679 }
    680 
    681 void ChromiumEnv::RecordOSError(MethodID method, int error) const {
    682   DCHECK(error > 0);
    683   RecordErrorAt(method);
    684   GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
    685 }
    686 
    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);
    692 }
    693 
    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);
    701 }
    702 
    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);
    708 }
    709 
    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);
    721 }
    722 
    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);
    732 }
    733 
    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));
    738 
    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);
    747 }
    748 
    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);
    756 }
    757 
    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);
    765   }
    766   virtual ~Thread() {}
    767   virtual void ThreadMain() {
    768     (*function_)(arg_);
    769     delete this;
    770   }
    771 
    772  private:
    773   void (*function_)(void* arg);
    774   void* arg_;
    775 };
    776 
    777 void ChromiumEnv::Schedule(void (*function)(void*), void* arg) {
    778   mu_.Acquire();
    779 
    780   // Start background thread if necessary
    781   if (!started_bgthread_) {
    782     started_bgthread_ = true;
    783     StartThread(&ChromiumEnv::BGThreadWrapper, this);
    784   }
    785 
    786   // If the queue is currently empty, the background thread may currently be
    787   // waiting.
    788   if (queue_.empty()) {
    789     bgsignal_.Signal();
    790   }
    791 
    792   // Add to priority queue
    793   queue_.push_back(BGItem());
    794   queue_.back().function = function;
    795   queue_.back().arg = arg;
    796 
    797   mu_.Release();
    798 }
    799 
    800 void ChromiumEnv::BGThread() {
    801   base::PlatformThread::SetName(name_.c_str());
    802 
    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();
    808     }
    809 
    810     void (*function)(void*) = queue_.front().function;
    811     void* arg = queue_.front().arg;
    812     queue_.pop_front();
    813 
    814     mu_.Release();
    815     TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
    816     (*function)(arg);
    817   }
    818 }
    819 
    820 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
    821   new Thread(function, arg); // Will self-delete.
    822 }
    823 
    824 static std::string GetDirName(const std::string& filename) {
    825   base::FilePath file = base::FilePath::FromUTF8Unsafe(filename);
    826   return FilePathToString(file.DirName());
    827 }
    828 
    829 void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
    830   base::AutoLock auto_lock(map_lock_);
    831   needs_sync_map_[GetDirName(filename)] = true;
    832 }
    833 
    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();
    837 }
    838 
    839 void ChromiumEnv::DidSyncDir(const std::string& filename) {
    840   base::AutoLock auto_lock(map_lock_);
    841   needs_sync_map_.erase(GetDirName(filename));
    842 }
    843 
    844 }  // namespace leveldb_env
    845 
    846 namespace leveldb {
    847 
    848 Env* IDBEnv() {
    849   return leveldb_env::idb_env.Pointer();
    850 }
    851 
    852 Env* Env::Default() {
    853   return leveldb_env::default_env.Pointer();
    854 }
    855 
    856 }  // namespace leveldb
    857 
    858