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.
10 #include "base/at_exit.h"
11 #include "base/debug/trace_event.h"
12 #include "base/file_util.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/files/file_path.h"
15 #include "base/lazy_instance.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/histogram.h"
19 #include "base/platform_file.h"
20 #include "base/posix/eintr_wrapper.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/synchronization/lock.h"
23 #include "base/sys_info.h"
24 #include "base/threading/platform_thread.h"
25 #include "base/threading/thread.h"
26 #include "chromium_logger.h"
27 #include "env_chromium.h"
28 #include "leveldb/env.h"
29 #include "leveldb/slice.h"
30 #include "port/port.h"
31 #include "third_party/re2/re2/re2.h"
32 #include "util/logging.h"
36 #include "base/win/win_util.h"
41 #include <sys/resource.h>
45 using namespace leveldb
;
47 namespace leveldb_env
{
51 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_ANDROID) || \
53 // The following are glibc-specific
55 size_t fread_wrapper(void *ptr
, size_t size
, size_t n
, FILE *file
) {
56 return fread(ptr
, size
, n
, file
);
59 size_t fwrite_wrapper(const void *ptr
, size_t size
, size_t n
, FILE *file
) {
60 return fwrite(ptr
, size
, n
, file
);
63 int fflush_wrapper(FILE *file
) {
67 #if !defined(OS_ANDROID)
68 int fdatasync(int fildes
) {
70 return _commit(fildes
);
72 return HANDLE_EINTR(fsync(fildes
));
80 // This class should be deleted if it doesn't turn up anything useful after
81 // going to stable (chrome 29).
83 TryToLockFILE(FILE* file
) : file_to_unlock_(NULL
) {
84 if (ftrylockfile(file
) == 0)
85 file_to_unlock_
= file
;
87 UMA_HISTOGRAM_BOOLEAN("LevelDBEnv.All.SafeThreadAccess", false);
91 funlockfile(file_to_unlock_
);
95 FILE* file_to_unlock_
;
98 size_t fread_wrapper(void *ptr
, size_t size
, size_t n
, FILE *file
) {
99 TryToLockFILE
lock(file
);
100 return fread_unlocked(ptr
, size
, n
, file
);
103 size_t fwrite_wrapper(const void *ptr
, size_t size
, size_t n
, FILE *file
) {
104 TryToLockFILE
lock(file
);
105 return fwrite_unlocked(ptr
, size
, n
, file
);
108 int fflush_wrapper(FILE *file
) {
109 TryToLockFILE
lock(file
);
110 return fflush_unlocked(file
);
115 // Wide-char safe fopen wrapper.
116 FILE* fopen_internal(const char* fname
, const char* mode
) {
118 return _wfopen(UTF8ToUTF16(fname
).c_str(), ASCIIToUTF16(mode
).c_str());
120 return fopen(fname
, mode
);
124 base::FilePath
CreateFilePath(const std::string
& file_path
) {
126 return base::FilePath(UTF8ToUTF16(file_path
));
128 return base::FilePath(file_path
);
132 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix
[]
133 = FILE_PATH_LITERAL("leveldb-test-");
135 const char* PlatformFileErrorString(const ::base::PlatformFileError
& error
) {
137 case ::base::PLATFORM_FILE_ERROR_FAILED
:
138 return "No further details.";
139 case ::base::PLATFORM_FILE_ERROR_IN_USE
:
140 return "File currently in use.";
141 case ::base::PLATFORM_FILE_ERROR_EXISTS
:
142 return "File already exists.";
143 case ::base::PLATFORM_FILE_ERROR_NOT_FOUND
:
144 return "File not found.";
145 case ::base::PLATFORM_FILE_ERROR_ACCESS_DENIED
:
146 return "Access denied.";
147 case ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED
:
148 return "Too many files open.";
149 case ::base::PLATFORM_FILE_ERROR_NO_MEMORY
:
150 return "Out of memory.";
151 case ::base::PLATFORM_FILE_ERROR_NO_SPACE
:
152 return "No space left on drive.";
153 case ::base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
:
154 return "Not a directory.";
155 case ::base::PLATFORM_FILE_ERROR_INVALID_OPERATION
:
156 return "Invalid operation.";
157 case ::base::PLATFORM_FILE_ERROR_SECURITY
:
158 return "Security error.";
159 case ::base::PLATFORM_FILE_ERROR_ABORT
:
160 return "File operation aborted.";
161 case ::base::PLATFORM_FILE_ERROR_NOT_A_FILE
:
162 return "The supplied path was not a file.";
163 case ::base::PLATFORM_FILE_ERROR_NOT_EMPTY
:
164 return "The file was not empty.";
165 case ::base::PLATFORM_FILE_ERROR_INVALID_URL
:
166 return "Invalid URL.";
167 case ::base::PLATFORM_FILE_ERROR_IO
:
168 return "OS or hardware error.";
169 case ::base::PLATFORM_FILE_OK
:
171 case ::base::PLATFORM_FILE_ERROR_MAX
:
175 return "Unknown error.";
178 class ChromiumSequentialFile
: public SequentialFile
{
180 std::string filename_
;
182 const UMALogger
* uma_logger_
;
185 ChromiumSequentialFile(const std::string
& fname
, FILE* f
,
186 const UMALogger
* uma_logger
)
187 : filename_(fname
), file_(f
), uma_logger_(uma_logger
) { }
188 virtual ~ChromiumSequentialFile() { fclose(file_
); }
190 virtual Status
Read(size_t n
, Slice
* result
, char* scratch
) {
192 size_t r
= fread_wrapper(scratch
, 1, n
, file_
);
193 *result
= Slice(scratch
, r
);
196 // We leave status as ok if we hit the end of the file
198 // A partial read with an error: return a non-ok status
199 s
= MakeIOError(filename_
, strerror(errno
), kSequentialFileRead
, errno
);
200 uma_logger_
->RecordErrorAt(kSequentialFileRead
);
206 virtual Status
Skip(uint64_t n
) {
207 if (fseek(file_
, n
, SEEK_CUR
)) {
208 int saved_errno
= errno
;
209 uma_logger_
->RecordErrorAt(kSequentialFileSkip
);
211 filename_
, strerror(saved_errno
), kSequentialFileSkip
, saved_errno
);
217 class ChromiumRandomAccessFile
: public RandomAccessFile
{
219 std::string filename_
;
220 ::base::PlatformFile file_
;
221 const UMALogger
* uma_logger_
;
224 ChromiumRandomAccessFile(const std::string
& fname
, ::base::PlatformFile file
,
225 const UMALogger
* uma_logger
)
226 : filename_(fname
), file_(file
), uma_logger_(uma_logger
) { }
227 virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_
); }
229 virtual Status
Read(uint64_t offset
, size_t n
, Slice
* result
,
230 char* scratch
) const {
232 int r
= ::base::ReadPlatformFile(file_
, offset
, scratch
, n
);
233 *result
= Slice(scratch
, (r
< 0) ? 0 : r
);
235 // An error: return a non-ok status
237 filename_
, "Could not perform read", kRandomAccessFileRead
);
238 uma_logger_
->RecordErrorAt(kRandomAccessFileRead
);
244 class ChromiumFileLock
: public FileLock
{
246 ::base::PlatformFile file_
;
251 Retrier(MethodID method
, RetrierProvider
* provider
)
252 : start_(base::TimeTicks::Now()),
253 limit_(start_
+ base::TimeDelta::FromMilliseconds(
254 provider
->MaxRetryTimeMillis())),
256 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
259 last_error_(base::PLATFORM_FILE_OK
),
260 provider_(provider
) {}
263 provider_
->GetRetryTimeHistogram(method_
)->AddTime(last_
- start_
);
264 if (last_error_
!= base::PLATFORM_FILE_OK
) {
265 DCHECK(last_error_
< 0);
266 provider_
->GetRecoveredFromErrorHistogram(method_
)->Add(-last_error_
);
270 bool ShouldKeepTrying(base::PlatformFileError last_error
) {
271 DCHECK_NE(last_error
, base::PLATFORM_FILE_OK
);
272 last_error_
= last_error
;
273 if (last_
< limit_
) {
274 base::PlatformThread::Sleep(time_to_sleep_
);
275 last_
= base::TimeTicks::Now();
283 base::TimeTicks start_
;
284 base::TimeTicks limit_
;
285 base::TimeTicks last_
;
286 base::TimeDelta time_to_sleep_
;
289 base::PlatformFileError last_error_
;
290 RetrierProvider
* provider_
;
293 class IDBEnv
: public ChromiumEnv
{
295 IDBEnv() : ChromiumEnv() { name_
= "LevelDBEnv.IDB"; }
298 ::base::LazyInstance
<IDBEnv
>::Leaky idb_env
= LAZY_INSTANCE_INITIALIZER
;
300 ::base::LazyInstance
<ChromiumEnv
>::Leaky default_env
=
301 LAZY_INSTANCE_INITIALIZER
;
303 } // unnamed namespace
305 const char* MethodIDToString(MethodID method
) {
307 case kSequentialFileRead
:
308 return "SequentialFileRead";
309 case kSequentialFileSkip
:
310 return "SequentialFileSkip";
311 case kRandomAccessFileRead
:
312 return "RandomAccessFileRead";
313 case kWritableFileAppend
:
314 return "WritableFileAppend";
315 case kWritableFileClose
:
316 return "WritableFileClose";
317 case kWritableFileFlush
:
318 return "WritableFileFlush";
319 case kWritableFileSync
:
320 return "WritableFileSync";
321 case kNewSequentialFile
:
322 return "NewSequentialFile";
323 case kNewRandomAccessFile
:
324 return "NewRandomAccessFile";
325 case kNewWritableFile
:
326 return "NewWritableFile";
334 return "GetFileSize";
341 case kGetTestDirectory
:
342 return "GetTestDirectory";
349 return "kNumEntries";
355 Status
MakeIOError(Slice filename
,
362 "%s (ChromeMethodErrno: %d::%s::%d)",
365 MethodIDToString(method
),
367 return Status::IOError(filename
, buf
);
370 Status
MakeIOError(Slice filename
,
373 base::PlatformFileError error
) {
378 "%s (ChromeMethodPFE: %d::%s::%d)",
381 MethodIDToString(method
),
383 return Status::IOError(filename
, buf
);
386 Status
MakeIOError(Slice filename
, const char* message
, MethodID method
) {
390 "%s (ChromeMethodOnly: %d::%s)",
393 MethodIDToString(method
));
394 return Status::IOError(filename
, buf
);
397 ErrorParsingResult
ParseMethodAndError(const char* string
,
398 MethodID
* method_param
,
401 if (RE2::PartialMatch(string
, "ChromeMethodOnly: (\\d+)", &method
)) {
402 *method_param
= static_cast<MethodID
>(method
);
405 if (RE2::PartialMatch(
406 string
, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method
, error
)) {
408 *method_param
= static_cast<MethodID
>(method
);
409 return METHOD_AND_PFE
;
411 if (RE2::PartialMatch(
412 string
, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method
, error
)) {
413 *method_param
= static_cast<MethodID
>(method
);
414 return METHOD_AND_ERRNO
;
419 std::string
FilePathToString(const base::FilePath
& file_path
) {
421 return UTF16ToUTF8(file_path
.value());
423 return file_path
.value();
427 ChromiumWritableFile::ChromiumWritableFile(const std::string
& fname
,
429 const UMALogger
* uma_logger
,
430 WriteTracker
* tracker
)
431 : filename_(fname
), file_(f
), uma_logger_(uma_logger
), tracker_(tracker
) {
432 base::FilePath path
= base::FilePath::FromUTF8Unsafe(fname
);
434 FilePathToString(path
.BaseName()).find("MANIFEST") !=
437 tracker_
->DidCreateNewFile(filename_
);
438 parent_dir_
= FilePathToString(CreateFilePath(fname
).DirName());
441 ChromiumWritableFile::~ChromiumWritableFile() {
443 // Ignoring any potential errors
448 Status
ChromiumWritableFile::SyncParent() {
451 TRACE_EVENT0("leveldb", "SyncParent");
454 HANDLE_EINTR(open(parent_dir_
.c_str(), O_RDONLY
));
456 return MakeIOError(parent_dir_
, strerror(errno
), kSyncParent
);
457 if (HANDLE_EINTR(fsync(parent_fd
)) != 0) {
458 s
= MakeIOError(parent_dir_
, strerror(errno
), kSyncParent
);
460 HANDLE_EINTR(close(parent_fd
));
465 Status
ChromiumWritableFile::Append(const Slice
& data
) {
466 if (is_manifest_
&& tracker_
->DoesDirNeedSync(filename_
)) {
467 Status s
= SyncParent();
470 tracker_
->DidSyncDir(filename_
);
473 size_t r
= fwrite_wrapper(data
.data(), 1, data
.size(), file_
);
474 if (r
!= data
.size()) {
475 int saved_errno
= errno
;
476 uma_logger_
->RecordOSError(kWritableFileAppend
, saved_errno
);
478 filename_
, strerror(saved_errno
), kWritableFileAppend
, saved_errno
);
483 Status
ChromiumWritableFile::Close() {
485 if (fclose(file_
) != 0) {
486 result
= MakeIOError(filename_
, strerror(errno
), kWritableFileClose
, errno
);
487 uma_logger_
->RecordErrorAt(kWritableFileClose
);
493 Status
ChromiumWritableFile::Flush() {
495 if (HANDLE_EINTR(fflush_wrapper(file_
))) {
496 int saved_errno
= errno
;
497 result
= MakeIOError(
498 filename_
, strerror(saved_errno
), kWritableFileFlush
, saved_errno
);
499 uma_logger_
->RecordOSError(kWritableFileFlush
, saved_errno
);
504 Status
ChromiumWritableFile::Sync() {
505 TRACE_EVENT0("leveldb", "ChromiumEnv::Sync");
509 if (HANDLE_EINTR(fflush_wrapper(file_
)))
511 // Sync even if fflush gave an error; perhaps the data actually got out,
512 // even though something went wrong.
513 if (fdatasync(fileno(file_
)) && !error
)
515 // Report the first error we found.
517 result
= MakeIOError(filename_
, strerror(error
), kWritableFileSync
, error
);
518 uma_logger_
->RecordErrorAt(kWritableFileSync
);
523 ChromiumEnv::ChromiumEnv()
524 : name_("LevelDBEnv"),
526 started_bgthread_(false),
527 kMaxRetryTimeMillis(1000) {
530 ChromiumEnv::~ChromiumEnv() {
531 // In chromium, ChromiumEnv is leaked. It'd be nice to add NOTREACHED here to
532 // ensure that behavior isn't accidentally changed, but there's an instance in
533 // a unit test that is deleted.
536 Status
ChromiumEnv::NewSequentialFile(const std::string
& fname
,
537 SequentialFile
** result
) {
538 FILE* f
= fopen_internal(fname
.c_str(), "rb");
541 int saved_errno
= errno
;
542 RecordOSError(kNewSequentialFile
, saved_errno
);
544 fname
, strerror(saved_errno
), kNewSequentialFile
, saved_errno
);
546 *result
= new ChromiumSequentialFile(fname
, f
, this);
551 void ChromiumEnv::RecordOpenFilesLimit(const std::string
& type
) {
552 #if defined(OS_POSIX)
553 struct rlimit nofile
;
554 if (getrlimit(RLIMIT_NOFILE
, &nofile
))
556 GetMaxFDHistogram(type
)->Add(nofile
.rlim_cur
);
560 Status
ChromiumEnv::NewRandomAccessFile(const std::string
& fname
,
561 RandomAccessFile
** result
) {
562 int flags
= ::base::PLATFORM_FILE_READ
| ::base::PLATFORM_FILE_OPEN
;
564 ::base::PlatformFileError error_code
;
565 ::base::PlatformFile file
= ::base::CreatePlatformFile(
566 CreateFilePath(fname
), flags
, &created
, &error_code
);
567 if (error_code
== ::base::PLATFORM_FILE_OK
) {
568 *result
= new ChromiumRandomAccessFile(fname
, file
, this);
569 RecordOpenFilesLimit("Success");
572 if (error_code
== ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED
)
573 RecordOpenFilesLimit("TooManyOpened");
575 RecordOpenFilesLimit("OtherError");
577 RecordOSError(kNewRandomAccessFile
, error_code
);
578 return MakeIOError(fname
,
579 PlatformFileErrorString(error_code
),
580 kNewRandomAccessFile
,
584 Status
ChromiumEnv::NewWritableFile(const std::string
& fname
,
585 WritableFile
** result
) {
587 FILE* f
= fopen_internal(fname
.c_str(), "wb");
589 int saved_errno
= errno
;
590 RecordErrorAt(kNewWritableFile
);
592 fname
, strerror(saved_errno
), kNewWritableFile
, saved_errno
);
594 *result
= new ChromiumWritableFile(fname
, f
, this, this);
599 bool ChromiumEnv::FileExists(const std::string
& fname
) {
600 return ::base::PathExists(CreateFilePath(fname
));
603 Status
ChromiumEnv::GetChildren(const std::string
& dir
,
604 std::vector
<std::string
>* result
) {
606 base::FileEnumerator
iter(
607 CreateFilePath(dir
), false, base::FileEnumerator::FILES
);
608 base::FilePath current
= iter
.Next();
609 while (!current
.empty()) {
610 result
->push_back(FilePathToString(current
.BaseName()));
611 current
= iter
.Next();
613 // TODO(jorlow): Unfortunately, the FileEnumerator swallows errors, so
614 // we'll always return OK. Maybe manually check for error
615 // conditions like the file not existing?
619 Status
ChromiumEnv::DeleteFile(const std::string
& fname
) {
621 // TODO(jorlow): Should we assert this is a file?
622 if (!::base::DeleteFile(CreateFilePath(fname
), false)) {
623 result
= MakeIOError(fname
, "Could not delete file.", kDeleteFile
);
624 RecordErrorAt(kDeleteFile
);
629 Status
ChromiumEnv::CreateDir(const std::string
& name
) {
631 base::PlatformFileError error
= base::PLATFORM_FILE_OK
;
632 Retrier
retrier(kCreateDir
, this);
634 if (::file_util::CreateDirectoryAndGetError(CreateFilePath(name
), &error
))
636 } while (retrier
.ShouldKeepTrying(error
));
637 result
= MakeIOError(name
, "Could not create directory.", kCreateDir
, error
);
638 RecordOSError(kCreateDir
, error
);
642 Status
ChromiumEnv::DeleteDir(const std::string
& name
) {
644 // TODO(jorlow): Should we assert this is a directory?
645 if (!::base::DeleteFile(CreateFilePath(name
), false)) {
646 result
= MakeIOError(name
, "Could not delete directory.", kDeleteDir
);
647 RecordErrorAt(kDeleteDir
);
652 Status
ChromiumEnv::GetFileSize(const std::string
& fname
, uint64_t* size
) {
655 if (!::file_util::GetFileSize(CreateFilePath(fname
), &signed_size
)) {
657 s
= MakeIOError(fname
, "Could not determine file size.", kGetFileSize
);
658 RecordErrorAt(kGetFileSize
);
660 *size
= static_cast<uint64_t>(signed_size
);
665 Status
ChromiumEnv::RenameFile(const std::string
& src
, const std::string
& dst
) {
667 base::FilePath src_file_path
= CreateFilePath(src
);
668 if (!::base::PathExists(src_file_path
))
670 base::FilePath destination
= CreateFilePath(dst
);
672 Retrier
retrier(kRenameFile
, this);
673 base::PlatformFileError error
= base::PLATFORM_FILE_OK
;
675 if (base::ReplaceFile(src_file_path
, destination
, &error
))
677 } while (retrier
.ShouldKeepTrying(error
));
679 DCHECK(error
!= base::PLATFORM_FILE_OK
);
680 RecordOSError(kRenameFile
, error
);
684 "Could not rename file: %s",
685 PlatformFileErrorString(error
));
686 return MakeIOError(src
, buf
, kRenameFile
, error
);
689 Status
ChromiumEnv::LockFile(const std::string
& fname
, FileLock
** lock
) {
692 int flags
= ::base::PLATFORM_FILE_OPEN_ALWAYS
|
693 ::base::PLATFORM_FILE_READ
|
694 ::base::PLATFORM_FILE_WRITE
|
695 ::base::PLATFORM_FILE_EXCLUSIVE_READ
|
696 ::base::PLATFORM_FILE_EXCLUSIVE_WRITE
;
698 ::base::PlatformFileError error_code
;
699 ::base::PlatformFile file
;
700 Retrier
retrier(kLockFile
, this);
702 file
= ::base::CreatePlatformFile(
703 CreateFilePath(fname
), flags
, &created
, &error_code
);
704 } while (error_code
!= ::base::PLATFORM_FILE_OK
&&
705 retrier
.ShouldKeepTrying(error_code
));
707 if (error_code
== ::base::PLATFORM_FILE_ERROR_NOT_FOUND
) {
708 ::base::FilePath parent
= CreateFilePath(fname
).DirName();
709 ::base::FilePath last_parent
;
710 int num_missing_ancestors
= 0;
712 if (base::DirectoryExists(parent
))
714 ++num_missing_ancestors
;
715 last_parent
= parent
;
716 parent
= parent
.DirName();
717 } while (parent
!= last_parent
);
718 RecordLockFileAncestors(num_missing_ancestors
);
721 if (error_code
!= ::base::PLATFORM_FILE_OK
) {
722 result
= MakeIOError(
723 fname
, PlatformFileErrorString(error_code
), kLockFile
, error_code
);
724 RecordOSError(kLockFile
, error_code
);
726 ChromiumFileLock
* my_lock
= new ChromiumFileLock
;
727 my_lock
->file_
= file
;
733 Status
ChromiumEnv::UnlockFile(FileLock
* lock
) {
734 ChromiumFileLock
* my_lock
= reinterpret_cast<ChromiumFileLock
*>(lock
);
736 if (!::base::ClosePlatformFile(my_lock
->file_
)) {
737 result
= MakeIOError("Could not close lock file.", "", kUnlockFile
);
738 RecordErrorAt(kUnlockFile
);
744 Status
ChromiumEnv::GetTestDirectory(std::string
* path
) {
746 if (test_directory_
.empty()) {
747 if (!::file_util::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix
,
750 RecordErrorAt(kGetTestDirectory
);
752 "Could not create temp directory.", "", kGetTestDirectory
);
755 *path
= FilePathToString(test_directory_
);
760 Status
ChromiumEnv::NewLogger(const std::string
& fname
, Logger
** result
) {
761 FILE* f
= fopen_internal(fname
.c_str(), "w");
764 int saved_errno
= errno
;
765 RecordOSError(kNewLogger
, saved_errno
);
766 return MakeIOError(fname
, strerror(saved_errno
), kNewLogger
, saved_errno
);
768 *result
= new ChromiumLogger(f
);
773 uint64_t ChromiumEnv::NowMicros() {
774 return ::base::TimeTicks::Now().ToInternalValue();
777 void ChromiumEnv::SleepForMicroseconds(int micros
) {
778 // Round up to the next millisecond.
779 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros
));
782 void ChromiumEnv::RecordErrorAt(MethodID method
) const {
783 GetMethodIOErrorHistogram()->Add(method
);
786 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors
) const {
787 GetLockFileAncestorHistogram()->Add(num_missing_ancestors
);
790 void ChromiumEnv::RecordOSError(MethodID method
,
791 base::PlatformFileError error
) const {
793 RecordErrorAt(method
);
794 GetOSErrorHistogram(method
, -base::PLATFORM_FILE_ERROR_MAX
)->Add(-error
);
797 void ChromiumEnv::RecordOSError(MethodID method
, int error
) const {
799 RecordErrorAt(method
);
800 GetOSErrorHistogram(method
, ERANGE
+ 1)->Add(error
);
803 base::HistogramBase
* ChromiumEnv::GetOSErrorHistogram(MethodID method
,
805 std::string
uma_name(name_
);
806 // TODO(dgrogan): This is probably not the best way to concatenate strings.
807 uma_name
.append(".IOError.").append(MethodIDToString(method
));
808 return base::LinearHistogram::FactoryGet(uma_name
, 1, limit
, limit
+ 1,
809 base::Histogram::kUmaTargetedHistogramFlag
);
812 base::HistogramBase
* ChromiumEnv::GetMethodIOErrorHistogram() const {
813 std::string
uma_name(name_
);
814 uma_name
.append(".IOError");
815 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
816 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
819 base::HistogramBase
* ChromiumEnv::GetMaxFDHistogram(
820 const std::string
& type
) const {
821 std::string
uma_name(name_
);
822 uma_name
.append(".MaxFDs.").append(type
);
823 // These numbers make each bucket twice as large as the previous bucket.
824 const int kFirstEntry
= 1;
825 const int kLastEntry
= 65536;
826 const int kNumBuckets
= 18;
827 return base::Histogram::FactoryGet(
828 uma_name
, kFirstEntry
, kLastEntry
, kNumBuckets
,
829 base::Histogram::kUmaTargetedHistogramFlag
);
832 base::HistogramBase
* ChromiumEnv::GetLockFileAncestorHistogram() const {
833 std::string
uma_name(name_
);
834 uma_name
.append(".LockFileAncestorsNotFound");
837 const int kNumBuckets
= 11;
838 return base::LinearHistogram::FactoryGet(
839 uma_name
, kMin
, kMax
, kNumBuckets
,
840 base::Histogram::kUmaTargetedHistogramFlag
);
843 base::HistogramBase
* ChromiumEnv::GetRetryTimeHistogram(MethodID method
) const {
844 std::string
uma_name(name_
);
845 // TODO(dgrogan): This is probably not the best way to concatenate strings.
846 uma_name
.append(".TimeUntilSuccessFor").append(MethodIDToString(method
));
848 const int kBucketSizeMillis
= 25;
849 // Add 2, 1 for each of the buckets <1 and >max.
850 const int kNumBuckets
= kMaxRetryTimeMillis
/ kBucketSizeMillis
+ 2;
851 return base::Histogram::FactoryTimeGet(
852 uma_name
, base::TimeDelta::FromMilliseconds(1),
853 base::TimeDelta::FromMilliseconds(kMaxRetryTimeMillis
+ 1),
855 base::Histogram::kUmaTargetedHistogramFlag
);
858 base::HistogramBase
* ChromiumEnv::GetRecoveredFromErrorHistogram(
859 MethodID method
) const {
860 std::string
uma_name(name_
);
861 uma_name
.append(".RetryRecoveredFromErrorIn")
862 .append(MethodIDToString(method
));
863 return base::LinearHistogram::FactoryGet(uma_name
, 1, kNumEntries
,
864 kNumEntries
+ 1, base::Histogram::kUmaTargetedHistogramFlag
);
867 class Thread
: public ::base::PlatformThread::Delegate
{
869 Thread(void (*function
)(void* arg
), void* arg
)
870 : function_(function
), arg_(arg
) {
871 ::base::PlatformThreadHandle handle
;
872 bool success
= ::base::PlatformThread::Create(0, this, &handle
);
876 virtual void ThreadMain() {
882 void (*function_
)(void* arg
);
886 void ChromiumEnv::Schedule(void (*function
)(void*), void* arg
) {
889 // Start background thread if necessary
890 if (!started_bgthread_
) {
891 started_bgthread_
= true;
892 StartThread(&ChromiumEnv::BGThreadWrapper
, this);
895 // If the queue is currently empty, the background thread may currently be
897 if (queue_
.empty()) {
901 // Add to priority queue
902 queue_
.push_back(BGItem());
903 queue_
.back().function
= function
;
904 queue_
.back().arg
= arg
;
909 void ChromiumEnv::BGThread() {
910 base::PlatformThread::SetName(name_
.c_str());
913 // Wait until there is an item that is ready to run
915 while (queue_
.empty()) {
919 void (*function
)(void*) = queue_
.front().function
;
920 void* arg
= queue_
.front().arg
;
924 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
929 void ChromiumEnv::StartThread(void (*function
)(void* arg
), void* arg
) {
930 new Thread(function
, arg
); // Will self-delete.
933 static std::string
GetDirName(const std::string
& filename
) {
934 base::FilePath file
= base::FilePath::FromUTF8Unsafe(filename
);
935 return FilePathToString(file
.DirName());
938 void ChromiumEnv::DidCreateNewFile(const std::string
& filename
) {
939 base::AutoLock
auto_lock(map_lock_
);
940 needs_sync_map_
[GetDirName(filename
)] = true;
943 bool ChromiumEnv::DoesDirNeedSync(const std::string
& filename
) {
944 base::AutoLock
auto_lock(map_lock_
);
945 return needs_sync_map_
.find(GetDirName(filename
)) != needs_sync_map_
.end();
948 void ChromiumEnv::DidSyncDir(const std::string
& filename
) {
949 base::AutoLock
auto_lock(map_lock_
);
950 needs_sync_map_
.erase(GetDirName(filename
));
953 } // namespace leveldb_env
958 return leveldb_env::idb_env
.Pointer();
961 Env
* Env::Default() {
962 return leveldb_env::default_env
.Pointer();
965 } // namespace leveldb