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 "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"
15 #include "base/command_line.h"
16 #include "base/win/win_util.h"
17 #include "env_chromium_win.h"
20 using namespace leveldb
;
22 namespace leveldb_env
{
26 const base::FilePath::CharType backup_table_extension
[] =
27 FILE_PATH_LITERAL(".bak");
28 const base::FilePath::CharType table_extension
[] = FILE_PATH_LITERAL(".ldb");
30 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix
[]
31 = FILE_PATH_LITERAL("leveldb-test-");
33 class ChromiumFileLock
: public FileLock
{
41 Retrier(MethodID method
, RetrierProvider
* provider
)
42 : start_(base::TimeTicks::Now()),
43 limit_(start_
+ base::TimeDelta::FromMilliseconds(
44 provider
->MaxRetryTimeMillis())),
46 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
49 last_error_(base::File::FILE_OK
),
50 provider_(provider
) {}
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_
);
60 bool ShouldKeepTrying(base::File::Error last_error
) {
61 DCHECK_NE(last_error
, base::File::FILE_OK
);
62 last_error_
= last_error
;
64 base::PlatformThread::Sleep(time_to_sleep_
);
65 last_
= base::TimeTicks::Now();
73 base::TimeTicks start_
;
74 base::TimeTicks limit_
;
75 base::TimeTicks last_
;
76 base::TimeDelta time_to_sleep_
;
79 base::File::Error last_error_
;
80 RetrierProvider
* provider_
;
83 class IDBEnvStdio
: public ChromiumEnvStdio
{
85 IDBEnvStdio() : ChromiumEnvStdio() {
86 name_
= "LevelDBEnv.IDB";
92 class IDBEnvWin
: public ChromiumEnvWin
{
94 IDBEnvWin() : ChromiumEnvWin() {
95 name_
= "LevelDBEnv.IDB";
102 ::base::LazyInstance
<IDBEnvWin
>::Leaky idb_env
=
103 LAZY_INSTANCE_INITIALIZER
;
105 ::base::LazyInstance
<IDBEnvStdio
>::Leaky idb_env
=
106 LAZY_INSTANCE_INITIALIZER
;
109 ::base::LazyInstance
<ChromiumEnvStdio
>::Leaky default_env
=
110 LAZY_INSTANCE_INITIALIZER
;
112 } // unnamed namespace
114 const char* MethodIDToString(MethodID 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";
143 return "GetFileSize";
150 case kGetTestDirectory
:
151 return "GetTestDirectory";
157 return "GetChildren";
160 return "kNumEntries";
166 Status
MakeIOError(Slice filename
,
173 "%s (ChromeMethodErrno: %d::%s::%d)",
176 MethodIDToString(method
),
178 return Status::IOError(filename
, buf
);
181 Status
MakeIOError(Slice filename
,
184 base::File::Error error
) {
189 "%s (ChromeMethodPFE: %d::%s::%d)",
192 MethodIDToString(method
),
194 return Status::IOError(filename
, buf
);
197 Status
MakeIOError(Slice filename
, const char* message
, MethodID method
) {
201 "%s (ChromeMethodOnly: %d::%s)",
204 MethodIDToString(method
));
205 return Status::IOError(filename
, buf
);
208 ErrorParsingResult
ParseMethodAndError(const char* string
,
209 MethodID
* method_param
,
212 if (RE2::PartialMatch(string
, "ChromeMethodOnly: (\\d+)", &method
)) {
213 *method_param
= static_cast<MethodID
>(method
);
216 if (RE2::PartialMatch(
217 string
, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method
, error
)) {
219 *method_param
= static_cast<MethodID
>(method
);
220 return METHOD_AND_PFE
;
222 if (RE2::PartialMatch(
223 string
, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method
, error
)) {
224 *method_param
= static_cast<MethodID
>(method
);
225 return METHOD_AND_ERRNO
;
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
233 const char* patterns
[] = {
235 "log record too small",
236 "corrupted internal key",
238 "missing start of fragmented record",
239 "error in middle of record",
240 "unknown record type",
241 "truncated record at end",
244 "FileReader invoked with unexpected value",
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",
258 "truncated block read",
259 "block checksum mismatch",
261 "corrupted compressed block contents",
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
) {
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;
290 std::string
GetCorruptionMessage(const leveldb::Status
& status
) {
291 int code
= GetCorruptionCode(status
);
293 return "Unknown corruption";
294 return patterns
[code
- 1];
297 bool IndicatesDiskFull(const leveldb::Status
& status
) {
300 leveldb_env::MethodID method
;
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
);
310 bool IsIOError(const leveldb::Status
& status
) {
311 leveldb_env::MethodID method
;
313 leveldb_env::ErrorParsingResult result
= leveldb_env::ParseMethodAndError(
314 status
.ToString().c_str(), &method
, &error
);
315 return result
!= leveldb_env::NONE
;
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
));
324 std::string
FilePathToString(const base::FilePath
& file_path
) {
326 return base::UTF16ToUTF8(file_path
.value());
328 return file_path
.value();
332 base::FilePath
ChromiumEnv::CreateFilePath(const std::string
& file_path
) {
334 return base::FilePath(base::UTF8ToUTF16(file_path
));
336 return base::FilePath(file_path
);
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
);
347 bool ChromiumEnv::HasTableExtension(const base::FilePath
& path
)
349 return path
.MatchesExtension(table_extension
);
352 ChromiumEnv::ChromiumEnv()
353 : name_("LevelDBEnv"),
356 started_bgthread_(false),
357 kMaxRetryTimeMillis(1000) {
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.
366 bool ChromiumEnv::FileExists(const std::string
& fname
) {
367 return ::base::PathExists(CreateFilePath(fname
));
370 const char* ChromiumEnv::FileErrorString(::base::File::Error 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
:
406 case ::base::File::FILE_ERROR_MAX
:
410 return "Unknown error.";
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
),
418 std::string
uma_name(name_
);
419 uma_name
.append(".TableRestore");
420 base::BooleanHistogram::FactoryGet(
421 uma_name
, base::Histogram::kUmaTargetedHistogramFlag
)->AddBoolean(result
);
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();
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());
438 std::set
<base::FilePath
> backups_only
;
439 std::set_difference(backups_found
.begin(),
441 tables_found
.begin(),
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
,
453 base::Histogram::kUmaTargetedHistogramFlag
)
454 ->Add(num_missing_files
);
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();
460 base::FilePath restored_table_name
=
461 RestoreFromBackup(dir_filepath
.Append(*it
));
462 result
->push_back(FilePathToString(restored_table_name
.BaseName()));
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
);
474 dir_string
, "Could not open/read directory", kGetChildren
, error
);
477 for (std::vector
<base::FilePath
>::iterator it
= entries
.begin();
480 result
->push_back(FilePathToString(*it
));
484 RestoreIfNecessary(dir_string
, result
);
488 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
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
);
496 if (make_backup_
&& fname_filepath
.MatchesExtension(table_extension
)) {
497 base::DeleteFile(fname_filepath
.ReplaceExtension(backup_table_extension
),
503 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
505 base::File::Error error
= base::File::FILE_OK
;
506 Retrier
retrier(kCreateDir
, this);
508 if (base::CreateDirectoryAndGetError(CreateFilePath(name
), &error
))
510 } while (retrier
.ShouldKeepTrying(error
));
511 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
512 RecordOSError(kCreateDir
, error
);
516 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
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
);
526 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
529 if (!::base::GetFileSize(CreateFilePath(fname
), &signed_size
)) {
531 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
532 RecordErrorAt(kGetFileSize
);
534 *size
= static_cast<uint64_t>(signed_size
);
539 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
541 base::FilePath src_file_path
= CreateFilePath(src
);
542 if (!::base::PathExists(src_file_path
))
544 base::FilePath destination
= CreateFilePath(dst
);
546 Retrier
retrier(kRenameFile
, this);
547 base::File::Error error
= base::File::FILE_OK
;
549 if (base::ReplaceFile(src_file_path
, destination
, &error
))
551 } while (retrier
.ShouldKeepTrying(error
));
553 DCHECK(error
!= base::File::FILE_OK
);
554 RecordOSError(kRenameFile
, error
);
558 "Could not rename file: %s",
559 FileErrorString(error
));
560 return MakeIOError(src
, buf
, kRenameFile
, error
);
563 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
566 int flags
= ::base::File::FLAG_OPEN_ALWAYS
|
567 ::base::File::FLAG_READ
|
568 ::base::File::FLAG_WRITE
;
569 ::base::File::Error error_code
;
571 Retrier
retrier(kLockFile
, this);
573 file
.Initialize(CreateFilePath(fname
), flags
);
575 error_code
= file
.error_details();
576 } while (!file
.IsValid() && retrier
.ShouldKeepTrying(error_code
));
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;
584 if (base::DirectoryExists(parent
))
586 ++num_missing_ancestors
;
587 last_parent
= parent
;
588 parent
= parent
.DirName();
589 } while (parent
!= last_parent
);
590 RecordLockFileAncestors(num_missing_ancestors
);
593 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
595 RecordOSError(kLockFile
, error_code
);
599 if (!locks_
.Insert(fname
)) {
600 result
= MakeIOError(fname
, "Lock file already locked.", kLockFile
);
604 Retrier lock_retrier
= Retrier(kLockFile
, this);
606 error_code
= file
.Lock();
607 } while (error_code
!= ::base::File::FILE_OK
&&
608 retrier
.ShouldKeepTrying(error_code
));
610 if (error_code
!= ::base::File::FILE_OK
) {
611 locks_
.Remove(fname
);
612 result
= MakeIOError(fname
, FileErrorString(error_code
), kLockFile
,
614 RecordOSError(kLockFile
, error_code
);
618 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
619 my_lock
->file_
= file
.Pass();
620 my_lock
->name_
= fname
;
625 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
626 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
629 ::base::File::Error error_code
= my_lock
->file_
.Unlock();
630 if (error_code
!= ::base::File::FILE_OK
) {
632 MakeIOError(my_lock
->name_
, "Could not unlock lock file.", kUnlockFile
);
633 RecordOSError(kUnlockFile
, error_code
);
635 bool removed
= locks_
.Remove(my_lock
->name_
);
641 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
643 if (test_directory_
.empty()) {
644 if (!base::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
647 RecordErrorAt(kGetTestDirectory
);
649 "Could not create temp directory.", "", kGetTestDirectory
);
652 *path
= FilePathToString(test_directory_
);
657 uint64_t ChromiumEnv::NowMicros() {
658 return ::base::TimeTicks::Now().ToInternalValue();
661 void ChromiumEnv::SleepForMicroseconds(int micros
) {
662 // Round up to the next millisecond.
663 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros
));
666 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
667 GetMethodIOErrorHistogram()->Add(method
);
670 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
671 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
674 void ChromiumEnv::RecordOSError(MethodID method
,
675 base::File::Error error
) const {
677 RecordErrorAt(method
);
678 GetOSErrorHistogram(method
, -base::File::FILE_ERROR_MAX
)->Add(-error
);
681 void ChromiumEnv::RecordOSError(MethodID method
, int error
) const {
683 RecordErrorAt(method
);
684 GetOSErrorHistogram(method
, ERANGE
+ 1)->Add(error
);
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
);
694 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
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
);
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
);
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
);
723 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
724 std::string
uma_name(name_
);
725 uma_name
.append(".LockFileAncestorsNotFound");
728 const int kNumBuckets
= 11;
729 return base::LinearHistogram::FactoryGet(
730 uma_name
, kMin
, kMax
, kNumBuckets
,
731 base::Histogram::kUmaTargetedHistogramFlag
);
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
));
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),
746 base::Histogram::kUmaTargetedHistogramFlag
);
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
);
758 class Thread
: public ::base::PlatformThread::Delegate
{
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
);
767 virtual void ThreadMain() {
773 void (*function_
)(void* arg
);
777 void ChromiumEnv::Schedule(void (*function
)(void*), void* arg
) {
780 // Start background thread if necessary
781 if (!started_bgthread_
) {
782 started_bgthread_
= true;
783 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
786 // If the queue is currently empty, the background thread may currently be
788 if (queue_
.empty()) {
792 // Add to priority queue
793 queue_
.push_back(BGItem());
794 queue_
.back().function
= function
;
795 queue_
.back().arg
= arg
;
800 void ChromiumEnv::BGThread() {
801 base::PlatformThread::SetName(name_
.c_str());
804 // Wait until there is an item that is ready to run
806 while (queue_
.empty()) {
810 void (*function
)(void*) = queue_
.front().function
;
811 void* arg
= queue_
.front().arg
;
815 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
820 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
821 new Thread(function
, arg
); // Will self-delete.
824 static std::string
GetDirName(const std::string
& filename
) {
825 base::FilePath file
= base::FilePath::FromUTF8Unsafe(filename
);
826 return FilePathToString(file
.DirName());
829 void ChromiumEnv::DidCreateNewFile(const std::string
& filename
) {
830 base::AutoLock
auto_lock(map_lock_
);
831 needs_sync_map_
[GetDirName(filename
)] = true;
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();
839 void ChromiumEnv::DidSyncDir(const std::string
& filename
) {
840 base::AutoLock
auto_lock(map_lock_
);
841 needs_sync_map_
.erase(GetDirName(filename
));
844 } // namespace leveldb_env
849 return leveldb_env::idb_env
.Pointer();
852 Env
* Env::Default() {
853 return leveldb_env::default_env
.Pointer();
856 } // namespace leveldb