diff options
author | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-14 22:20:32 +0000 |
---|---|---|
committer | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-14 22:20:32 +0000 |
commit | b2841b0739079820ae5a3f405fa777e5109a2e79 (patch) | |
tree | 4395081c3a84401c46936a6f1b89749336ce147c /chrome/browser/process_singleton_mac.cc | |
parent | bd9ca3554160c3e4cac9ff289d2713d216fcbd6f (diff) | |
download | chromium_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.cc | 83 |
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; } |