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"
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"
20 #include "base/command_line.h"
21 #include "base/win/win_util.h"
22 #include "third_party/leveldatabase/env_chromium_win.h"
25 using leveldb::FileLock;
27 using leveldb::Status;
29 namespace leveldb_env {
33 const base::FilePath::CharType backup_table_extension[] =
34 FILE_PATH_LITERAL(".bak");
35 const base::FilePath::CharType table_extension[] = FILE_PATH_LITERAL(".ldb");
37 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[]
38 = FILE_PATH_LITERAL("leveldb-test-");
40 class ChromiumFileLock : public FileLock {
48 Retrier(MethodID method, RetrierProvider* provider)
49 : start_(base::TimeTicks::Now()),
50 limit_(start_ + base::TimeDelta::FromMilliseconds(
51 provider->MaxRetryTimeMillis())),
53 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
56 last_error_(base::File::FILE_OK),
57 provider_(provider) {}
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_);
67 bool ShouldKeepTrying(base::File::Error last_error) {
68 DCHECK_NE(last_error, base::File::FILE_OK);
69 last_error_ = last_error;
71 base::PlatformThread::Sleep(time_to_sleep_);
72 last_ = base::TimeTicks::Now();
80 base::TimeTicks start_;
81 base::TimeTicks limit_;
82 base::TimeTicks last_;
83 base::TimeDelta time_to_sleep_;
86 base::File::Error last_error_;
87 RetrierProvider* provider_;
90 class IDBEnvStdio : public ChromiumEnvStdio {
92 IDBEnvStdio() : ChromiumEnvStdio() {
93 name_ = "LevelDBEnv.IDB";
99 class IDBEnvWin : public ChromiumEnvWin {
101 IDBEnvWin() : ChromiumEnvWin() {
102 name_ = "LevelDBEnv.IDB";
109 ::base::LazyInstance<IDBEnvWin>::Leaky idb_env =
110 LAZY_INSTANCE_INITIALIZER;
112 ::base::LazyInstance<IDBEnvStdio>::Leaky idb_env =
113 LAZY_INSTANCE_INITIALIZER;
116 ::base::LazyInstance<ChromiumEnvStdio>::Leaky default_env =
117 LAZY_INSTANCE_INITIALIZER;
119 } // unnamed namespace
121 const char* MethodIDToString(MethodID 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";
150 return "GetFileSize";
157 case kGetTestDirectory:
158 return "GetTestDirectory";
164 return "GetChildren";
167 return "kNumEntries";
173 Status MakeIOError(Slice filename,
180 "%s (ChromeMethodErrno: %d::%s::%d)",
183 MethodIDToString(method),
185 return Status::IOError(filename, buf);
188 Status MakeIOError(Slice filename,
191 base::File::Error error) {
196 "%s (ChromeMethodPFE: %d::%s::%d)",
199 MethodIDToString(method),
201 return Status::IOError(filename, buf);
204 Status MakeIOError(Slice filename, const char* message, MethodID method) {
208 "%s (ChromeMethodOnly: %d::%s)",
211 MethodIDToString(method));
212 return Status::IOError(filename, buf);
215 ErrorParsingResult ParseMethodAndError(const char* string,
216 MethodID* method_param,
219 if (RE2::PartialMatch(string, "ChromeMethodOnly: (\\d+)", &method)) {
220 *method_param = static_cast<MethodID>(method);
223 if (RE2::PartialMatch(
224 string, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method, error)) {
226 *method_param = static_cast<MethodID>(method);
227 return METHOD_AND_PFE;
229 if (RE2::PartialMatch(
230 string, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method, error)) {
231 *method_param = static_cast<MethodID>(method);
232 return METHOD_AND_ERRNO;
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
240 const char* patterns[] = {
242 "log record too small",
243 "corrupted internal key",
245 "missing start of fragmented record",
246 "error in middle of record",
247 "unknown record type",
248 "truncated record at end",
251 "FileReader invoked with unexpected value",
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",
265 "truncated block read",
266 "block checksum mismatch",
268 "corrupted compressed block contents",
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) {
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;
297 std::string GetCorruptionMessage(const leveldb::Status& status) {
298 int code = GetCorruptionCode(status);
300 return "Unknown corruption";
301 return patterns[code - 1];
304 bool IndicatesDiskFull(const leveldb::Status& status) {
307 leveldb_env::MethodID method;
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);
317 bool IsIOError(const leveldb::Status& status) {
318 leveldb_env::MethodID method;
320 leveldb_env::ErrorParsingResult result = leveldb_env::ParseMethodAndError(
321 status.ToString().c_str(), &method, &error);
322 return result != leveldb_env::NONE;
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));
331 std::string FilePathToString(const base::FilePath& file_path) {
333 return base::UTF16ToUTF8(file_path.value());
335 return file_path.value();
339 base::FilePath ChromiumEnv::CreateFilePath(const std::string& file_path) {
341 return base::FilePath(base::UTF8ToUTF16(file_path));
343 return base::FilePath(file_path);
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);
354 bool ChromiumEnv::HasTableExtension(const base::FilePath& path) {
355 return path.MatchesExtension(table_extension);
358 ChromiumEnv::ChromiumEnv()
359 : name_("LevelDBEnv"),
362 started_bgthread_(false),
363 kMaxRetryTimeMillis(1000) {
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.
372 bool ChromiumEnv::FileExists(const std::string& fname) {
373 return ::base::PathExists(CreateFilePath(fname));
376 const char* ChromiumEnv::FileErrorString(::base::File::Error 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:
412 case ::base::File::FILE_ERROR_MAX:
416 return "Unknown error.";
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),
424 std::string uma_name(name_);
425 uma_name.append(".TableRestore");
426 base::BooleanHistogram::FactoryGet(
427 uma_name, base::Histogram::kUmaTargetedHistogramFlag)->AddBoolean(result);
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();
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());
444 std::set<base::FilePath> backups_only =
445 base::STLSetDifference<std::set<base::FilePath> >(backups_found,
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,
457 base::Histogram::kUmaTargetedHistogramFlag)
458 ->Add(num_missing_files);
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();
464 base::FilePath restored_table_name =
465 RestoreFromBackup(dir_filepath.Append(*it));
466 result->push_back(FilePathToString(restored_table_name.BaseName()));
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);
478 dir_string, "Could not open/read directory", kGetChildren, error);
481 for (std::vector<base::FilePath>::iterator it = entries.begin();
484 result->push_back(FilePathToString(*it));
488 RestoreIfNecessary(dir_string, result);
492 Status ChromiumEnv::DeleteFile(const std::string& fname) {
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);
500 if (make_backup_ && fname_filepath.MatchesExtension(table_extension)) {
501 base::DeleteFile(fname_filepath.ReplaceExtension(backup_table_extension),
507 Status ChromiumEnv::CreateDir(const std::string& name) {
509 base::File::Error error = base::File::FILE_OK;
510 Retrier retrier(kCreateDir, this);
512 if (base::CreateDirectoryAndGetError(CreateFilePath(name), &error))
514 } while (retrier.ShouldKeepTrying(error));
515 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
516 RecordOSError(kCreateDir, error);
520 Status ChromiumEnv::DeleteDir(const std::string& name) {
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);
530 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
533 if (!::base::GetFileSize(CreateFilePath(fname), &signed_size)) {
535 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
536 RecordErrorAt(kGetFileSize);
538 *size = static_cast<uint64_t>(signed_size);
543 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
545 base::FilePath src_file_path = CreateFilePath(src);
546 if (!::base::PathExists(src_file_path))
548 base::FilePath destination = CreateFilePath(dst);
550 Retrier retrier(kRenameFile, this);
551 base::File::Error error = base::File::FILE_OK;
553 if (base::ReplaceFile(src_file_path, destination, &error))
555 } while (retrier.ShouldKeepTrying(error));
557 DCHECK(error != base::File::FILE_OK);
558 RecordOSError(kRenameFile, error);
562 "Could not rename file: %s",
563 FileErrorString(error));
564 return MakeIOError(src, buf, kRenameFile, error);
567 Status ChromiumEnv::LockFile(const std::string& fname, FileLock** lock) {
570 int flags = ::base::File::FLAG_OPEN_ALWAYS |
571 ::base::File::FLAG_READ |
572 ::base::File::FLAG_WRITE;
573 ::base::File::Error error_code;
575 Retrier retrier(kLockFile, this);
577 file.Initialize(CreateFilePath(fname), flags);
579 error_code = file.error_details();
580 } while (!file.IsValid() && retrier.ShouldKeepTrying(error_code));
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;
588 if (base::DirectoryExists(parent))
590 ++num_missing_ancestors;
591 last_parent = parent;
592 parent = parent.DirName();
593 } while (parent != last_parent);
594 RecordLockFileAncestors(num_missing_ancestors);
597 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
599 RecordOSError(kLockFile, error_code);
603 if (!locks_.Insert(fname)) {
604 result = MakeIOError(fname, "Lock file already locked.", kLockFile);
608 Retrier lock_retrier = Retrier(kLockFile, this);
610 error_code = file.Lock();
611 } while (error_code != ::base::File::FILE_OK &&
612 retrier.ShouldKeepTrying(error_code));
614 if (error_code != ::base::File::FILE_OK) {
615 locks_.Remove(fname);
616 result = MakeIOError(fname, FileErrorString(error_code), kLockFile,
618 RecordOSError(kLockFile, error_code);
622 ChromiumFileLock* my_lock = new ChromiumFileLock;
623 my_lock->file_ = file.Pass();
624 my_lock->name_ = fname;
629 Status ChromiumEnv::UnlockFile(FileLock* lock) {
630 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
633 ::base::File::Error error_code = my_lock->file_.Unlock();
634 if (error_code != ::base::File::FILE_OK) {
636 MakeIOError(my_lock->name_, "Could not unlock lock file.", kUnlockFile);
637 RecordOSError(kUnlockFile, error_code);
639 bool removed = locks_.Remove(my_lock->name_);
645 Status ChromiumEnv::GetTestDirectory(std::string* path) {
647 if (test_directory_.empty()) {
648 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
651 RecordErrorAt(kGetTestDirectory);
653 "Could not create temp directory.", "", kGetTestDirectory);
656 *path = FilePathToString(test_directory_);
661 uint64_t ChromiumEnv::NowMicros() {
662 return ::base::TimeTicks::Now().ToInternalValue();
665 void ChromiumEnv::SleepForMicroseconds(int micros) {
666 // Round up to the next millisecond.
667 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros));
670 void ChromiumEnv::RecordErrorAt(MethodID method) const {
671 GetMethodIOErrorHistogram()->Add(method);
674 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const {
675 GetLockFileAncestorHistogram()->Add(num_missing_ancestors);
678 void ChromiumEnv::RecordOSError(MethodID method,
679 base::File::Error error) const {
681 RecordErrorAt(method);
682 GetOSErrorHistogram(method, -base::File::FILE_ERROR_MAX)->Add(-error);
685 void ChromiumEnv::RecordOSError(MethodID method, int error) const {
687 RecordErrorAt(method);
688 GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
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);
698 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
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);
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);
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);
727 base::HistogramBase* ChromiumEnv::GetLockFileAncestorHistogram() const {
728 std::string uma_name(name_);
729 uma_name.append(".LockFileAncestorsNotFound");
732 const int kNumBuckets = 11;
733 return base::LinearHistogram::FactoryGet(
734 uma_name, kMin, kMax, kNumBuckets,
735 base::Histogram::kUmaTargetedHistogramFlag);
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));
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),
750 base::Histogram::kUmaTargetedHistogramFlag);
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);
762 class Thread : public ::base::PlatformThread::Delegate {
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);
771 virtual void ThreadMain() {
777 void (*function_)(void* arg);
781 void ChromiumEnv::Schedule(ScheduleFunc* function, void* arg) {
784 // Start background thread if necessary
785 if (!started_bgthread_) {
786 started_bgthread_ = true;
787 StartThread(&ChromiumEnv::BGThreadWrapper, this);
790 // If the queue is currently empty, the background thread may currently be
792 if (queue_.empty()) {
796 // Add to priority queue
797 queue_.push_back(BGItem());
798 queue_.back().function = function;
799 queue_.back().arg = arg;
804 void ChromiumEnv::BGThread() {
805 base::PlatformThread::SetName(name_.c_str());
808 // Wait until there is an item that is ready to run
810 while (queue_.empty()) {
814 void (*function)(void*) = queue_.front().function;
815 void* arg = queue_.front().arg;
819 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
824 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
825 new Thread(function, arg); // Will self-delete.
828 static std::string GetDirName(const std::string& filename) {
829 base::FilePath file = base::FilePath::FromUTF8Unsafe(filename);
830 return FilePathToString(file.DirName());
833 void ChromiumEnv::DidCreateNewFile(const std::string& filename) {
834 base::AutoLock auto_lock(map_lock_);
835 needs_sync_map_[GetDirName(filename)] = true;
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();
843 void ChromiumEnv::DidSyncDir(const std::string& filename) {
844 base::AutoLock auto_lock(map_lock_);
845 needs_sync_map_.erase(GetDirName(filename));
848 } // namespace leveldb_env
853 return leveldb_env::idb_env.Pointer();
856 Env* Env::Default() {
857 return leveldb_env::default_env.Pointer();
860 } // namespace leveldb