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