summaryrefslogtreecommitdiffstats
path: root/chrome/browser/process_singleton_mac.cc
diff options
context:
space:
mode:
authorshess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-14 22:20:32 +0000
committershess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-14 22:20:32 +0000
commitb2841b0739079820ae5a3f405fa777e5109a2e79 (patch)
tree4395081c3a84401c46936a6f1b89749336ce147c /chrome/browser/process_singleton_mac.cc
parentbd9ca3554160c3e4cac9ff289d2713d216fcbd6f (diff)
downloadchromium_src-b2841b0739079820ae5a3f405fa777e5109a2e79.zip
chromium_src-b2841b0739079820ae5a3f405fa777e5109a2e79.tar.gz
chromium_src-b2841b0739079820ae5a3f405fa777e5109a2e79.tar.bz2
[Mac] Lock profile against multiple uses.
Mac uses Launch Services to make sure one Chrome is started from the GUI, and AppleEvents and Launch Services to pass in URLs. When a developer or test launches multiple browsers against a profile, it fails in mysterious ways. This adds a profile lock to flush out any hidden assumptions. BUG=22163,58986,59061 TEST=Run Chromium twice with same --user-data-dir. Second instances shouldn't launch. Review URL: http://codereview.chromium.org/3777003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@62659 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/process_singleton_mac.cc')
-rw-r--r--chrome/browser/process_singleton_mac.cc83
1 files changed, 75 insertions, 8 deletions
diff --git a/chrome/browser/process_singleton_mac.cc b/chrome/browser/process_singleton_mac.cc
index b6d2498..994efca 100644
--- a/chrome/browser/process_singleton_mac.cc
+++ b/chrome/browser/process_singleton_mac.cc
@@ -2,8 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/file.h>
+
#include "chrome/browser/process_singleton.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_util.h"
+#include "base/metrics/histogram.h"
+#include "chrome/common/chrome_constants.h"
+
+namespace {
+
+// From "man 2 intro".
+const int kMaxErrno = EOPNOTSUPP;
+
+} // namespace
+
// This class is used to funnel messages to a single instance of the browser
// process. This is needed for several reasons on other platforms.
//
@@ -17,16 +33,20 @@
// Neither of those cases apply on the Mac. Launch Services ensures that there
// is only one instance of the process, and we get URLs to open via AppleEvents
// and, once again, the Launch Services system. We have no need to manage this
-// ourselves.
+// ourselves. An exclusive lock is used to flush out anyone making incorrect
+// assumptions.
ProcessSingleton::ProcessSingleton(const FilePath& user_data_dir)
: locked_(false),
- foreground_window_(NULL) {
- // This space intentionally left blank.
+ foreground_window_(NULL),
+ lock_path_(user_data_dir.Append(chrome::kSingletonLockFilename)),
+ lock_fd_(-1) {
}
ProcessSingleton::~ProcessSingleton() {
- // This space intentionally left blank.
+ // Make sure the lock is released. Process death will also release
+ // it, even if this is not called.
+ Cleanup();
}
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
@@ -35,15 +55,62 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
}
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
- // This space intentionally left blank.
- return PROCESS_NONE;
+ // Windows tries NotifyOtherProcess() first.
+ return Create() ? PROCESS_NONE : PROFILE_IN_USE;
}
+// Attempt to acquire an exclusive lock on an empty file in the
+// profile directory. Returns |true| if it gets the lock.
+// TODO(shess): The older code always returned |true|. Monitor the
+// histograms and convert the marked failure cases to |false| once
+// it's clear that it is safe to do. http://crbug.com/58986
+// TODO(shess): Rather than logging failures, popup an alert. Punting
+// that for now because it would require confidence that this code is
+// never called in a situation where an alert wouldn't work.
+// http://crbug.com/59061
bool ProcessSingleton::Create() {
- // This space intentionally left blank.
+ DCHECK_EQ(-1, lock_fd_) << "lock_path_ is already open.";
+
+ lock_fd_ = HANDLE_EINTR(open(lock_path_.value().c_str(),
+ O_RDONLY | O_CREAT, 0644));
+ if (lock_fd_ == -1) {
+ const int capture_errno = errno;
+ DPCHECK(lock_fd_ != -1) << "Unexpected failure opening profile lockfile";
+ UMA_HISTOGRAM_ENUMERATION("ProcessSingleton.OpenError",
+ capture_errno, kMaxErrno);
+ // TODO(shess): Change to |false|.
+ return true;
+ }
+
+ // Acquire an exclusive lock in non-blocking fashion. If the lock
+ // is already held, this will return |EWOULDBLOCK|.
+ int rc = HANDLE_EINTR(flock(lock_fd_, LOCK_EX|LOCK_NB));
+ if (rc == -1) {
+ const int capture_errno = errno;
+ DPCHECK(errno == EWOULDBLOCK)
+ << "Unexpected failure locking profile lockfile";
+
+ Cleanup();
+
+ // Other errors indicate something crazy is happening.
+ if (capture_errno != EWOULDBLOCK) {
+ UMA_HISTOGRAM_ENUMERATION("ProcessSingleton.LockError",
+ capture_errno, kMaxErrno);
+ // TODO(shess): Change to |false|.
+ return true;
+ }
+
+ // The file is open by another process and locked.
+ LOG(ERROR) << "Unable to obtain profile lock.";
+ return false;
+ }
+
return true;
}
void ProcessSingleton::Cleanup() {
- // This space intentionally left blank.
+ // Closing the file also releases the lock.
+ if (lock_fd_ != -1)
+ HANDLE_EINTR(close(lock_fd_));
+ lock_fd_ = -1;
}