cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blob0333380fd2f3e6ac0dff4318b52eda1b0445144c
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 <errno.h>
6 #include <stdio.h>
8 #include <deque>
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"
34 #if defined(OS_WIN)
35 #include <io.h>
36 #include "base/win/win_util.h"
37 #endif
39 #if defined(OS_POSIX)
40 #include <fcntl.h>
41 #include <sys/resource.h>
42 #include <sys/time.h>
43 #endif
45 using namespace leveldb;
47 namespace leveldb_env {
49 namespace {
51 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_ANDROID) || \
52 defined(OS_OPENBSD)
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) {
64 return fflush(file);
67 #if !defined(OS_ANDROID)
68 int fdatasync(int fildes) {
69 #if defined(OS_WIN)
70 return _commit(fildes);
71 #else
72 return HANDLE_EINTR(fsync(fildes));
73 #endif
75 #endif
77 #else
79 class TryToLockFILE {
80 // This class should be deleted if it doesn't turn up anything useful after
81 // going to stable (chrome 29).
82 public:
83 TryToLockFILE(FILE* file) : file_to_unlock_(NULL) {
84 if (ftrylockfile(file) == 0)
85 file_to_unlock_ = file;
86 else
87 UMA_HISTOGRAM_BOOLEAN("LevelDBEnv.All.SafeThreadAccess", false);
89 ~TryToLockFILE() {
90 if (file_to_unlock_)
91 funlockfile(file_to_unlock_);
94 private:
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);
113 #endif
115 // Wide-char safe fopen wrapper.
116 FILE* fopen_internal(const char* fname, const char* mode) {
117 #if defined(OS_WIN)
118 return _wfopen(UTF8ToUTF16(fname).c_str(), ASCIIToUTF16(mode).c_str());
119 #else
120 return fopen(fname, mode);
121 #endif
124 base::FilePath CreateFilePath(const std::string& file_path) {
125 #if defined(OS_WIN)
126 return base::FilePath(UTF8ToUTF16(file_path));
127 #else
128 return base::FilePath(file_path);
129 #endif
132 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[]
133 = FILE_PATH_LITERAL("leveldb-test-");
135 const char* PlatformFileErrorString(const ::base::PlatformFileError& error) {
136 switch (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:
170 return "OK.";
171 case ::base::PLATFORM_FILE_ERROR_MAX:
172 NOTREACHED();
174 NOTIMPLEMENTED();
175 return "Unknown error.";
178 class ChromiumSequentialFile: public SequentialFile {
179 private:
180 std::string filename_;
181 FILE* file_;
182 const UMALogger* uma_logger_;
184 public:
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) {
191 Status s;
192 size_t r = fread_wrapper(scratch, 1, n, file_);
193 *result = Slice(scratch, r);
194 if (r < n) {
195 if (feof(file_)) {
196 // We leave status as ok if we hit the end of the file
197 } else {
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);
203 return s;
206 virtual Status Skip(uint64_t n) {
207 if (fseek(file_, n, SEEK_CUR)) {
208 int saved_errno = errno;
209 uma_logger_->RecordErrorAt(kSequentialFileSkip);
210 return MakeIOError(
211 filename_, strerror(saved_errno), kSequentialFileSkip, saved_errno);
213 return Status::OK();
217 class ChromiumRandomAccessFile: public RandomAccessFile {
218 private:
219 std::string filename_;
220 ::base::PlatformFile file_;
221 const UMALogger* uma_logger_;
223 public:
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 {
231 Status s;
232 int r = ::base::ReadPlatformFile(file_, offset, scratch, n);
233 *result = Slice(scratch, (r < 0) ? 0 : r);
234 if (r < 0) {
235 // An error: return a non-ok status
236 s = MakeIOError(
237 filename_, "Could not perform read", kRandomAccessFileRead);
238 uma_logger_->RecordErrorAt(kRandomAccessFileRead);
240 return s;
244 class ChromiumFileLock : public FileLock {
245 public:
246 ::base::PlatformFile file_;
249 class Retrier {
250 public:
251 Retrier(MethodID method, RetrierProvider* provider)
252 : start_(base::TimeTicks::Now()),
253 limit_(start_ + base::TimeDelta::FromMilliseconds(
254 provider->MaxRetryTimeMillis())),
255 last_(start_),
256 time_to_sleep_(base::TimeDelta::FromMilliseconds(10)),
257 success_(true),
258 method_(method),
259 last_error_(base::PLATFORM_FILE_OK),
260 provider_(provider) {}
261 ~Retrier() {
262 if (success_) {
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();
276 return true;
278 success_ = false;
279 return false;
282 private:
283 base::TimeTicks start_;
284 base::TimeTicks limit_;
285 base::TimeTicks last_;
286 base::TimeDelta time_to_sleep_;
287 bool success_;
288 MethodID method_;
289 base::PlatformFileError last_error_;
290 RetrierProvider* provider_;
293 class IDBEnv : public ChromiumEnv {
294 public:
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) {
306 switch (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";
327 case kDeleteFile:
328 return "DeleteFile";
329 case kCreateDir:
330 return "CreateDir";
331 case kDeleteDir:
332 return "DeleteDir";
333 case kGetFileSize:
334 return "GetFileSize";
335 case kRenameFile:
336 return "RenameFile";
337 case kLockFile:
338 return "LockFile";
339 case kUnlockFile:
340 return "UnlockFile";
341 case kGetTestDirectory:
342 return "GetTestDirectory";
343 case kNewLogger:
344 return "NewLogger";
345 case kSyncParent:
346 return "SyncParent";
347 case kNumEntries:
348 NOTREACHED();
349 return "kNumEntries";
351 NOTREACHED();
352 return "Unknown";
355 Status MakeIOError(Slice filename,
356 const char* message,
357 MethodID method,
358 int saved_errno) {
359 char buf[512];
360 snprintf(buf,
361 sizeof(buf),
362 "%s (ChromeMethodErrno: %d::%s::%d)",
363 message,
364 method,
365 MethodIDToString(method),
366 saved_errno);
367 return Status::IOError(filename, buf);
370 Status MakeIOError(Slice filename,
371 const char* message,
372 MethodID method,
373 base::PlatformFileError error) {
374 DCHECK(error < 0);
375 char buf[512];
376 snprintf(buf,
377 sizeof(buf),
378 "%s (ChromeMethodPFE: %d::%s::%d)",
379 message,
380 method,
381 MethodIDToString(method),
382 -error);
383 return Status::IOError(filename, buf);
386 Status MakeIOError(Slice filename, const char* message, MethodID method) {
387 char buf[512];
388 snprintf(buf,
389 sizeof(buf),
390 "%s (ChromeMethodOnly: %d::%s)",
391 message,
392 method,
393 MethodIDToString(method));
394 return Status::IOError(filename, buf);
397 ErrorParsingResult ParseMethodAndError(const char* string,
398 MethodID* method_param,
399 int* error) {
400 int method;
401 if (RE2::PartialMatch(string, "ChromeMethodOnly: (\\d+)", &method)) {
402 *method_param = static_cast<MethodID>(method);
403 return METHOD_ONLY;
405 if (RE2::PartialMatch(
406 string, "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method, error)) {
407 *error = -*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;
416 return NONE;
419 std::string FilePathToString(const base::FilePath& file_path) {
420 #if defined(OS_WIN)
421 return UTF16ToUTF8(file_path.value());
422 #else
423 return file_path.value();
424 #endif
427 ChromiumWritableFile::ChromiumWritableFile(const std::string& fname,
428 FILE* f,
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);
433 is_manifest_ =
434 FilePathToString(path.BaseName()).find("MANIFEST") !=
435 std::string::npos;
436 if (!is_manifest_)
437 tracker_->DidCreateNewFile(filename_);
438 parent_dir_ = FilePathToString(CreateFilePath(fname).DirName());
441 ChromiumWritableFile::~ChromiumWritableFile() {
442 if (file_ != NULL) {
443 // Ignoring any potential errors
444 fclose(file_);
448 Status ChromiumWritableFile::SyncParent() {
449 Status s;
450 #if !defined(OS_WIN)
451 TRACE_EVENT0("leveldb", "SyncParent");
453 int parent_fd =
454 HANDLE_EINTR(open(parent_dir_.c_str(), O_RDONLY));
455 if (parent_fd < 0)
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));
461 #endif
462 return s;
465 Status ChromiumWritableFile::Append(const Slice& data) {
466 if (is_manifest_ && tracker_->DoesDirNeedSync(filename_)) {
467 Status s = SyncParent();
468 if (!s.ok())
469 return s;
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);
477 return MakeIOError(
478 filename_, strerror(saved_errno), kWritableFileAppend, saved_errno);
480 return Status::OK();
483 Status ChromiumWritableFile::Close() {
484 Status result;
485 if (fclose(file_) != 0) {
486 result = MakeIOError(filename_, strerror(errno), kWritableFileClose, errno);
487 uma_logger_->RecordErrorAt(kWritableFileClose);
489 file_ = NULL;
490 return result;
493 Status ChromiumWritableFile::Flush() {
494 Status result;
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);
501 return result;
504 Status ChromiumWritableFile::Sync() {
505 TRACE_EVENT0("leveldb", "ChromiumEnv::Sync");
506 Status result;
507 int error = 0;
509 if (HANDLE_EINTR(fflush_wrapper(file_)))
510 error = errno;
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)
514 error = errno;
515 // Report the first error we found.
516 if (error) {
517 result = MakeIOError(filename_, strerror(error), kWritableFileSync, error);
518 uma_logger_->RecordErrorAt(kWritableFileSync);
520 return result;
523 ChromiumEnv::ChromiumEnv()
524 : name_("LevelDBEnv"),
525 bgsignal_(&mu_),
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");
539 if (f == NULL) {
540 *result = NULL;
541 int saved_errno = errno;
542 RecordOSError(kNewSequentialFile, saved_errno);
543 return MakeIOError(
544 fname, strerror(saved_errno), kNewSequentialFile, saved_errno);
545 } else {
546 *result = new ChromiumSequentialFile(fname, f, this);
547 return Status::OK();
551 void ChromiumEnv::RecordOpenFilesLimit(const std::string& type) {
552 #if defined(OS_POSIX)
553 struct rlimit nofile;
554 if (getrlimit(RLIMIT_NOFILE, &nofile))
555 return;
556 GetMaxFDHistogram(type)->Add(nofile.rlim_cur);
557 #endif
560 Status ChromiumEnv::NewRandomAccessFile(const std::string& fname,
561 RandomAccessFile** result) {
562 int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN;
563 bool created;
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");
570 return Status::OK();
572 if (error_code == ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED)
573 RecordOpenFilesLimit("TooManyOpened");
574 else
575 RecordOpenFilesLimit("OtherError");
576 *result = NULL;
577 RecordOSError(kNewRandomAccessFile, error_code);
578 return MakeIOError(fname,
579 PlatformFileErrorString(error_code),
580 kNewRandomAccessFile,
581 error_code);
584 Status ChromiumEnv::NewWritableFile(const std::string& fname,
585 WritableFile** result) {
586 *result = NULL;
587 FILE* f = fopen_internal(fname.c_str(), "wb");
588 if (f == NULL) {
589 int saved_errno = errno;
590 RecordErrorAt(kNewWritableFile);
591 return MakeIOError(
592 fname, strerror(saved_errno), kNewWritableFile, saved_errno);
593 } else {
594 *result = new ChromiumWritableFile(fname, f, this, this);
595 return Status::OK();
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) {
605 result->clear();
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?
616 return Status::OK();
619 Status ChromiumEnv::DeleteFile(const std::string& fname) {
620 Status result;
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);
626 return result;
629 Status ChromiumEnv::CreateDir(const std::string& name) {
630 Status result;
631 base::PlatformFileError error = base::PLATFORM_FILE_OK;
632 Retrier retrier(kCreateDir, this);
633 do {
634 if (::file_util::CreateDirectoryAndGetError(CreateFilePath(name), &error))
635 return result;
636 } while (retrier.ShouldKeepTrying(error));
637 result = MakeIOError(name, "Could not create directory.", kCreateDir, error);
638 RecordOSError(kCreateDir, error);
639 return result;
642 Status ChromiumEnv::DeleteDir(const std::string& name) {
643 Status result;
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);
649 return result;
652 Status ChromiumEnv::GetFileSize(const std::string& fname, uint64_t* size) {
653 Status s;
654 int64_t signed_size;
655 if (!::file_util::GetFileSize(CreateFilePath(fname), &signed_size)) {
656 *size = 0;
657 s = MakeIOError(fname, "Could not determine file size.", kGetFileSize);
658 RecordErrorAt(kGetFileSize);
659 } else {
660 *size = static_cast<uint64_t>(signed_size);
662 return s;
665 Status ChromiumEnv::RenameFile(const std::string& src, const std::string& dst) {
666 Status result;
667 base::FilePath src_file_path = CreateFilePath(src);
668 if (!::base::PathExists(src_file_path))
669 return result;
670 base::FilePath destination = CreateFilePath(dst);
672 Retrier retrier(kRenameFile, this);
673 base::PlatformFileError error = base::PLATFORM_FILE_OK;
674 do {
675 if (base::ReplaceFile(src_file_path, destination, &error))
676 return result;
677 } while (retrier.ShouldKeepTrying(error));
679 DCHECK(error != base::PLATFORM_FILE_OK);
680 RecordOSError(kRenameFile, error);
681 char buf[100];
682 snprintf(buf,
683 sizeof(buf),
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) {
690 *lock = NULL;
691 Status result;
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;
697 bool created;
698 ::base::PlatformFileError error_code;
699 ::base::PlatformFile file;
700 Retrier retrier(kLockFile, this);
701 do {
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;
711 do {
712 if (base::DirectoryExists(parent))
713 break;
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);
725 } else {
726 ChromiumFileLock* my_lock = new ChromiumFileLock;
727 my_lock->file_ = file;
728 *lock = my_lock;
730 return result;
733 Status ChromiumEnv::UnlockFile(FileLock* lock) {
734 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
735 Status result;
736 if (!::base::ClosePlatformFile(my_lock->file_)) {
737 result = MakeIOError("Could not close lock file.", "", kUnlockFile);
738 RecordErrorAt(kUnlockFile);
740 delete my_lock;
741 return result;
744 Status ChromiumEnv::GetTestDirectory(std::string* path) {
745 mu_.Acquire();
746 if (test_directory_.empty()) {
747 if (!::file_util::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
748 &test_directory_)) {
749 mu_.Release();
750 RecordErrorAt(kGetTestDirectory);
751 return MakeIOError(
752 "Could not create temp directory.", "", kGetTestDirectory);
755 *path = FilePathToString(test_directory_);
756 mu_.Release();
757 return Status::OK();
760 Status ChromiumEnv::NewLogger(const std::string& fname, Logger** result) {
761 FILE* f = fopen_internal(fname.c_str(), "w");
762 if (f == NULL) {
763 *result = NULL;
764 int saved_errno = errno;
765 RecordOSError(kNewLogger, saved_errno);
766 return MakeIOError(fname, strerror(saved_errno), kNewLogger, saved_errno);
767 } else {
768 *result = new ChromiumLogger(f);
769 return Status::OK();
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 {
792 DCHECK(error < 0);
793 RecordErrorAt(method);
794 GetOSErrorHistogram(method, -base::PLATFORM_FILE_ERROR_MAX)->Add(-error);
797 void ChromiumEnv::RecordOSError(MethodID method, int error) const {
798 DCHECK(error > 0);
799 RecordErrorAt(method);
800 GetOSErrorHistogram(method, ERANGE + 1)->Add(error);
803 base::HistogramBase* ChromiumEnv::GetOSErrorHistogram(MethodID method,
804 int limit) const {
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");
835 const int kMin = 1;
836 const int kMax = 10;
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),
854 kNumBuckets,
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 {
868 public:
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);
873 DCHECK(success);
875 virtual ~Thread() {}
876 virtual void ThreadMain() {
877 (*function_)(arg_);
878 delete this;
881 private:
882 void (*function_)(void* arg);
883 void* arg_;
886 void ChromiumEnv::Schedule(void (*function)(void*), void* arg) {
887 mu_.Acquire();
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
896 // waiting.
897 if (queue_.empty()) {
898 bgsignal_.Signal();
901 // Add to priority queue
902 queue_.push_back(BGItem());
903 queue_.back().function = function;
904 queue_.back().arg = arg;
906 mu_.Release();
909 void ChromiumEnv::BGThread() {
910 base::PlatformThread::SetName(name_.c_str());
912 while (true) {
913 // Wait until there is an item that is ready to run
914 mu_.Acquire();
915 while (queue_.empty()) {
916 bgsignal_.Wait();
919 void (*function)(void*) = queue_.front().function;
920 void* arg = queue_.front().arg;
921 queue_.pop_front();
923 mu_.Release();
924 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task");
925 (*function)(arg);
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
955 namespace leveldb {
957 Env* IDBEnv() {
958 return leveldb_env::idb_env.Pointer();
961 Env* Env::Default() {
962 return leveldb_env::default_env.Pointer();
965 } // namespace leveldb