summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/chrome_common.gypi4
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/multi_process_lock.h39
-rw-r--r--chrome/common/multi_process_lock_linux.cc95
-rw-r--r--chrome/common/multi_process_lock_mac.cc54
-rw-r--r--chrome/common/multi_process_lock_unittest.cc157
-rw-r--r--chrome/common/multi_process_lock_win.cc58
7 files changed, 408 insertions, 0 deletions
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index 89aeadc..f7f44fc 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -86,6 +86,10 @@
'common/message_router.h',
'common/metrics_helpers.cc',
'common/metrics_helpers.h',
+ 'common/multi_process_lock.h',
+ 'common/multi_process_lock_linux.cc',
+ 'common/multi_process_lock_mac.cc',
+ 'common/multi_process_lock_win.cc',
'common/nacl_cmd_line.cc',
'common/nacl_cmd_line.h',
'common/nacl_messages.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 6ea2d6c..354e1ca 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1570,6 +1570,7 @@
'common/json_schema_validator_unittest.cc',
'common/json_value_serializer_unittest.cc',
'common/mru_cache_unittest.cc',
+ 'common/multi_process_lock_unittest.cc',
'common/net/gaia/gaia_auth_fetcher_unittest.cc',
'common/net/gaia/gaia_auth_fetcher_unittest.h',
'common/net/gaia/gaia_authenticator_unittest.cc',
diff --git a/chrome/common/multi_process_lock.h b/chrome/common/multi_process_lock.h
new file mode 100644
index 0000000..24754f5
--- /dev/null
+++ b/chrome/common/multi_process_lock.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef CHROME_COMMON_MULTI_PROCESS_LOCK_H_
+#define CHROME_COMMON_MULTI_PROCESS_LOCK_H_
+#pragma once
+
+#include <sys/types.h>
+#include <string>
+
+// Platform abstraction for a lock that can be shared between processes.
+// The process that owns the lock will release it on exit even if
+// the exit is due to a crash. Locks are not recursive.
+class MultiProcessLock {
+ public:
+
+ // The length of a multi-process lock name is limited on Linux, so
+ // it is limited it on all platforms for consistency. This length does
+ // not include a terminator.
+ static const size_t MULTI_PROCESS_LOCK_NAME_MAX_LEN = 106;
+
+ // Factory method for creating a multi-process lock.
+ // |name| is the name of the lock. The name has special meaning on Windows
+ // where the prefix can determine the namespace of the lock.
+ // See http://msdn.microsoft.com/en-us/library/aa382954(v=VS.85).aspx for
+ // details.
+ static MultiProcessLock* Create(const std::string& name);
+
+ virtual ~MultiProcessLock() { }
+
+ // Try to grab ownership of the lock.
+ virtual bool TryLock() = 0;
+
+ // Release ownership of the lock.
+ virtual void Unlock() = 0;
+};
+
+#endif // CHROME_COMMON_MULTI_PROCESS_LOCK_H_
diff --git a/chrome/common/multi_process_lock_linux.cc b/chrome/common/multi_process_lock_linux.cc
new file mode 100644
index 0000000..0a83c89
--- /dev/null
+++ b/chrome/common/multi_process_lock_linux.cc
@@ -0,0 +1,95 @@
+// 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 "chrome/common/multi_process_lock.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/logging.h"
+
+class MultiProcessLockLinux : public MultiProcessLock {
+ public:
+ explicit MultiProcessLockLinux(const std::string& name)
+ : name_(name), fd_(-1) { }
+
+ virtual ~MultiProcessLockLinux() {
+ if (fd_ != -1) {
+ Unlock();
+ }
+ }
+
+ virtual bool TryLock() {
+ if (fd_ != -1) {
+ DLOG(ERROR) << "MultiProcessLock is already locked - " << name_;
+ return true;
+ }
+
+ if (name_.length() > MULTI_PROCESS_LOCK_NAME_MAX_LEN) {
+ LOG(ERROR) << "Socket name too long - " << name_;
+ return false;
+ }
+
+ struct sockaddr_un address;
+
+ // +1 for terminator, +1 for 0 in position 0 that makes it an
+ // abstract named socket.
+ // If this assert fails it is because sockaddr_un.sun_path size has been
+ // redefined and MULTI_PROCESS_LOCK_NAME_MAX_LEN can change accordingly.
+ COMPILE_ASSERT(sizeof(address.sun_path)
+ == MULTI_PROCESS_LOCK_NAME_MAX_LEN + 2, sun_path_size_changed);
+
+ memset(&address, 0, sizeof(address));
+ strcpy(&address.sun_path[1], name_.c_str());
+
+ // Must set the first character of the path to something non-zero
+ // before we call SUN_LEN which depends on strcpy working.
+ address.sun_path[0] = '@';
+ size_t length = SUN_LEN(&address);
+
+ // Reset the first character of the path back to zero so that
+ // bind returns an abstract name socket.
+ address.sun_path[0] = 0;
+ address.sun_family = AF_LOCAL;
+
+ int socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (socket_fd < 0) {
+ PLOG(ERROR) << "Couldn't create socket - " << name_;
+ return false;
+ }
+
+ if (bind(socket_fd,
+ reinterpret_cast<sockaddr *>(&address),
+ length) == 0) {
+ fd_ = socket_fd;
+ return true;
+ } else {
+ PLOG(ERROR) << "Couldn't bind socket - "
+ << &(address.sun_path[1])
+ << " Length: " << length;
+ HANDLE_EINTR(close(socket_fd));
+ return false;
+ }
+ }
+
+ virtual void Unlock() {
+ if (fd_ == -1) {
+ DLOG(ERROR) << "Over-unlocked MultiProcessLock - " << name_;
+ return;
+ }
+ HANDLE_EINTR(close(fd_));
+ fd_ = -1;
+ }
+
+ private:
+ std::string name_;
+ int fd_;
+ DISALLOW_COPY_AND_ASSIGN(MultiProcessLockLinux);
+};
+
+MultiProcessLock* MultiProcessLock::Create(const std::string &name) {
+ return new MultiProcessLockLinux(name);
+}
diff --git a/chrome/common/multi_process_lock_mac.cc b/chrome/common/multi_process_lock_mac.cc
new file mode 100644
index 0000000..9a16d71
--- /dev/null
+++ b/chrome/common/multi_process_lock_mac.cc
@@ -0,0 +1,54 @@
+// 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 "chrome/common/multi_process_lock.h"
+
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/sys_string_conversions.h"
+
+class MultiProcessLockMac : public MultiProcessLock {
+ public:
+ explicit MultiProcessLockMac(const std::string& name) : name_(name) { }
+
+ virtual ~MultiProcessLockMac() {
+ if (port_ != NULL) {
+ Unlock();
+ }
+ }
+
+ virtual bool TryLock() {
+ if (port_ != NULL) {
+ DLOG(ERROR) << "MultiProcessLock is already locked - " << name_;
+ return true;
+ }
+
+ if (name_.length() > MULTI_PROCESS_LOCK_NAME_MAX_LEN) {
+ LOG(ERROR) << "Socket name too long - " << name_;
+ return false;
+ }
+
+ CFStringRef cf_name(base::SysUTF8ToCFStringRef(name_));
+ base::mac::ScopedCFTypeRef<CFStringRef> scoped_cf_name(cf_name);
+ port_.reset(CFMessagePortCreateLocal(NULL, cf_name, NULL, NULL, NULL));
+ return port_ != NULL;
+ }
+
+ virtual void Unlock() {
+ if (port_ == NULL) {
+ DLOG(ERROR) << "Over-unlocked MultiProcessLock - " << name_;
+ return;
+ }
+ port_.reset();
+ }
+
+ private:
+ std::string name_;
+ base::mac::ScopedCFTypeRef<CFMessagePortRef> port_;
+ DISALLOW_COPY_AND_ASSIGN(MultiProcessLockMac);
+};
+
+MultiProcessLock* MultiProcessLock::Create(const std::string &name) {
+ return new MultiProcessLockMac(name);
+}
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;
+}
diff --git a/chrome/common/multi_process_lock_win.cc b/chrome/common/multi_process_lock_win.cc
new file mode 100644
index 0000000..8bb525d
--- /dev/null
+++ b/chrome/common/multi_process_lock_win.cc
@@ -0,0 +1,58 @@
+// 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 "chrome/common/multi_process_lock.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_handle.h"
+
+class MultiProcessLockWin : public MultiProcessLock {
+ public:
+ explicit MultiProcessLockWin(const std::string& name) : name_(name) { }
+
+ virtual ~MultiProcessLockWin() {
+ if (event_.Get() != NULL) {
+ Unlock();
+ }
+ }
+
+ virtual bool TryLock() {
+ if (event_.Get() != NULL) {
+ DLOG(ERROR) << "MultiProcessLock is already locked - " << name_;
+ return true;
+ }
+
+ if (name_.length() > MULTI_PROCESS_LOCK_NAME_MAX_LEN) {
+ LOG(ERROR) << "Socket name too long - " << name_;
+ return false;
+ }
+
+ string16 wname = UTF8ToUTF16(name_);
+ event_.Set(CreateEvent(NULL, FALSE, FALSE, wname.c_str()));
+ if (event_.Get() && GetLastError() != ERROR_ALREADY_EXISTS) {
+ return true;
+ } else {
+ event_.Set(NULL);
+ return false;
+ }
+ }
+
+ virtual void Unlock() {
+ if (event_.Get() == NULL) {
+ DLOG(ERROR) << "Over-unlocked MultiProcessLock - " << name_;
+ return;
+ }
+ event_.Set(NULL);
+ }
+
+ private:
+ std::string name_;
+ base::win::ScopedHandle event_;
+ DISALLOW_COPY_AND_ASSIGN(MultiProcessLockWin);
+};
+
+MultiProcessLock* MultiProcessLock::Create(const std::string &name) {
+ return new MultiProcessLockWin(name);
+}