filelock_test.cc 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. #include <fcntl.h>
  7. #include "rocksdb/env.h"
  8. #include "rocksdb/status.h"
  9. #ifdef __FreeBSD__
  10. #include <sys/types.h>
  11. #include <sys/wait.h>
  12. #endif
  13. #ifdef __OpenBSD__
  14. #include <sys/wait.h>
  15. #endif
  16. #include <vector>
  17. #include "test_util/testharness.h"
  18. #include "util/coding.h"
  19. #include "util/string_util.h"
  20. namespace ROCKSDB_NAMESPACE {
  21. class LockTest : public testing::Test {
  22. public:
  23. static LockTest* current_;
  24. std::string file_;
  25. ROCKSDB_NAMESPACE::Env* env_;
  26. LockTest()
  27. : file_(test::PerThreadDBPath("db_testlock_file")),
  28. env_(ROCKSDB_NAMESPACE::Env::Default()) {
  29. current_ = this;
  30. }
  31. ~LockTest() override = default;
  32. Status LockFile(FileLock** db_lock) { return env_->LockFile(file_, db_lock); }
  33. Status UnlockFile(FileLock* db_lock) { return env_->UnlockFile(db_lock); }
  34. bool AssertFileIsLocked() {
  35. return CheckFileLock(/* lock_expected = */ true);
  36. }
  37. bool AssertFileIsNotLocked() {
  38. return CheckFileLock(/* lock_expected = */ false);
  39. }
  40. bool CheckFileLock(bool lock_expected) {
  41. // We need to fork to check the fcntl lock as we need
  42. // to open and close the file from a different process
  43. // to avoid either releasing the lock on close, or not
  44. // contending for it when requesting a lock.
  45. #ifdef OS_WIN
  46. // WaitForSingleObject and GetExitCodeProcess can do what waitpid does.
  47. // TODO - implement on Windows
  48. return true;
  49. #else
  50. pid_t pid = fork();
  51. if (0 == pid) {
  52. // child process
  53. int exit_val = EXIT_FAILURE;
  54. int fd = open(file_.c_str(), O_RDWR | O_CREAT, 0644);
  55. if (fd < 0) {
  56. // could not open file, could not check if it was locked
  57. fprintf(stderr, "Open on on file %s failed.\n", file_.c_str());
  58. exit(exit_val);
  59. }
  60. struct flock f;
  61. memset(&f, 0, sizeof(f));
  62. f.l_type = (F_WRLCK);
  63. f.l_whence = SEEK_SET;
  64. f.l_start = 0;
  65. f.l_len = 0; // Lock/unlock entire file
  66. int value = fcntl(fd, F_SETLK, &f);
  67. if (value == -1) {
  68. if (lock_expected) {
  69. exit_val = EXIT_SUCCESS;
  70. }
  71. } else {
  72. if (!lock_expected) {
  73. exit_val = EXIT_SUCCESS;
  74. }
  75. }
  76. close(fd); // lock is released for child process
  77. exit(exit_val);
  78. } else if (pid > 0) {
  79. // parent process
  80. int status;
  81. while (-1 == waitpid(pid, &status, 0)) {
  82. }
  83. if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
  84. // child process exited with non success status
  85. return false;
  86. } else {
  87. return true;
  88. }
  89. } else {
  90. fprintf(stderr, "Fork failed\n");
  91. return false;
  92. }
  93. return false;
  94. #endif
  95. }
  96. };
  97. LockTest* LockTest::current_;
  98. TEST_F(LockTest, LockBySameThread) {
  99. FileLock* lock1;
  100. FileLock* lock2;
  101. // acquire a lock on a file
  102. ASSERT_OK(LockFile(&lock1));
  103. // check the file is locked
  104. ASSERT_TRUE(AssertFileIsLocked());
  105. // re-acquire the lock on the same file. This should fail.
  106. Status s = LockFile(&lock2);
  107. ASSERT_TRUE(s.IsIOError());
  108. #ifndef OS_WIN
  109. // Validate that error message contains current thread ID.
  110. ASSERT_TRUE(s.ToString().find(std::to_string(
  111. Env::Default()->GetThreadID())) != std::string::npos);
  112. #endif
  113. // check the file is locked
  114. ASSERT_TRUE(AssertFileIsLocked());
  115. // release the lock
  116. ASSERT_OK(UnlockFile(lock1));
  117. // check the file is not locked
  118. ASSERT_TRUE(AssertFileIsNotLocked());
  119. }
  120. } // namespace ROCKSDB_NAMESPACE
  121. int main(int argc, char** argv) {
  122. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  123. ::testing::InitGoogleTest(&argc, argv);
  124. return RUN_ALL_TESTS();
  125. }