blob: 638d74ddf6ab187894e177b19147d1f6b91e3ea5 [file] [log] [blame]
shessdd6fc7e52015-12-11 21:53:331// Copyright 2015 The Chromium Authors. All rights reserved.
shesse66da3f2015-12-10 22:12:172// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/command_line.h"
6#include "base/files/file.h"
7#include "base/files/file_util.h"
8#include "base/files/scoped_temp_dir.h"
avi543540e2015-12-24 05:15:329#include "base/macros.h"
shesse66da3f2015-12-10 22:12:1710#include "base/test/multiprocess_test.h"
11#include "base/test/test_timeouts.h"
12#include "base/threading/platform_thread.h"
13#include "base/time/time.h"
Alexandre Elias52f64302017-07-26 22:23:0814#include "build/build_config.h"
shesse66da3f2015-12-10 22:12:1715#include "testing/gtest/include/gtest/gtest.h"
16#include "testing/multiprocess_func_list.h"
17
18using base::File;
19using base::FilePath;
20
21namespace {
22
23// Flag for the parent to share a temp dir to the child.
24const char kTempDirFlag[] = "temp-dir";
25
Victor Costan1cf421f2019-01-28 19:35:5926// Flags to control how the process locks the file.
27const char kFileLockShared[] = "file-lock-shared";
28const char kFileLockExclusive[] = "file-lock-exclusive";
29
shesse66da3f2015-12-10 22:12:1730// Flags to control how the subprocess unlocks the file.
31const char kFileUnlock[] = "file-unlock";
32const char kCloseUnlock[] = "close-unlock";
33const char kExitUnlock[] = "exit-unlock";
34
35// File to lock in temp dir.
36const char kLockFile[] = "lockfile";
37
38// Constants for various requests and responses, used as |signal_file| parameter
39// to signal/wait helpers.
40const char kSignalLockFileLocked[] = "locked.signal";
41const char kSignalLockFileClose[] = "close.signal";
42const char kSignalLockFileClosed[] = "closed.signal";
43const char kSignalLockFileUnlock[] = "unlock.signal";
44const char kSignalLockFileUnlocked[] = "unlocked.signal";
45const char kSignalExit[] = "exit.signal";
46
47// Signal an event by creating a file which didn't previously exist.
48bool SignalEvent(const FilePath& signal_dir, const char* signal_file) {
49 File file(signal_dir.AppendASCII(signal_file),
shessdd6fc7e52015-12-11 21:53:3350 File::FLAG_CREATE | File::FLAG_WRITE);
shesse66da3f2015-12-10 22:12:1751 return file.IsValid();
52}
53
54// Check whether an event was signaled.
55bool CheckEvent(const FilePath& signal_dir, const char* signal_file) {
56 File file(signal_dir.AppendASCII(signal_file),
57 File::FLAG_OPEN | File::FLAG_READ);
58 return file.IsValid();
59}
60
61// Busy-wait for an event to be signaled, returning false for timeout.
62bool WaitForEventWithTimeout(const FilePath& signal_dir,
63 const char* signal_file,
64 const base::TimeDelta& timeout) {
65 const base::Time finish_by = base::Time::Now() + timeout;
66 while (!CheckEvent(signal_dir, signal_file)) {
67 if (base::Time::Now() > finish_by)
68 return false;
69 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
70 }
71 return true;
72}
73
74// Wait forever for the event to be signaled (should never return false).
75bool WaitForEvent(const FilePath& signal_dir, const char* signal_file) {
76 return WaitForEventWithTimeout(signal_dir, signal_file,
77 base::TimeDelta::Max());
78}
79
80// Keep these in sync so StartChild*() can refer to correct test main.
81#define ChildMain ChildLockUnlock
82#define ChildMainString "ChildLockUnlock"
83
84// Subprocess to test getting a file lock then releasing it. |kTempDirFlag|
85// must pass in an existing temporary directory for the lockfile and signal
86// files. One of the following flags must be passed to determine how to unlock
87// the lock file:
88// - |kFileUnlock| calls Unlock() to unlock.
89// - |kCloseUnlock| calls Close() while the lock is held.
90// - |kExitUnlock| exits while the lock is held.
91MULTIPROCESS_TEST_MAIN(ChildMain) {
92 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
93 const FilePath temp_path = command_line->GetSwitchValuePath(kTempDirFlag);
94 CHECK(base::DirectoryExists(temp_path));
95
Victor Costan1cf421f2019-01-28 19:35:5996 const bool use_shared_lock = command_line->HasSwitch(kFileLockShared);
97 const bool use_exclusive_lock = command_line->HasSwitch(kFileLockExclusive);
98 CHECK_NE(use_shared_lock, use_exclusive_lock);
99
100 const File::LockMode mode =
101 use_exclusive_lock ? File::LockMode::kExclusive : File::LockMode::kShared;
102
shesse66da3f2015-12-10 22:12:17103 // Immediately lock the file.
104 File file(temp_path.AppendASCII(kLockFile),
105 File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE);
106 CHECK(file.IsValid());
Victor Costan1cf421f2019-01-28 19:35:59107 CHECK_EQ(File::FILE_OK, file.Lock(mode));
shesse66da3f2015-12-10 22:12:17108 CHECK(SignalEvent(temp_path, kSignalLockFileLocked));
109
110 if (command_line->HasSwitch(kFileUnlock)) {
111 // Wait for signal to unlock, then unlock the file.
112 CHECK(WaitForEvent(temp_path, kSignalLockFileUnlock));
113 CHECK_EQ(File::FILE_OK, file.Unlock());
114 CHECK(SignalEvent(temp_path, kSignalLockFileUnlocked));
115 } else if (command_line->HasSwitch(kCloseUnlock)) {
116 // Wait for the signal to close, then close the file.
117 CHECK(WaitForEvent(temp_path, kSignalLockFileClose));
118 file.Close();
119 CHECK(!file.IsValid());
120 CHECK(SignalEvent(temp_path, kSignalLockFileClosed));
121 } else {
122 CHECK(command_line->HasSwitch(kExitUnlock));
123 }
124
125 // Wait for signal to exit, so that unlock or close can be distinguished from
126 // exit.
127 CHECK(WaitForEvent(temp_path, kSignalExit));
128 return 0;
129}
130
131} // namespace
132
133class FileLockingTest : public testing::Test {
134 public:
Chris Watkinsbb7211c2017-11-29 07:16:38135 FileLockingTest() = default;
shesse66da3f2015-12-10 22:12:17136
137 protected:
138 void SetUp() override {
139 testing::Test::SetUp();
shesse66da3f2015-12-10 22:12:17140
shessdd6fc7e52015-12-11 21:53:33141 // Setup the temp dir and the lock file.
142 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
shesse66da3f2015-12-10 22:12:17143 lock_file_.Initialize(
vabr411f4fc2016-09-08 09:26:27144 temp_dir_.GetPath().AppendASCII(kLockFile),
shesse66da3f2015-12-10 22:12:17145 File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
146 ASSERT_TRUE(lock_file_.IsValid());
shesse66da3f2015-12-10 22:12:17147 }
148
149 bool SignalEvent(const char* signal_file) {
vabr411f4fc2016-09-08 09:26:27150 return ::SignalEvent(temp_dir_.GetPath(), signal_file);
shesse66da3f2015-12-10 22:12:17151 }
152
153 bool WaitForEventOrTimeout(const char* signal_file) {
vabr411f4fc2016-09-08 09:26:27154 return ::WaitForEventWithTimeout(temp_dir_.GetPath(), signal_file,
shesse66da3f2015-12-10 22:12:17155 TestTimeouts::action_timeout());
156 }
157
Victor Costan1cf421f2019-01-28 19:35:59158 // Start a child process set to use the specified locking mode and unlock
159 // action, and wait for it to lock the file.
160 void StartChildAndSignalLock(File::LockMode lock_mode,
161 const char* unlock_action) {
shesse66da3f2015-12-10 22:12:17162 // Create a temporary dir and spin up a ChildLockExit subprocess against it.
vabr411f4fc2016-09-08 09:26:27163 const FilePath temp_path = temp_dir_.GetPath();
shesse66da3f2015-12-10 22:12:17164 base::CommandLine child_command_line(
165 base::GetMultiProcessTestChildBaseCommandLine());
166 child_command_line.AppendSwitchPath(kTempDirFlag, temp_path);
167 child_command_line.AppendSwitch(unlock_action);
Victor Costan1cf421f2019-01-28 19:35:59168 switch (lock_mode) {
169 case File::LockMode::kExclusive:
170 child_command_line.AppendSwitch(kFileLockExclusive);
171 break;
172 case File::LockMode::kShared:
173 child_command_line.AppendSwitch(kFileLockShared);
174 break;
175 }
Jay Civelli4a44260b2017-08-21 19:26:29176 lock_child_ = base::SpawnMultiProcessTestChild(
jcivelli87c322b2017-03-14 17:55:53177 ChildMainString, child_command_line, base::LaunchOptions());
Jay Civelli4a44260b2017-08-21 19:26:29178 ASSERT_TRUE(lock_child_.IsValid());
shesse66da3f2015-12-10 22:12:17179
180 // Wait for the child to lock the file.
181 ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileLocked));
182 }
183
184 // Signal the child to exit cleanly.
185 void ExitChildCleanly() {
186 ASSERT_TRUE(SignalEvent(kSignalExit));
187 int rv = -1;
jcivellif4462a352017-01-10 04:45:59188 ASSERT_TRUE(WaitForMultiprocessTestChildExit(
Jay Civelli4a44260b2017-08-21 19:26:29189 lock_child_, TestTimeouts::action_timeout(), &rv));
shesse66da3f2015-12-10 22:12:17190 ASSERT_EQ(0, rv);
191 }
192
193 base::ScopedTempDir temp_dir_;
194 base::File lock_file_;
Jay Civelli4a44260b2017-08-21 19:26:29195 base::Process lock_child_;
shesse66da3f2015-12-10 22:12:17196
197 private:
198 DISALLOW_COPY_AND_ASSIGN(FileLockingTest);
199};
200
201// Test that locks are released by Unlock().
Victor Costan1cf421f2019-01-28 19:35:59202TEST_F(FileLockingTest, LockAndUnlockExclusive) {
203 StartChildAndSignalLock(File::LockMode::kExclusive, kFileUnlock);
shesse66da3f2015-12-10 22:12:17204
Victor Costan1cf421f2019-01-28 19:35:59205 ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
206 ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
shesse66da3f2015-12-10 22:12:17207 ASSERT_TRUE(SignalEvent(kSignalLockFileUnlock));
208 ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileUnlocked));
Victor Costan1cf421f2019-01-28 19:35:59209 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
210 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
211 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
212 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
213
214 ExitChildCleanly();
215}
216TEST_F(FileLockingTest, LockAndUnlockShared) {
217 StartChildAndSignalLock(File::LockMode::kShared, kFileUnlock);
218
219 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
220 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
221 ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
222 ASSERT_TRUE(SignalEvent(kSignalLockFileUnlock));
223 ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileUnlocked));
224 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
225 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
226 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
shesse66da3f2015-12-10 22:12:17227 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
228
229 ExitChildCleanly();
230}
231
232// Test that locks are released on Close().
Victor Costan1cf421f2019-01-28 19:35:59233TEST_F(FileLockingTest, UnlockOnCloseExclusive) {
234 StartChildAndSignalLock(File::LockMode::kExclusive, kCloseUnlock);
shesse66da3f2015-12-10 22:12:17235
Victor Costan1cf421f2019-01-28 19:35:59236 ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
237 ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
shesse66da3f2015-12-10 22:12:17238 ASSERT_TRUE(SignalEvent(kSignalLockFileClose));
239 ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileClosed));
Victor Costan1cf421f2019-01-28 19:35:59240 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
241 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
242 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
243 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
244
245 ExitChildCleanly();
246}
247TEST_F(FileLockingTest, UnlockOnCloseShared) {
248 StartChildAndSignalLock(File::LockMode::kShared, kCloseUnlock);
249
250 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
251 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
252 ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
253 ASSERT_TRUE(SignalEvent(kSignalLockFileClose));
254 ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileClosed));
255 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
256 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
257 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
shesse66da3f2015-12-10 22:12:17258 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
259
260 ExitChildCleanly();
261}
262
263// Test that locks are released on exit.
Victor Costan1cf421f2019-01-28 19:35:59264TEST_F(FileLockingTest, UnlockOnExitExclusive) {
265 StartChildAndSignalLock(File::LockMode::kExclusive, kExitUnlock);
shesse66da3f2015-12-10 22:12:17266
Victor Costan1cf421f2019-01-28 19:35:59267 ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
268 ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
shesse66da3f2015-12-10 22:12:17269 ExitChildCleanly();
Victor Costan1cf421f2019-01-28 19:35:59270 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
271 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
272 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
273 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
274}
275TEST_F(FileLockingTest, UnlockOnExitShared) {
276 StartChildAndSignalLock(File::LockMode::kShared, kExitUnlock);
277
278 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
279 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
280 ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
281 ExitChildCleanly();
282 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
283 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
284 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
shesse66da3f2015-12-10 22:12:17285 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
286}
287
288// Test that killing the process releases the lock. This should cover crashing.
Jeremy Roman1a2e29b22017-08-16 18:20:00289// Flaky on Android (http://crbug.com/747518)
290#if defined(OS_ANDROID)
291#define MAYBE_UnlockOnTerminate DISABLED_UnlockOnTerminate
292#else
293#define MAYBE_UnlockOnTerminate UnlockOnTerminate
294#endif
295TEST_F(FileLockingTest, MAYBE_UnlockOnTerminate) {
shesse66da3f2015-12-10 22:12:17296 // The child will wait for an exit which never arrives.
Victor Costan1cf421f2019-01-28 19:35:59297 StartChildAndSignalLock(File::LockMode::kExclusive, kExitUnlock);
shesse66da3f2015-12-10 22:12:17298
Victor Costan1cf421f2019-01-28 19:35:59299 ASSERT_NE(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
Jay Civelli4a44260b2017-08-21 19:26:29300 ASSERT_TRUE(TerminateMultiProcessTestChild(lock_child_, 0, true));
Victor Costan1cf421f2019-01-28 19:35:59301 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kShared));
302 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
303 ASSERT_EQ(File::FILE_OK, lock_file_.Lock(File::LockMode::kExclusive));
shesse66da3f2015-12-10 22:12:17304 ASSERT_EQ(File::FILE_OK, lock_file_.Unlock());
305}