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. 4 5 #include <errno.h> 6 #include <stdio.h> 7 8 #include <deque> 9 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" 33 34 #if defined(OS_WIN) 35 #include <io.h> 36 #include "base/win/win_util.h" 37 #endif 38 39 #if defined(OS_POSIX) 40 #include <fcntl.h> 41 #include <sys/resource.h> 42 #include <sys/time.h> 43 #endif 44 45 using namespace leveldb; 46 47 namespace leveldb_env { 48 49 namespace { 50 51 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_ANDROID) || \ 52 defined(OS_OPENBSD) 53 // The following are glibc-specific 54 55 size_t fread_wrapper(void *ptr, size_t size, size_t n, FILE *file) { 56 return fread(ptr, size, n, file); 57 } 58 59 size_t fwrite_wrapper(const void *ptr, size_t size, size_t n, FILE *file) { 60 return fwrite(ptr, size, n, file); 61 } 62 63 int fflush_wrapper(FILE *file) { 64 return fflush(file); 65 } 66 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 74 } 75 #endif 76 77 #else 78 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); 88 } 89 ~TryToLockFILE() { 90 if (file_to_unlock_) 91 funlockfile(file_to_unlock_); 92 } 93 94 private: 95 FILE* file_to_unlock_; 96 }; 97 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); 101 } 102 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); 106 } 107 108 int fflush_wrapper(FILE *file) { 109 TryToLockFILE lock(file); 110 return fflush_unlocked(file); 111 } 112 113 #endif 114 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 122 } 123 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 130 } 131 132 static const base::FilePath::CharType kLevelDBTestDirectoryPrefix[] 133 = FILE_PATH_LITERAL("leveldb-test-"); 134 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(); 173 } 174 NOTIMPLEMENTED(); 175 return "Unknown error."; 176 } 177 178 class ChromiumSequentialFile: public SequentialFile { 179 private: 180 std::string filename_; 181 FILE* file_; 182 const UMALogger* uma_logger_; 183 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_); } 189 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); 201 } 202 } 203 return s; 204 } 205 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); 212 } 213 return Status::OK(); 214 } 215 }; 216 217 class ChromiumRandomAccessFile: public RandomAccessFile { 218 private: 219 std::string filename_; 220 ::base::PlatformFile file_; 221 const UMALogger* uma_logger_; 222 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_); } 228 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); 239 } 240 return s; 241 } 242 }; 243 244 class ChromiumFileLock : public FileLock { 245 public: 246 ::base::PlatformFile file_; 247 }; 248 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_); 267 } 268 } 269 } 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; 277 } 278 success_ = false; 279 return false; 280 } 281 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_; 291 }; 292 293 class IDBEnv : public ChromiumEnv { 294 public: 295 IDBEnv() : ChromiumEnv() { name_ = "LevelDBEnv.IDB"; } 296 }; 297 298 ::base::LazyInstance<IDBEnv>::Leaky idb_env = LAZY_INSTANCE_INITIALIZER; 299 300 ::base::LazyInstance<ChromiumEnv>::Leaky default_env = 301 LAZY_INSTANCE_INITIALIZER; 302 303 } // unnamed namespace 304 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"; 350 } 351 NOTREACHED(); 352 return "Unknown"; 353 } 354 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); 368 } 369 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); 384 } 385 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); 395 } 396 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; 404 } 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; 410 } 411 if (RE2::PartialMatch( 412 string, "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method, error)) { 413 *method_param = static_cast<MethodID>(method); 414 return METHOD_AND_ERRNO; 415 } 416 return NONE; 417 } 418 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 425 } 426 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()); 439 } 440 441 ChromiumWritableFile::~ChromiumWritableFile() { 442 if (file_ != NULL) { 443 // Ignoring any potential errors 444 fclose(file_); 445 } 446 } 447 448 Status ChromiumWritableFile::SyncParent() { 449 Status s; 450 #if !defined(OS_WIN) 451 TRACE_EVENT0("leveldb", "SyncParent"); 452 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); 459 }; 460 HANDLE_EINTR(close(parent_fd)); 461 #endif 462 return s; 463 } 464 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_); 471 } 472 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); 479 } 480 return Status::OK(); 481 } 482 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); 488 } 489 file_ = NULL; 490 return result; 491 } 492 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); 500 } 501 return result; 502 } 503 504 Status ChromiumWritableFile::Sync() { 505 TRACE_EVENT0("leveldb", "ChromiumEnv::Sync"); 506 Status result; 507 int error = 0; 508 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); 519 } 520 return result; 521 } 522 523 ChromiumEnv::ChromiumEnv() 524 : name_("LevelDBEnv"), 525 bgsignal_(&mu_), 526 started_bgthread_(false), 527 kMaxRetryTimeMillis(1000) { 528 } 529 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. 534 } 535 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(); 548 } 549 } 550 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 558 } 559 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(); 571 } 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); 582 } 583 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(); 596 } 597 } 598 599 bool ChromiumEnv::FileExists(const std::string& fname) { 600 return ::base::PathExists(CreateFilePath(fname)); 601 } 602 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(); 612 } 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(); 617 } 618 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); 625 } 626 return result; 627 } 628 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; 640 } 641 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); 648 } 649 return result; 650 } 651 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); 661 } 662 return s; 663 } 664 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); 671 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)); 678 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); 687 } 688 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)); 706 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); 719 } 720 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; 729 } 730 return result; 731 } 732 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); 739 } 740 delete my_lock; 741 return result; 742 } 743 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); 753 } 754 } 755 *path = FilePathToString(test_directory_); 756 mu_.Release(); 757 return Status::OK(); 758 } 759 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(); 770 } 771 } 772 773 uint64_t ChromiumEnv::NowMicros() { 774 return ::base::TimeTicks::Now().ToInternalValue(); 775 } 776 777 void ChromiumEnv::SleepForMicroseconds(int micros) { 778 // Round up to the next millisecond. 779 ::base::PlatformThread::Sleep(::base::TimeDelta::FromMicroseconds(micros)); 780 } 781 782 void ChromiumEnv::RecordErrorAt(MethodID method) const { 783 GetMethodIOErrorHistogram()->Add(method); 784 } 785 786 void ChromiumEnv::RecordLockFileAncestors(int num_missing_ancestors) const { 787 GetLockFileAncestorHistogram()->Add(num_missing_ancestors); 788 } 789 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); 795 } 796 797 void ChromiumEnv::RecordOSError(MethodID method, int error) const { 798 DCHECK(error > 0); 799 RecordErrorAt(method); 800 GetOSErrorHistogram(method, ERANGE + 1)->Add(error); 801 } 802 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); 810 } 811 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); 817 } 818 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); 830 } 831 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); 841 } 842 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)); 847 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); 856 } 857 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); 865 } 866 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); 874 } 875 virtual ~Thread() {} 876 virtual void ThreadMain() { 877 (*function_)(arg_); 878 delete this; 879 } 880 881 private: 882 void (*function_)(void* arg); 883 void* arg_; 884 }; 885 886 void ChromiumEnv::Schedule(void (*function)(void*), void* arg) { 887 mu_.Acquire(); 888 889 // Start background thread if necessary 890 if (!started_bgthread_) { 891 started_bgthread_ = true; 892 StartThread(&ChromiumEnv::BGThreadWrapper, this); 893 } 894 895 // If the queue is currently empty, the background thread may currently be 896 // waiting. 897 if (queue_.empty()) { 898 bgsignal_.Signal(); 899 } 900 901 // Add to priority queue 902 queue_.push_back(BGItem()); 903 queue_.back().function = function; 904 queue_.back().arg = arg; 905 906 mu_.Release(); 907 } 908 909 void ChromiumEnv::BGThread() { 910 base::PlatformThread::SetName(name_.c_str()); 911 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(); 917 } 918 919 void (*function)(void*) = queue_.front().function; 920 void* arg = queue_.front().arg; 921 queue_.pop_front(); 922 923 mu_.Release(); 924 TRACE_EVENT0("leveldb", "ChromiumEnv::BGThread-Task"); 925 (*function)(arg); 926 } 927 } 928 929 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) { 930 new Thread(function, arg); // Will self-delete. 931 } 932 933 static std::string GetDirName(const std::string& filename) { 934 base::FilePath file = base::FilePath::FromUTF8Unsafe(filename); 935 return FilePathToString(file.DirName()); 936 } 937 938 void ChromiumEnv::DidCreateNewFile(const std::string& filename) { 939 base::AutoLock auto_lock(map_lock_); 940 needs_sync_map_[GetDirName(filename)] = true; 941 } 942 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(); 946 } 947 948 void ChromiumEnv::DidSyncDir(const std::string& filename) { 949 base::AutoLock auto_lock(map_lock_); 950 needs_sync_map_.erase(GetDirName(filename)); 951 } 952 953 } // namespace leveldb_env 954 955 namespace leveldb { 956 957 Env* IDBEnv() { 958 return leveldb_env::idb_env.Pointer(); 959 } 960 961 Env* Env::Default() { 962 return leveldb_env::default_env.Pointer(); 963 } 964 965 } // namespace leveldb 966 967