OLD | NEW |
(Empty) | |
| 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 <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/platform_file.h" |
| 15 #include "base/process_util.h" |
| 16 #include "base/stringprintf.h" |
| 17 #include "base/synchronization/lock.h" |
| 18 #include "base/sys_info.h" |
| 19 #include "base/task.h" |
| 20 #include "base/threading/platform_thread.h" |
| 21 #include "base/threading/thread.h" |
| 22 #include "base/utf_string_conversions.h" |
| 23 #include "leveldb/env.h" |
| 24 #include "leveldb/slice.h" |
| 25 #include "port/port.h" |
| 26 #include "util/logging.h" |
| 27 |
| 28 #if defined(OS_WIN) |
| 29 #include <io.h> |
| 30 #include "base/win/win_util.h" |
| 31 #endif |
| 32 |
| 33 #if defined(OS_MACOSX) || defined(OS_WIN) |
| 34 // The following are glibc-specific |
| 35 namespace { |
| 36 |
| 37 size_t fread_unlocked(void *ptr, size_t size, size_t n, FILE *file) { |
| 38 return fread(ptr, size, n, file); |
| 39 } |
| 40 |
| 41 size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, FILE *file) { |
| 42 return fwrite(ptr, size, n, file); |
| 43 } |
| 44 |
| 45 int fflush_unlocked(FILE *file) { |
| 46 return fflush(file); |
| 47 } |
| 48 |
| 49 int fdatasync(int fildes) { |
| 50 #if defined(OS_WIN) |
| 51 return _commit(fildes); |
| 52 #else |
| 53 return fsync(fildes); |
| 54 #endif |
| 55 } |
| 56 |
| 57 } |
| 58 #endif |
| 59 |
| 60 namespace leveldb { |
| 61 |
| 62 namespace { |
| 63 |
| 64 class Thread; |
| 65 |
| 66 static const ::FilePath::CharType kLevelDBTestDirectoryPrefix[] |
| 67 = FILE_PATH_LITERAL("leveldb-test-"); |
| 68 |
| 69 ::FilePath CreateFilePath(const std::string& file_path) { |
| 70 #if defined(OS_WIN) |
| 71 return FilePath(UTF8ToUTF16(file_path)); |
| 72 #else |
| 73 return FilePath(file_path); |
| 74 #endif |
| 75 } |
| 76 |
| 77 std::string FilePathToString(const ::FilePath& file_path) { |
| 78 #if defined(OS_WIN) |
| 79 return UTF16ToUTF8(file_path.value()); |
| 80 #else |
| 81 return file_path.value(); |
| 82 #endif |
| 83 } |
| 84 |
| 85 // TODO(jorlow): This should be moved into Chromium's base. |
| 86 const char* PlatformFileErrorString(const ::base::PlatformFileError& error) { |
| 87 switch (error) { |
| 88 case ::base::PLATFORM_FILE_ERROR_FAILED: |
| 89 return "Opening file failed."; |
| 90 case ::base::PLATFORM_FILE_ERROR_IN_USE: |
| 91 return "File currently in use."; |
| 92 case ::base::PLATFORM_FILE_ERROR_EXISTS: |
| 93 return "File already exists."; |
| 94 case ::base::PLATFORM_FILE_ERROR_NOT_FOUND: |
| 95 return "File not found."; |
| 96 case ::base::PLATFORM_FILE_ERROR_ACCESS_DENIED: |
| 97 return "Access denied."; |
| 98 case ::base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED: |
| 99 return "Too many files open."; |
| 100 case ::base::PLATFORM_FILE_ERROR_NO_MEMORY: |
| 101 return "Out of memory."; |
| 102 case ::base::PLATFORM_FILE_ERROR_NO_SPACE: |
| 103 return "No space left on drive."; |
| 104 case ::base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY: |
| 105 return "Not a directory."; |
| 106 case ::base::PLATFORM_FILE_ERROR_INVALID_OPERATION: |
| 107 return "Invalid operation."; |
| 108 case ::base::PLATFORM_FILE_ERROR_SECURITY: |
| 109 return "Security error."; |
| 110 case ::base::PLATFORM_FILE_ERROR_ABORT: |
| 111 return "File operation aborted."; |
| 112 case ::base::PLATFORM_FILE_ERROR_NOT_A_FILE: |
| 113 return "The supplied path was not a file."; |
| 114 case ::base::PLATFORM_FILE_ERROR_NOT_EMPTY: |
| 115 return "The file was not empty."; |
| 116 case ::base::PLATFORM_FILE_ERROR_INVALID_URL: |
| 117 return "Invalid URL."; |
| 118 case ::base::PLATFORM_FILE_OK: |
| 119 return "OK."; |
| 120 } |
| 121 NOTIMPLEMENTED(); |
| 122 return "Unknown error."; |
| 123 } |
| 124 |
| 125 class ChromiumSequentialFile: public SequentialFile { |
| 126 private: |
| 127 std::string filename_; |
| 128 FILE* file_; |
| 129 |
| 130 public: |
| 131 ChromiumSequentialFile(const std::string& fname, FILE* f) |
| 132 : filename_(fname), file_(f) { } |
| 133 virtual ~ChromiumSequentialFile() { fclose(file_); } |
| 134 |
| 135 virtual Status Read(size_t n, Slice* result, char* scratch) { |
| 136 Status s; |
| 137 size_t r = fread_unlocked(scratch, 1, n, file_); |
| 138 *result = Slice(scratch, r); |
| 139 if (r < n) { |
| 140 if (feof(file_)) { |
| 141 // We leave status as ok if we hit the end of the file |
| 142 } else { |
| 143 // A partial read with an error: return a non-ok status |
| 144 s = Status::IOError(filename_, strerror(errno)); |
| 145 } |
| 146 } |
| 147 return s; |
| 148 } |
| 149 |
| 150 virtual Status Skip(uint64_t n) { |
| 151 if (fseek(file_, n, SEEK_CUR)) { |
| 152 return Status::IOError(filename_, strerror(errno)); |
| 153 } |
| 154 return Status::OK(); |
| 155 } |
| 156 }; |
| 157 |
| 158 class ChromiumRandomAccessFile: public RandomAccessFile { |
| 159 private: |
| 160 std::string filename_; |
| 161 ::base::PlatformFile file_; |
| 162 |
| 163 public: |
| 164 ChromiumRandomAccessFile(const std::string& fname, ::base::PlatformFile file) |
| 165 : filename_(fname), file_(file) { } |
| 166 virtual ~ChromiumRandomAccessFile() { ::base::ClosePlatformFile(file_); } |
| 167 |
| 168 virtual Status Read(uint64_t offset, size_t n, Slice* result, |
| 169 char* scratch) const { |
| 170 Status s; |
| 171 int r = ::base::ReadPlatformFile(file_, offset, scratch, n); |
| 172 *result = Slice(scratch, (r < 0) ? 0 : r); |
| 173 if (r < 0) { |
| 174 // An error: return a non-ok status |
| 175 s = Status::IOError(filename_, "Could not preform read"); |
| 176 } |
| 177 return s; |
| 178 } |
| 179 }; |
| 180 |
| 181 class ChromiumWritableFile : public WritableFile { |
| 182 private: |
| 183 std::string filename_; |
| 184 FILE* file_; |
| 185 |
| 186 public: |
| 187 ChromiumWritableFile(const std::string& fname, FILE* f) |
| 188 : filename_(fname), file_(f) { } |
| 189 |
| 190 ~ChromiumWritableFile() { |
| 191 if (file_ != NULL) { |
| 192 // Ignoring any potential errors |
| 193 fclose(file_); |
| 194 } |
| 195 } |
| 196 |
| 197 virtual Status Append(const Slice& data) { |
| 198 size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_); |
| 199 Status result; |
| 200 if (r != data.size()) { |
| 201 result = Status::IOError(filename_, strerror(errno)); |
| 202 } |
| 203 return result; |
| 204 } |
| 205 |
| 206 virtual Status Close() { |
| 207 Status result; |
| 208 if (fclose(file_) != 0) { |
| 209 result = Status::IOError(filename_, strerror(errno)); |
| 210 } |
| 211 file_ = NULL; |
| 212 return result; |
| 213 } |
| 214 |
| 215 virtual Status Flush() { |
| 216 Status result; |
| 217 if (fflush_unlocked(file_) != 0) { |
| 218 result = Status::IOError(filename_, strerror(errno)); |
| 219 } |
| 220 return result; |
| 221 } |
| 222 |
| 223 virtual Status Sync() { |
| 224 Status result; |
| 225 if ((fflush_unlocked(file_) != 0) || |
| 226 (fdatasync(fileno(file_)) != 0)) { |
| 227 result = Status::IOError(filename_, strerror(errno)); |
| 228 } |
| 229 return result; |
| 230 } |
| 231 }; |
| 232 |
| 233 class ChromiumFileLock : public FileLock { |
| 234 public: |
| 235 ::base::PlatformFile file_; |
| 236 }; |
| 237 |
| 238 class ChromiumEnv : public Env { |
| 239 public: |
| 240 ChromiumEnv(); |
| 241 virtual ~ChromiumEnv() { |
| 242 fprintf(stderr, "Destroying Env::Default()\n"); |
| 243 exit(1); |
| 244 } |
| 245 |
| 246 virtual Status NewSequentialFile(const std::string& fname, |
| 247 SequentialFile** result) { |
| 248 FILE* f = fopen(fname.c_str(), "rb"); |
| 249 if (f == NULL) { |
| 250 *result = NULL; |
| 251 return Status::IOError(fname, strerror(errno)); |
| 252 } else { |
| 253 *result = new ChromiumSequentialFile(fname, f); |
| 254 return Status::OK(); |
| 255 } |
| 256 } |
| 257 |
| 258 virtual Status NewRandomAccessFile(const std::string& fname, |
| 259 RandomAccessFile** result) { |
| 260 int flags = ::base::PLATFORM_FILE_READ | ::base::PLATFORM_FILE_OPEN; |
| 261 bool created; |
| 262 ::base::PlatformFileError error_code; |
| 263 ::base::PlatformFile file = ::base::CreatePlatformFile( |
| 264 CreateFilePath(fname), flags, &created, &error_code); |
| 265 if (error_code != ::base::PLATFORM_FILE_OK) { |
| 266 *result = NULL; |
| 267 return Status::IOError(fname, PlatformFileErrorString(error_code)); |
| 268 } |
| 269 *result = new ChromiumRandomAccessFile(fname, file); |
| 270 return Status::OK(); |
| 271 } |
| 272 |
| 273 virtual Status NewWritableFile(const std::string& fname, |
| 274 WritableFile** result) { |
| 275 *result = NULL; |
| 276 FILE* f = fopen(fname.c_str(), "wb"); |
| 277 if (f == NULL) { |
| 278 return Status::IOError(fname, strerror(errno)); |
| 279 } else { |
| 280 *result = new ChromiumWritableFile(fname, f); |
| 281 return Status::OK(); |
| 282 } |
| 283 } |
| 284 |
| 285 virtual bool FileExists(const std::string& fname) { |
| 286 return ::file_util::PathExists(CreateFilePath(fname)); |
| 287 } |
| 288 |
| 289 virtual Status GetChildren(const std::string& dir, |
| 290 std::vector<std::string>* result) { |
| 291 result->clear(); |
| 292 ::file_util::FileEnumerator iter( |
| 293 CreateFilePath(dir), false, ::file_util::FileEnumerator::FILES); |
| 294 ::FilePath current = iter.Next(); |
| 295 while (!current.empty()) { |
| 296 result->push_back(FilePathToString(current.BaseName())); |
| 297 current = iter.Next(); |
| 298 } |
| 299 // TODO(jorlow): Unfortunately, the FileEnumerator swallows errors, so |
| 300 // we'll always return OK. Maybe manually check for error |
| 301 // conditions like the file not existing? |
| 302 return Status::OK(); |
| 303 } |
| 304 |
| 305 virtual Status DeleteFile(const std::string& fname) { |
| 306 Status result; |
| 307 // TODO(jorlow): Should we assert this is a file? |
| 308 if (!::file_util::Delete(CreateFilePath(fname), false)) { |
| 309 result = Status::IOError(fname, "Could not delete file."); |
| 310 } |
| 311 return result; |
| 312 }; |
| 313 |
| 314 virtual Status CreateDir(const std::string& name) { |
| 315 Status result; |
| 316 if (!::file_util::CreateDirectory(CreateFilePath(name))) { |
| 317 result = Status::IOError(name, "Could not create directory."); |
| 318 } |
| 319 return result; |
| 320 }; |
| 321 |
| 322 virtual Status DeleteDir(const std::string& name) { |
| 323 Status result; |
| 324 // TODO(jorlow): Should we assert this is a directory? |
| 325 if (!::file_util::Delete(CreateFilePath(name), false)) { |
| 326 result = Status::IOError(name, "Could not delete directory."); |
| 327 } |
| 328 return result; |
| 329 }; |
| 330 |
| 331 virtual Status GetFileSize(const std::string& fname, uint64_t* size) { |
| 332 Status s; |
| 333 int64_t signed_size; |
| 334 if (!::file_util::GetFileSize(CreateFilePath(fname), &signed_size)) { |
| 335 *size = 0; |
| 336 s = Status::IOError(fname, "Could not determine file size."); |
| 337 } else { |
| 338 *size = static_cast<uint64_t>(signed_size); |
| 339 } |
| 340 return s; |
| 341 } |
| 342 |
| 343 virtual Status RenameFile(const std::string& src, const std::string& dst) { |
| 344 Status result; |
| 345 if (!::file_util::ReplaceFile(CreateFilePath(src), CreateFilePath(dst))) { |
| 346 result = Status::IOError(src, "Could not rename file."); |
| 347 } |
| 348 return result; |
| 349 } |
| 350 |
| 351 virtual Status LockFile(const std::string& fname, FileLock** lock) { |
| 352 *lock = NULL; |
| 353 Status result; |
| 354 int flags = ::base::PLATFORM_FILE_OPEN_ALWAYS | |
| 355 ::base::PLATFORM_FILE_READ | |
| 356 ::base::PLATFORM_FILE_WRITE | |
| 357 ::base::PLATFORM_FILE_EXCLUSIVE_READ | |
| 358 ::base::PLATFORM_FILE_EXCLUSIVE_WRITE; |
| 359 bool created; |
| 360 ::base::PlatformFileError error_code; |
| 361 ::base::PlatformFile file = ::base::CreatePlatformFile( |
| 362 CreateFilePath(fname), flags, &created, &error_code); |
| 363 if (error_code != ::base::PLATFORM_FILE_OK) { |
| 364 result = Status::IOError(fname, PlatformFileErrorString(error_code)); |
| 365 } else { |
| 366 ChromiumFileLock* my_lock = new ChromiumFileLock; |
| 367 my_lock->file_ = file; |
| 368 *lock = my_lock; |
| 369 } |
| 370 return result; |
| 371 } |
| 372 |
| 373 virtual Status UnlockFile(FileLock* lock) { |
| 374 ChromiumFileLock* my_lock = reinterpret_cast<ChromiumFileLock*>(lock); |
| 375 Status result; |
| 376 if (!::base::ClosePlatformFile(my_lock->file_)) { |
| 377 result = Status::IOError("Could not close lock file."); |
| 378 } |
| 379 delete my_lock; |
| 380 return result; |
| 381 } |
| 382 |
| 383 virtual void Schedule(void (*function)(void*), void* arg); |
| 384 |
| 385 virtual void StartThread(void (*function)(void* arg), void* arg); |
| 386 |
| 387 virtual std::string UserIdentifier() { |
| 388 #if defined(OS_WIN) |
| 389 std::wstring user_sid; |
| 390 bool ret = ::base::win::GetUserSidString(&user_sid); |
| 391 DCHECK(ret); |
| 392 return UTF16ToUTF8(user_sid); |
| 393 #else |
| 394 char buf[100]; |
| 395 snprintf(buf, sizeof(buf), "%d", int(geteuid())); |
| 396 return buf; |
| 397 #endif |
| 398 } |
| 399 |
| 400 virtual Status GetTestDirectory(std::string* path) { |
| 401 mu_.Acquire(); |
| 402 if (test_directory_.empty()) { |
| 403 if (!::file_util::CreateNewTempDirectory(kLevelDBTestDirectoryPrefix, |
| 404 &test_directory_)) { |
| 405 mu_.Release(); |
| 406 return Status::IOError("Could not create temp directory."); |
| 407 } |
| 408 } |
| 409 *path = FilePathToString(test_directory_); |
| 410 mu_.Release(); |
| 411 return Status::OK(); |
| 412 } |
| 413 |
| 414 class ChromiumLogger : public Logger { |
| 415 public: |
| 416 ChromiumLogger(const std::string& filename) : filename_(filename) { |
| 417 } |
| 418 |
| 419 virtual void Logv(const char* format, va_list ap) { |
| 420 VLOG(5) << "LevelDB: " << filename_ << " " << StringPrintf(format, ap); |
| 421 } |
| 422 |
| 423 private: |
| 424 std::string filename_; |
| 425 }; |
| 426 |
| 427 virtual Status NewLogger(const std::string& fname, Logger** result) { |
| 428 *result = new ChromiumLogger(fname); |
| 429 return Status::OK(); |
| 430 } |
| 431 |
| 432 virtual uint64_t NowMicros() { |
| 433 return ::base::TimeTicks::HighResNow().ToInternalValue(); |
| 434 } |
| 435 |
| 436 virtual void SleepForMicroseconds(int micros) { |
| 437 // Round up to the next millisecond. |
| 438 ::base::PlatformThread::Sleep((micros + 999) / 1000); |
| 439 } |
| 440 |
| 441 private: |
| 442 // BGThread() is the body of the background thread |
| 443 void BGThread(); |
| 444 static void BGThreadWrapper(void* arg) { |
| 445 reinterpret_cast<ChromiumEnv*>(arg)->BGThread(); |
| 446 } |
| 447 |
| 448 FilePath test_directory_; |
| 449 |
| 450 size_t page_size_; |
| 451 ::base::Lock mu_; |
| 452 ::base::ConditionVariable bgsignal_; |
| 453 bool started_bgthread_; |
| 454 |
| 455 // Entry per Schedule() call |
| 456 struct BGItem { void* arg; void (*function)(void*); }; |
| 457 typedef std::deque<BGItem> BGQueue; |
| 458 BGQueue queue_; |
| 459 }; |
| 460 |
| 461 ChromiumEnv::ChromiumEnv() |
| 462 : page_size_(::base::SysInfo::VMAllocationGranularity()), |
| 463 bgsignal_(&mu_), |
| 464 started_bgthread_(false) { |
| 465 #if defined(OS_MACOSX) |
| 466 ::base::EnableTerminationOnHeapCorruption(); |
| 467 ::base::EnableTerminationOnOutOfMemory(); |
| 468 #endif // OS_MACOSX |
| 469 } |
| 470 |
| 471 class Thread : public ::base::PlatformThread::Delegate { |
| 472 public: |
| 473 Thread(void (*function)(void* arg), void* arg) |
| 474 : function_(function), arg_(arg) { |
| 475 ::base::PlatformThreadHandle handle; |
| 476 bool success = ::base::PlatformThread::Create(0, this, &handle); |
| 477 DCHECK(success); |
| 478 } |
| 479 virtual ~Thread() {} |
| 480 virtual void ThreadMain() { |
| 481 (*function_)(arg_); |
| 482 delete this; |
| 483 } |
| 484 |
| 485 private: |
| 486 void (*function_)(void* arg); |
| 487 void* arg_; |
| 488 }; |
| 489 |
| 490 void ChromiumEnv::Schedule(void (*function)(void*), void* arg) { |
| 491 mu_.Acquire(); |
| 492 |
| 493 // Start background thread if necessary |
| 494 if (!started_bgthread_) { |
| 495 started_bgthread_ = true; |
| 496 StartThread(&ChromiumEnv::BGThreadWrapper, this); |
| 497 } |
| 498 |
| 499 // If the queue is currently empty, the background thread may currently be |
| 500 // waiting. |
| 501 if (queue_.empty()) { |
| 502 bgsignal_.Signal(); |
| 503 } |
| 504 |
| 505 // Add to priority queue |
| 506 queue_.push_back(BGItem()); |
| 507 queue_.back().function = function; |
| 508 queue_.back().arg = arg; |
| 509 |
| 510 mu_.Release(); |
| 511 } |
| 512 |
| 513 void ChromiumEnv::BGThread() { |
| 514 while (true) { |
| 515 // Wait until there is an item that is ready to run |
| 516 mu_.Acquire(); |
| 517 while (queue_.empty()) { |
| 518 bgsignal_.Wait(); |
| 519 } |
| 520 |
| 521 void (*function)(void*) = queue_.front().function; |
| 522 void* arg = queue_.front().arg; |
| 523 queue_.pop_front(); |
| 524 |
| 525 mu_.Release(); |
| 526 (*function)(arg); |
| 527 } |
| 528 } |
| 529 |
| 530 void ChromiumEnv::StartThread(void (*function)(void* arg), void* arg) { |
| 531 new Thread(function, arg); // Will self-delete. |
| 532 } |
| 533 |
| 534 ::base::LazyInstance<ChromiumEnv, ::base::LeakyLazyInstanceTraits<ChromiumEnv> > |
| 535 default_env(::base::LINKER_INITIALIZED); |
| 536 |
| 537 } |
| 538 |
| 539 Env* Env::Default() { |
| 540 return default_env.Pointer(); |
| 541 } |
| 542 |
| 543 } |
OLD | NEW |