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/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"
21 #include "base/command_line.h"
22 #include "base/win/win_util.h"
23 #include "third_party/leveldatabase/env_chromium_win.h"
26 using leveldb::FileLock
;
28 using leveldb::Status
;
30 namespace leveldb_env
{
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
{
49 Retrier(MethodID method
, RetrierProvider
* provider
)
50 : start_(base::TimeTicks::Now()),
51 limit_(start_
+ base::TimeDelta::FromMilliseconds(
52 provider
->MaxRetryTimeMillis())),
54 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
57 last_error_(base::File::FILE_OK
),
58 provider_(provider
) {}
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
;
72 base::PlatformThread::Sleep(time_to_sleep_
);
73 last_
= base::TimeTicks::Now();
81 base::TimeTicks start_
;
82 base::TimeTicks limit_
;
83 base::TimeTicks last_
;
84 base::TimeDelta time_to_sleep_
;
87 base::File::Error last_error_
;
88 RetrierProvider
* provider_
;
91 class IDBEnvStdio
: public ChromiumEnvStdio
{
93 IDBEnvStdio() : ChromiumEnvStdio() {
94 name_
= "LevelDBEnv.IDB";
100 class IDBEnvWin
: public ChromiumEnvWin
{
102 IDBEnvWin() : ChromiumEnvWin() {
103 name_
= "LevelDBEnv.IDB";
110 ::base::LazyInstance
<IDBEnvWin
>::Leaky idb_env
=
111 LAZY_INSTANCE_INITIALIZER
;
113 ::base::LazyInstance
<IDBEnvStdio
>::Leaky idb_env
=
114 LAZY_INSTANCE_INITIALIZER
;
117 ::base::LazyInstance
<ChromiumEnvStdio
>::Leaky default_env
=
118 LAZY_INSTANCE_INITIALIZER
;
120 } // unnamed namespace
122 const char* MethodIDToString(MethodID 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";
151 return "GetFileSize";
158 case kGetTestDirectory
:
159 return "GetTestDirectory";
165 return "GetChildren";
168 return "kNumEntries";
174 Status
MakeIOError(Slice filename
,
181 "%s (ChromeMethodErrno: %d::%s::%d)",
184 MethodIDToString(method
),
186 return Status::IOError(filename
, buf
);
189 Status
MakeIOError(Slice filename
,
192 base::File::Error error
) {
197 "%s (ChromeMethodPFE: %d::%s::%d)",
200 MethodIDToString(method
),
202 return Status::IOError(filename
, buf
);
205 Status
MakeIOError(Slice filename
, const char* message
, MethodID method
) {
209 "%s (ChromeMethodOnly: %d::%s)",
212 MethodIDToString(method
));
213 return Status::IOError(filename
, buf
);
216 ErrorParsingResult
ParseMethodAndError(const char* string
,
217 MethodID
* method_param
,
220 if (RE2::PartialMatch(string
, "ChromeMethodOnly: (\\d+)", &method
)) {
221 *method_param
= static_cast<MethodID
>(method
);
224 if (RE2::PartialMatch(
225 string
, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method
, 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
;
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
241 const char* patterns
[] = {
243 "log record too small",
244 "corrupted internal key",
246 "missing start of fragmented record",
247 "error in middle of record",
248 "unknown record type",
249 "truncated record at end",
252 "FileReader invoked with unexpected value",
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",
266 "truncated block read",
267 "block checksum mismatch",
269 "corrupted compressed block contents",
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
) {
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
);
301 return "Unknown corruption";
302 return patterns
[code
- 1];
305 bool IndicatesDiskFull(const leveldb::Status
& status
) {
308 leveldb_env::MethodID method
;
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
;
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
) {
334 return base::UTF16ToUTF8(file_path
.value());
336 return file_path
.value();
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"),
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
) {
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
:
409 case ::base::File::FILE_ERROR_MAX
:
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
),
421 std::string
uma_name(name_
);
422 uma_name
.append(".TableRestore");
423 base::BooleanHistogram::FactoryGet(
424 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
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();
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
,
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
,
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();
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
);
475 dir_string
, "Could not open/read directory", kGetChildren
, error
);
478 for (std::vector
<base::FilePath
>::iterator it
= entries
.begin();
481 result
->push_back(FilePathToString(*it
));
485 RestoreIfNecessary(dir_string
, result
);
489 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
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
),
504 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
506 base::File::Error error
= base::File::FILE_OK
;
507 Retrier
retrier(kCreateDir
, this);
509 if (base::CreateDirectoryAndGetError(CreateFilePath(name
), &error
))
511 } while (retrier
.ShouldKeepTrying(error
));
512 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
513 RecordOSError(kCreateDir
, error
);
517 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
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
);
527 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
530 if (!::base::GetFileSize(CreateFilePath(fname
), &signed_size
)) {
532 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
533 RecordErrorAt(kGetFileSize
);
535 *size
= static_cast<uint64_t>(signed_size
);
540 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
542 base::FilePath src_file_path
= CreateFilePath(src
);
543 if (!::base::PathExists(src_file_path
))
545 base::FilePath destination
= CreateFilePath(dst
);
547 Retrier
retrier(kRenameFile
, this);
548 base::File::Error error
= base::File::FILE_OK
;
550 if (base::ReplaceFile(src_file_path
, destination
, &error
))
552 } while (retrier
.ShouldKeepTrying(error
));
554 DCHECK(error
!= base::File::FILE_OK
);
555 RecordOSError(kRenameFile
, error
);
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
) {
567 int flags
= ::base::File::FLAG_OPEN_ALWAYS
|
568 ::base::File::FLAG_READ
|
569 ::base::File::FLAG_WRITE
;
570 ::base::File::Error error_code
;
572 Retrier
retrier(kLockFile
, this);
574 file
.Initialize(CreateFilePath(fname
), flags
);
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;
585 if (base::DirectoryExists(parent
))
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
,
596 RecordOSError(kLockFile
, error_code
);
600 if (!locks_
.Insert(fname
)) {
601 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
605 Retrier lock_retrier
= Retrier(kLockFile
, this);
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
,
615 RecordOSError(kLockFile
, error_code
);
619 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
620 my_lock
->file_
= file
.Pass();
621 my_lock
->name_
= fname
;
626 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
627 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
630 ::base::File::Error error_code
= my_lock
->file_
.Unlock();
631 if (error_code
!= ::base::File::FILE_OK
) {
633 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
634 RecordOSError(kUnlockFile
, error_code
);
636 bool removed
= locks_
.Remove(my_lock
->name_
);
642 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
644 if (test_directory_
.empty()) {
645 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
648 RecordErrorAt(kGetTestDirectory
);
650 "Could not create temp directory.", "", kGetTestDirectory
);
653 *path
= FilePathToString(test_directory_
);
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 {
678 RecordErrorAt(method
);
679 GetOSErrorHistogram(method
, -base::File::FILE_ERROR_MAX
)->Add(-error
);
682 void ChromiumEnv::RecordOSError(MethodID method
, int error
) const {
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
,
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");
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),
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
{
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
);
768 virtual void ThreadMain() {
774 void (*function_
)(void* arg
);
778 void ChromiumEnv::Schedule(ScheduleFunc
* function
, void* arg
) {
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
789 if (queue_
.empty()) {
793 // Add to priority queue
794 queue_
.push_back(BGItem());
795 queue_
.back().function
= function
;
796 queue_
.back().arg
= arg
;
801 void ChromiumEnv::BGThread() {
802 base::PlatformThread::SetName(name_
.c_str());
805 // Wait until there is an item that is ready to run
807 while (queue_
.empty()) {
811 void (*function
)(void*) = queue_
.front().function
;
812 void* arg
= queue_
.front().arg
;
816 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
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
850 return leveldb_env::idb_env
.Pointer();
853 Env
* Env::Default() {
854 return leveldb_env::default_env
.Pointer();
857 } // namespace leveldb