summaryrefslogtreecommitdiffstats
path: root/src/rocksdb/util/filelock_test.cc
blob: 20efd513f4f94ff457ef66d32f5b22332b5c5360 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).
//
#include "rocksdb/status.h"
#include "rocksdb/env.h"

#include <fcntl.h>
#include <vector>
#include "test_util/testharness.h"
#include "util/coding.h"

namespace ROCKSDB_NAMESPACE {

class LockTest : public testing::Test {
 public:
  static LockTest* current_;
  std::string file_;
  ROCKSDB_NAMESPACE::Env* env_;

  LockTest()
      : file_(test::PerThreadDBPath("db_testlock_file")),
        env_(ROCKSDB_NAMESPACE::Env::Default()) {
    current_ = this;
  }

  ~LockTest() override {}

  Status LockFile(FileLock** db_lock) {
    return env_->LockFile(file_, db_lock);
  }

  Status UnlockFile(FileLock* db_lock) {
    return env_->UnlockFile(db_lock);
  }

  bool AssertFileIsLocked(){
    return CheckFileLock( /* lock_expected = */ true);
  }

  bool AssertFileIsNotLocked(){
    return CheckFileLock( /* lock_expected = */ false);
  }

  bool CheckFileLock(bool lock_expected){
    // We need to fork to check the fcntl lock as we need
    // to open and close the file from a different process
    // to avoid either releasing the lock on close, or not
    // contending for it when requesting a lock.

#ifdef OS_WIN

    // WaitForSingleObject and GetExitCodeProcess can do what waitpid does.
    // TODO - implement on Windows
    return true;

#else

    pid_t pid = fork();
    if ( 0 == pid ) {
      // child process
      int exit_val = EXIT_FAILURE;
      int fd = open(file_.c_str(), O_RDWR | O_CREAT, 0644);
      if (fd < 0) {
        // could not open file, could not check if it was locked
        fprintf( stderr, "Open on on file %s failed.\n",file_.c_str());
        exit(exit_val);
      }

      struct flock f;
      memset(&f, 0, sizeof(f));
      f.l_type = (F_WRLCK);
      f.l_whence = SEEK_SET;
      f.l_start = 0;
      f.l_len = 0; // Lock/unlock entire file
      int value = fcntl(fd, F_SETLK, &f);
      if( value == -1 ){
        if( lock_expected ){
          exit_val = EXIT_SUCCESS;
        }
      } else {
        if( ! lock_expected ){
          exit_val = EXIT_SUCCESS;
        }
      }
      close(fd); // lock is released for child process
      exit(exit_val);
    } else if (pid > 0) {
      // parent process
      int status;
      while (-1 == waitpid(pid, &status, 0));
      if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        // child process exited with non success status
        return false;
      } else {
        return true;
      }
    } else {
      fprintf( stderr, "Fork failed\n" );
      return false;
    }
    return false;

#endif

  }

};
LockTest* LockTest::current_;

TEST_F(LockTest, LockBySameThread) {
  FileLock* lock1;
  FileLock* lock2;

  // acquire a lock on a file
  ASSERT_OK(LockFile(&lock1));

  // check the file is locked
  ASSERT_TRUE( AssertFileIsLocked() );

  // re-acquire the lock on the same file. This should fail.
  ASSERT_TRUE(LockFile(&lock2).IsIOError());

  // check the file is locked
  ASSERT_TRUE( AssertFileIsLocked() );

  // release the lock
  ASSERT_OK(UnlockFile(lock1));

  // check the file is not locked
  ASSERT_TRUE( AssertFileIsNotLocked() );

}

}  // namespace ROCKSDB_NAMESPACE

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}