summaryrefslogtreecommitdiffstats
path: root/chrome/common/multi_process_lock_unittest.cc
diff options
context:
space:
mode:
authordmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-16 21:14:58 +0000
committerdmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-16 21:14:58 +0000
commit293fd7a9eb27dac7599a752e847c5b15f449519f (patch)
treeacc6e4a4c1d5d2ea551a64b92e8223dc94bad728 /chrome/common/multi_process_lock_unittest.cc
parentc18215ab75f2f05e1da3f901fd385393a5d8def6 (diff)
downloadchromium_src-293fd7a9eb27dac7599a752e847c5b15f449519f.zip
chromium_src-293fd7a9eb27dac7599a752e847c5b15f449519f.tar.gz
chromium_src-293fd7a9eb27dac7599a752e847c5b15f449519f.tar.bz2
Add multi_process_lock to base
Platform abstraction for a shared lock between processes. The process that owns the lock will release it on exit even if exit is due to a crash. For cloud-print and remoting we want to be able to have a singleton service-process that can run independently of the browser process. This service process will communicate with the browser process via the standard IAC channels, but we want to have a way of signaling to other processes that a) there is a service-process running and b) that it is in a state where it is ready to be communicated with. The multi_process_lock class is intended to work as a simple flag that can be queried from multiple processes. If the service-process should crash, we would like the flag to be cleared automatically so that there is never confusion about the state of the service-process. Other approaches considered for some Unix/Mac: - Standard unix domain sockets depend on the file system and don't clean up properly in the case of a crash. - Shared memory on unix depend on the file system and don't clean up properly in the case of a crash. - System V semaphores on unix again depend on the file system. - named_mach_ports on Mac OS. Bootstrap_register_name has been deprecated on 10.6, so we are doing essentially the same thing using CFMessagePort. On Windows it is implemented as an event. On Mac it is implemented using a CFMessagePort name. On Linux it is implement using an abstract name port socket. TEST=none BUG=none Review URL: http://codereview.chromium.org/4721001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@66323 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/multi_process_lock_unittest.cc')
-rw-r--r--chrome/common/multi_process_lock_unittest.cc157
1 files changed, 157 insertions, 0 deletions
diff --git a/chrome/common/multi_process_lock_unittest.cc b/chrome/common/multi_process_lock_unittest.cc
new file mode 100644
index 0000000..733eb2d
--- /dev/null
+++ b/chrome/common/multi_process_lock_unittest.cc
@@ -0,0 +1,157 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/scoped_ptr.h"
+#include "base/stringprintf.h"
+#include "base/test/multiprocess_test.h"
+#include "base/time.h"
+#include "chrome/common/multi_process_lock.h"
+#include "testing/multiprocess_func_list.h"
+
+class MultiProcessLockTest : public base::MultiProcessTest {
+ public:
+ static const char kLockEnviromentVarName[];
+
+ class ScopedEnvironmentVariable {
+ public:
+ ScopedEnvironmentVariable(const std::string &name,
+ const std::string &value)
+ : name_(name), environment_(base::Environment::Create()) {
+ environment_->SetVar(name_.c_str(), value);
+ }
+ ~ScopedEnvironmentVariable() {
+ environment_->UnSetVar(name_.c_str());
+ }
+
+ private:
+ std::string name_;
+ scoped_ptr<base::Environment> environment_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedEnvironmentVariable);
+ };
+
+ std::string GenerateLockName();
+ void ExpectLockIsLocked(const std::string &name);
+ void ExpectLockIsUnlocked(const std::string &name);
+};
+
+const char MultiProcessLockTest::kLockEnviromentVarName[]
+ = "MULTI_PROCESS_TEST_LOCK_NAME";
+
+std::string MultiProcessLockTest::GenerateLockName() {
+ base::Time now = base::Time::NowFromSystemTime();
+ return base::StringPrintf("multi_process_test_lock %lf%lf",
+ now.ToDoubleT(), base::RandDouble());
+}
+
+void MultiProcessLockTest::ExpectLockIsLocked(const std::string &name) {
+ ScopedEnvironmentVariable var(kLockEnviromentVarName, name);
+ base::ProcessHandle handle = SpawnChild("MultiProcessLockTryFailMain", false);
+ ASSERT_TRUE(handle);
+ int exit_code = 0;
+ EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
+ EXPECT_EQ(exit_code, 0);
+}
+
+void MultiProcessLockTest::ExpectLockIsUnlocked(
+ const std::string &name) {
+ ScopedEnvironmentVariable var(kLockEnviromentVarName, name);
+ base::ProcessHandle handle = SpawnChild("MultiProcessLockTrySucceedMain",
+ false);
+ ASSERT_TRUE(handle);
+ int exit_code = 0;
+ EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
+ EXPECT_EQ(exit_code, 0);
+}
+
+TEST_F(MultiProcessLockTest, BasicCreationTest) {
+ // Test basic creation/destruction with no lock taken
+ std::string name = GenerateLockName();
+ scoped_ptr<MultiProcessLock> scoped(MultiProcessLock::Create(name));
+ ExpectLockIsUnlocked(name);
+ scoped.reset(NULL);
+}
+
+TEST_F(MultiProcessLockTest, LongNameTest) {
+ // Linux has a max path name of 108 characters.
+ // http://lxr.linux.no/linux+v2.6.36/include/linux/un.h
+ // This is enforced on all platforms.
+ LOG(INFO) << "Following error log due to long name is expected";
+ std::string name("This is a name that is longer than one hundred and eight "
+ "characters to make sure that we fail appropriately on linux when we "
+ "have a path that is to long for linux to handle");
+ scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
+ EXPECT_FALSE(test_lock->TryLock());
+}
+
+TEST_F(MultiProcessLockTest, SimpleLock) {
+ std::string name = GenerateLockName();
+ scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
+ EXPECT_TRUE(test_lock->TryLock());
+ ExpectLockIsLocked(name);
+ test_lock->Unlock();
+ ExpectLockIsUnlocked(name);
+}
+
+TEST_F(MultiProcessLockTest, RecursiveLock) {
+ std::string name = GenerateLockName();
+ scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
+ EXPECT_TRUE(test_lock->TryLock());
+ ExpectLockIsLocked(name);
+ LOG(INFO) << "Following error log "
+ << "'MultiProcessLock is already locked' is expected";
+ EXPECT_TRUE(test_lock->TryLock());
+ ExpectLockIsLocked(name);
+ test_lock->Unlock();
+ ExpectLockIsUnlocked(name);
+ LOG(INFO) << "Following error log "
+ << "'Over-unlocked MultiProcessLock' is expected";
+ test_lock->Unlock();
+ ExpectLockIsUnlocked(name);
+ test_lock.reset();
+}
+
+TEST_F(MultiProcessLockTest, LockScope) {
+ // Check to see that lock is released when it goes out of scope.
+ std::string name = GenerateLockName();
+ {
+ scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
+ EXPECT_TRUE(test_lock->TryLock());
+ ExpectLockIsLocked(name);
+ }
+ ExpectLockIsUnlocked(name);
+}
+
+MULTIPROCESS_TEST_MAIN(MultiProcessLockTryFailMain) {
+ std::string name;
+ scoped_ptr<base::Environment> environment(base::Environment::Create());
+ EXPECT_TRUE(environment->GetVar(MultiProcessLockTest::kLockEnviromentVarName,
+ &name));
+#if defined(OS_MACOSX)
+ // OS X sends out a log if a lock fails.
+ // Hopefully this will keep people from panicking about it when they
+ // are perusing thge build logs.
+ LOG(INFO) << "Following error log "
+ << "\"CFMessagePort: bootstrap_register(): failed 1100 (0x44c) "
+ << "'Permission denied'\" is expected";
+#endif // defined(OS_MACOSX)
+ scoped_ptr<MultiProcessLock> test_lock(
+ MultiProcessLock::Create(name));
+ EXPECT_FALSE(test_lock->TryLock());
+ return 0;
+}
+
+MULTIPROCESS_TEST_MAIN(MultiProcessLockTrySucceedMain) {
+ std::string name;
+ scoped_ptr<base::Environment> environment(base::Environment::Create());
+ EXPECT_TRUE(environment->GetVar(MultiProcessLockTest::kLockEnviromentVarName,
+ &name));
+ scoped_ptr<MultiProcessLock> test_lock(
+ MultiProcessLock::Create(name));
+ EXPECT_TRUE(test_lock->TryLock());
+ return 0;
+}