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