By moving the call to Load() up in SearchProvider::Start(), we are giving a chance...
[chromium-blink-merge.git] / third_party / leveldatabase / env_chromium.cc
blob474599c11b6f4022ec3e9691f2cda16e99093715
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 <deque>
6 #include <errno.h>
7 #include <stdio.h>
8 #include "base/at_exit.h"
9 #include "base/file_path.h"
10 #include "base/file_util.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/platform_file.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "base/stringprintf.h"
18 #include "base/synchronization/lock.h"
19 #include "base/sys_info.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/threading/thread.h"
22 #include "base/utf_string_conversions.h"
23 #include "chromium_logger.h"
24 #include "leveldb/env.h"
25 #include "leveldb/slice.h"
26 #include "port/port.h"
27 #include "util/logging.h"
29 #if defined(OS_WIN)
30 #include <io.h>
31 #include "base/win/win_util.h"
32 #endif
34 #if !defined(OS_WIN)
35 #include <fcntl.h>
36 #endif
38 namespace {
40 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_ANDROID) || \
41 defined(OS_OPENBSD)
42 // The following are glibc-specific
44 size_t fread_unlocked(void *ptr, size_t size, size_t n, FILE *file) {
45 return fread(ptr, size, n, file);
48 size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, FILE *file) {
49 return fwrite(ptr, size, n, file);
52 int fflush_unlocked(FILE *file) {
53 return fflush(file);
56 #if !defined(OS_ANDROID)
57 int fdatasync(int fildes) {
58 #if defined(OS_WIN)
59 return _commit(fildes);
60 #else
61 return HANDLE_EINTR(fsync(fildes));
62 #endif
64 #endif
66 #endif
68 // Wide-char safe fopen wrapper.
69 FILE* fopen_internal(const char* fname, const char* mode) {
70 #if defined(OS_WIN)
71 return _wfopen(UTF8ToUTF16(fname).c_str(), ASCIIToUTF16(mode).c_str());
72 #else
73 return fopen(fname, mode);
74 #endif
77 ::FilePath CreateFilePath(const std::string& file_path) {
78 #if defined(OS_WIN)
79 return FilePath(UTF8ToUTF16(file_path));
80 #else
81 return FilePath(file_path);
82 #endif
85 std::string FilePathToString(const ::FilePath& file_path) {
86 #if defined(OS_WIN)
87 return UTF16ToUTF8(file_path.value());
88 #else
89 return file_path.value();
90 #endif
93 bool sync_parent(const std::string& fname) {
94 #if !defined(OS_WIN)
95 FilePath parent_dir = CreateFilePath(fname).DirName();
96 int parent_fd = HANDLE_EINTR(open(FilePathToString(parent_dir).c_str(), O_RDONLY));
97 if (parent_fd < 0)
98 return false;
99 HANDLE_EINTR(fsync(parent_fd));
100 HANDLE_EINTR(close(parent_fd));
101 #endif
102 return true;
105 enum UmaEntry {
106 kSequentialFileRead,
107 kSequentialFileSkip,
108 kRandomAccessFileRead,
109 kWritableFileAppend,
110 kWritableFileClose,
111 kWritableFileFlush,
112 kWritableFileSync,
113 kNewSequentialFile,
114 kNewRandomAccessFile,
115 kNewWritableFile,
116 kDeleteFile,
117 kCreateDir,
118 kDeleteDir,
119 kGetFileSize,
120 kRenamefile,
121 kLockFile,
122 kUnlockFile,
123 kGetTestDirectory,
124 kNewLogger,
125 kNumEntries
128 class UMALogger {
129 public:
130 virtual void RecordErrorAt(UmaEntry entry) const = 0;
131 virtual void LogRandomAccessFileError(base::PlatformFileError error_code)
132 const = 0;
135 } // namespace
137 namespace leveldb {
139 namespace {
141 class Thread;
143 static const ::FilePath::CharType kLevelDBTestDirectoryPrefix[]
144 = FILE_PATH_LITERAL("leveldb-test-");
146 const char* PlatformFileErrorString(const ::base::PlatformFileError& error) {
147 switch (error) {
148 case ::base::PLATFORM_FILE_ERROR_FAILED:
149 return "Opening file failed.";
150 case ::base::PLATFORM_FILE_ERROR_IN_USE:
151 return "File currently in use.";
152 case ::base::PLATFORM_FILE_ERROR_EXISTS:
153 return "File already exists.";
154 case ::base::PLATFORM_FILE_ERROR_NOT_FOUND:
155 return "File not found.";
156 case ::base::PLATFORM_FILE_ERROR_ACCESS_DENIED:
157 return "Access denied.";
158 case ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED:
159 return "Too many files open.";
160 case ::base::PLATFORM_FILE_ERROR_NO_MEMORY:
161 return "Out of memory.";
162 case ::base::PLATFORM_FILE_ERROR_NO_SPACE:
163 return "No space left on drive.";
164 case ::base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY:
165 return "Not a directory.";
166 case ::base::PLATFORM_FILE_ERROR_INVALID_OPERATION:
167 return "Invalid operation.";
168 case ::base::PLATFORM_FILE_ERROR_SECURITY:
169 return "Security error.";
170 case ::base::PLATFORM_FILE_ERROR_ABORT:
171 return "File operation aborted.";
172 case ::base::PLATFORM_FILE_ERROR_NOT_A_FILE:
173 return "The supplied path was not a file.";
174 case ::base::PLATFORM_FILE_ERROR_NOT_EMPTY:
175 return "The file was not empty.";
176 case ::base::PLATFORM_FILE_ERROR_INVALID_URL:
177 return "Invalid URL.";
178 case ::base::PLATFORM_FILE_OK:
179 return "OK.";
180 case ::base::PLATFORM_FILE_ERROR_MAX:
181 NOTREACHED();
183 NOTIMPLEMENTED();
184 return "Unknown error.";
187 class ChromiumSequentialFile: public SequentialFile {
188 private:
189 std::string filename_;
190 FILE* file_;
191 const UMALogger* uma_logger_;
193 public:
194 ChromiumSequentialFile(const std::string& fname, FILE* f,
195 const UMALogger* uma_logger)
196 : filename_(fname), file_(f), uma_logger_(uma_logger) { }
197 virtual ~ChromiumSequentialFile() { fclose(file_); }
199 virtual Status Read(size_t n, Slice* result, char* scratch) {
200 Status s;
201 size_t r = fread_unlocked(scratch, 1, n, file_);
202 *result = Slice(scratch, r);
203 if (r < n) {
204 if (feof(file_)) {
205 // We leave status as ok if we hit the end of the file
206 } else {
207 // A partial read with an error: return a non-ok status
208 s = Status::IOError(filename_, strerror(errno));
209 uma_logger_->RecordErrorAt(kSequentialFileRead);
212 return s;
215 virtual Status Skip(uint64_t n) {
216 if (fseek(file_, n, SEEK_CUR)) {
217 uma_logger_->RecordErrorAt(kSequentialFileSkip);
218 return Status::IOError(filename_, strerror(errno));
220 return Status::OK();
224 class ChromiumRandomAccessFile: public RandomAccessFile {
225 private:
226 std::string filename_;
227 ::base::PlatformFile file_;
228 const UMALogger* uma_logger_;
230 public:
231 ChromiumRandomAccessFile(const std::string& fname, ::base::PlatformFile file,
232 const UMALogger* uma_logger)
233 : filename_(fname), file_(file), uma_logger_(uma_logger) { }
234 virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_); }
236 virtual Status Read(uint64_t offset, size_t n, Slice* result,
237 char* scratch) const {
238 Status s;
239 int r = ::base::ReadPlatformFile(file_, offset, scratch, n);
240 *result = Slice(scratch, (r < 0) ? 0 : r);
241 if (r < 0) {
242 // An error: return a non-ok status
243 s = Status::IOError(filename_, "Could not perform read");
244 uma_logger_->RecordErrorAt(kRandomAccessFileRead);
246 return s;
250 class ChromiumWritableFile : public WritableFile {
251 private:
252 std::string filename_;
253 FILE* file_;
254 const UMALogger* uma_logger_;
256 public:
257 ChromiumWritableFile(const std::string& fname, FILE* f,
258 const UMALogger* uma_logger)
259 : filename_(fname), file_(f), uma_logger_(uma_logger) { }
261 ~ChromiumWritableFile() {
262 if (file_ != NULL) {
263 // Ignoring any potential errors
264 fclose(file_);
268 virtual Status Append(const Slice& data) {
269 size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_);
270 Status result;
271 if (r != data.size()) {
272 result = Status::IOError(filename_, strerror(errno));
273 uma_logger_->RecordErrorAt(kWritableFileAppend);
275 return result;
278 virtual Status Close() {
279 Status result;
280 if (fclose(file_) != 0) {
281 result = Status::IOError(filename_, strerror(errno));
282 uma_logger_->RecordErrorAt(kWritableFileClose);
284 file_ = NULL;
285 return result;
288 virtual Status Flush() {
289 Status result;
290 if (fflush_unlocked(file_) != 0) {
291 result = Status::IOError(filename_, strerror(errno));
292 uma_logger_->RecordErrorAt(kWritableFileFlush);
294 return result;
297 virtual Status Sync() {
298 Status result;
299 int error = 0;
301 if (fflush_unlocked(file_))
302 error = errno;
303 // Sync even if fflush gave an error; perhaps the data actually got out,
304 // even though something went wrong.
305 if (fdatasync(fileno(file_)) && !error)
306 error = errno;
307 // Report the first error we found.
308 if (error) {
309 result = Status::IOError(filename_, strerror(error));
310 uma_logger_->RecordErrorAt(kWritableFileSync);
312 return result;
316 class ChromiumFileLock : public FileLock {
317 public:
318 ::base::PlatformFile file_;
321 class ChromiumEnv : public Env, public UMALogger {
322 public:
323 ChromiumEnv();
324 virtual ~ChromiumEnv() {
325 NOTREACHED();
328 virtual Status NewSequentialFile(const std::string& fname,
329 SequentialFile** result) {
330 FILE* f = fopen_internal(fname.c_str(), "rb");
331 if (f == NULL) {
332 *result = NULL;
333 RecordErrorAt(kNewSequentialFile);
334 return Status::IOError(fname, strerror(errno));
335 } else {
336 *result = new ChromiumSequentialFile(fname, f, this);
337 return Status::OK();
341 virtual Status NewRandomAccessFile(const std::string& fname,
342 RandomAccessFile** result) {
343 int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN;
344 bool created;
345 ::base::PlatformFileError error_code;
346 ::base::PlatformFile file = ::base::CreatePlatformFile(
347 CreateFilePath(fname), flags, &created, &error_code);
348 if (error_code != ::base::PLATFORM_FILE_OK) {
349 *result = NULL;
350 RecordErrorAt(kNewRandomAccessFile);
351 LogRandomAccessFileError(error_code);
352 return Status::IOError(fname, PlatformFileErrorString(error_code));
354 *result = new ChromiumRandomAccessFile(fname, file, this);
355 return Status::OK();
358 virtual Status NewWritableFile(const std::string& fname,
359 WritableFile** result) {
360 *result = NULL;
361 FILE* f = fopen_internal(fname.c_str(), "wb");
362 if (f == NULL) {
363 RecordErrorAt(kNewWritableFile);
364 return Status::IOError(fname, strerror(errno));
365 } else {
366 if (!sync_parent(fname)) {
367 fclose(f);
368 return Status::IOError(fname, strerror(errno));
370 *result = new ChromiumWritableFile(fname, f, this);
371 return Status::OK();
375 virtual bool FileExists(const std::string& fname) {
376 return ::file_util::PathExists(CreateFilePath(fname));
379 virtual Status GetChildren(const std::string& dir,
380 std::vector<std::string>* result) {
381 result->clear();
382 ::file_util::FileEnumerator iter(
383 CreateFilePath(dir), false, ::file_util::FileEnumerator::FILES);
384 ::FilePath current = iter.Next();
385 while (!current.empty()) {
386 result->push_back(FilePathToString(current.BaseName()));
387 current = iter.Next();
389 // TODO(jorlow): Unfortunately, the FileEnumerator swallows errors, so
390 // we'll always return OK. Maybe manually check for error
391 // conditions like the file not existing?
392 return Status::OK();
395 virtual Status DeleteFile(const std::string& fname) {
396 Status result;
397 // TODO(jorlow): Should we assert this is a file?
398 if (!::file_util::Delete(CreateFilePath(fname), false)) {
399 result = Status::IOError(fname, "Could not delete file.");
400 RecordErrorAt(kDeleteFile);
402 return result;
405 virtual Status CreateDir(const std::string& name) {
406 Status result;
407 if (!::file_util::CreateDirectory(CreateFilePath(name))) {
408 result = Status::IOError(name, "Could not create directory.");
409 RecordErrorAt(kCreateDir);
411 return result;
414 virtual Status DeleteDir(const std::string& name) {
415 Status result;
416 // TODO(jorlow): Should we assert this is a directory?
417 if (!::file_util::Delete(CreateFilePath(name), false)) {
418 result = Status::IOError(name, "Could not delete directory.");
419 RecordErrorAt(kDeleteDir);
421 return result;
424 virtual Status GetFileSize(const std::string& fname, uint64_t* size) {
425 Status s;
426 int64_t signed_size;
427 if (!::file_util::GetFileSize(CreateFilePath(fname), &signed_size)) {
428 *size = 0;
429 s = Status::IOError(fname, "Could not determine file size.");
430 RecordErrorAt(kGetFileSize);
431 } else {
432 *size = static_cast<uint64_t>(signed_size);
434 return s;
437 virtual Status RenameFile(const std::string& src, const std::string& dst) {
438 Status result;
439 FilePath src_file_path = CreateFilePath(src);
440 if (!::file_util::PathExists(src_file_path))
441 return result;
442 if (!::file_util::ReplaceFile(src_file_path, CreateFilePath(dst))) {
443 result = Status::IOError(src, "Could not rename file.");
444 RecordErrorAt(kRenamefile);
445 } else {
446 sync_parent(dst);
447 if (src != dst)
448 sync_parent(src);
450 return result;
453 virtual Status LockFile(const std::string& fname, FileLock** lock) {
454 *lock = NULL;
455 Status result;
456 int flags = ::base::PLATFORM_FILE_OPEN_ALWAYS |
457 ::base::PLATFORM_FILE_READ |
458 ::base::PLATFORM_FILE_WRITE |
459 ::base::PLATFORM_FILE_EXCLUSIVE_READ |
460 ::base::PLATFORM_FILE_EXCLUSIVE_WRITE;
461 bool created;
462 ::base::PlatformFileError error_code;
463 ::base::PlatformFile file = ::base::CreatePlatformFile(
464 CreateFilePath(fname), flags, &created, &error_code);
465 if (error_code != ::base::PLATFORM_FILE_OK) {
466 result = Status::IOError(fname, PlatformFileErrorString(error_code));
467 RecordErrorAt(kLockFile);
468 } else {
469 ChromiumFileLock* my_lock = new ChromiumFileLock;
470 my_lock->file_ = file;
471 *lock = my_lock;
473 return result;
476 virtual Status UnlockFile(FileLock* lock) {
477 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock);
478 Status result;
479 if (!::base::ClosePlatformFile(my_lock->file_)) {
480 result = Status::IOError("Could not close lock file.");
481 RecordErrorAt(kUnlockFile);
483 delete my_lock;
484 return result;
487 virtual void Schedule(void (*function)(void*), void* arg);
489 virtual void StartThread(void (*function)(void* arg), void* arg);
491 virtual std::string UserIdentifier() {
492 #if defined(OS_WIN)
493 std::wstring user_sid;
494 bool ret = ::base::win::GetUserSidString(&user_sid);
495 DCHECK(ret);
496 return UTF16ToUTF8(user_sid);
497 #else
498 char buf[100];
499 snprintf(buf, sizeof(buf), "%d", int(geteuid()));
500 return buf;
501 #endif
504 virtual Status GetTestDirectory(std::string* path) {
505 mu_.Acquire();
506 if (test_directory_.empty()) {
507 if (!::file_util::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix,
508 &test_directory_)) {
509 mu_.Release();
510 RecordErrorAt(kGetTestDirectory);
511 return Status::IOError("Could not create temp directory.");
514 *path = FilePathToString(test_directory_);
515 mu_.Release();
516 return Status::OK();
519 virtual Status NewLogger(const std::string& fname, Logger** result) {
520 FILE* f = fopen_internal(fname.c_str(), "w");
521 if (f == NULL) {
522 *result = NULL;
523 RecordErrorAt(kNewLogger);
524 return Status::IOError(fname, strerror(errno));
525 } else {
526 if (!sync_parent(fname)) {
527 fclose(f);
528 return Status::IOError(fname, strerror(errno));
530 *result = new ChromiumLogger(f);
531 return Status::OK();
535 virtual uint64_t NowMicros() {
536 return ::base::TimeTicks::Now().ToInternalValue();
539 virtual void SleepForMicroseconds(int micros) {
540 // Round up to the next millisecond.
541 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros));
544 void RecordErrorAt(UmaEntry entry) const {
545 io_error_histogram_->Add(entry);
548 void LogRandomAccessFileError(base::PlatformFileError error_code) const {
549 DCHECK(error_code < 0);
550 random_access_file_histogram_->Add(-error_code);
553 protected:
554 void InitHistograms(const std::string& uma_title);
556 private:
557 // BGThread() is the body of the background thread
558 void BGThread();
559 static void BGThreadWrapper(void* arg) {
560 reinterpret_cast<ChromiumEnv*>(arg)->BGThread();
563 FilePath test_directory_;
565 size_t page_size_;
566 ::base::Lock mu_;
567 ::base::ConditionVariable bgsignal_;
568 bool started_bgthread_;
570 // Entry per Schedule() call
571 struct BGItem { void* arg; void (*function)(void*); };
572 typedef std::deque<BGItem> BGQueue;
573 BGQueue queue_;
575 base::Histogram* io_error_histogram_;
576 base::Histogram* random_access_file_histogram_;
579 ChromiumEnv::ChromiumEnv()
580 : page_size_(::base::SysInfo::VMAllocationGranularity()),
581 bgsignal_(&mu_),
582 started_bgthread_(false) {
583 InitHistograms("LevelDBEnv");
586 void ChromiumEnv::InitHistograms(const std::string& uma_title) {
587 std::string uma_name(uma_title);
588 uma_name.append(".IOError");
589 // Note: The calls to FactoryGet aren't thread-safe. It's ok to call them here
590 // because this method is only called from LazyInstance, which provides
591 // thread-safety.
592 io_error_histogram_ = base::LinearHistogram::FactoryGet(uma_name, 1,
593 kNumEntries, kNumEntries + 1, base::Histogram::kUmaTargetedHistogramFlag);
595 uma_name.append(".RandomAccessFile");
596 random_access_file_histogram_ = base::LinearHistogram::FactoryGet(uma_name, 1,
597 -base::PLATFORM_FILE_ERROR_MAX, -base::PLATFORM_FILE_ERROR_MAX + 1,
598 base::Histogram::kUmaTargetedHistogramFlag);
601 class Thread : public ::base::PlatformThread::Delegate {
602 public:
603 Thread(void (*function)(void* arg), void* arg)
604 : function_(function), arg_(arg) {
605 ::base::PlatformThreadHandle handle;
606 bool success = ::base::PlatformThread::Create(0, this, &handle);
607 DCHECK(success);
609 virtual ~Thread() {}
610 virtual void ThreadMain() {
611 (*function_)(arg_);
612 delete this;
615 private:
616 void (*function_)(void* arg);
617 void* arg_;
620 void ChromiumEnv::Schedule(void (*function)(void*), void* arg) {
621 mu_.Acquire();
623 // Start background thread if necessary
624 if (!started_bgthread_) {
625 started_bgthread_ = true;
626 StartThread(&ChromiumEnv::BGThreadWrapper, this);
629 // If the queue is currently empty, the background thread may currently be
630 // waiting.
631 if (queue_.empty()) {
632 bgsignal_.Signal();
635 // Add to priority queue
636 queue_.push_back(BGItem());
637 queue_.back().function = function;
638 queue_.back().arg = arg;
640 mu_.Release();
643 void ChromiumEnv::BGThread() {
644 while (true) {
645 // Wait until there is an item that is ready to run
646 mu_.Acquire();
647 while (queue_.empty()) {
648 bgsignal_.Wait();
651 void (*function)(void*) = queue_.front().function;
652 void* arg = queue_.front().arg;
653 queue_.pop_front();
655 mu_.Release();
656 (*function)(arg);
660 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) {
661 new Thread(function, arg); // Will self-delete.
664 class IDBEnv : public ChromiumEnv {
665 public:
666 IDBEnv() : ChromiumEnv() {
667 InitHistograms("LevelDBEnv.IDB");
671 ::base::LazyInstance<IDBEnv>::Leaky
672 idb_env = LAZY_INSTANCE_INITIALIZER;
674 ::base::LazyInstance<ChromiumEnv>::Leaky
675 default_env = LAZY_INSTANCE_INITIALIZER;
679 Env* IDBEnv() {
680 return idb_env.Pointer();
683 Env* Env::Default() {
684 return default_env.Pointer();