summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/mach_ipc_mac.h33
-rw-r--r--base/mach_ipc_mac.mm13
-rw-r--r--base/process_util.h12
-rw-r--r--base/process_util_posix.cc101
-rw-r--r--chrome/app/chrome_dll_main.cc28
-rw-r--r--chrome/browser/child_process_launcher.cc36
-rw-r--r--chrome/browser/mach_broker_mac.cc158
-rw-r--r--chrome/browser/mach_broker_mac.h37
-rw-r--r--chrome/browser/mach_broker_mac_unittest.cc46
9 files changed, 296 insertions, 168 deletions
diff --git a/base/mach_ipc_mac.h b/base/mach_ipc_mac.h
index 5836c3a..91b9729 100644
--- a/base/mach_ipc_mac.h
+++ b/base/mach_ipc_mac.h
@@ -20,7 +20,7 @@
//
// The three main classes of interest are
//
-// MachMessage: a wrapper for a mach message of the following form
+// MachMessage: a wrapper for a Mach message of the following form
// mach_msg_header_t
// mach_msg_body_t
// optional descriptors
@@ -30,10 +30,10 @@
// and are used instead of MachMessage which is an abstract base class
//
// ReceivePort:
-// Represents a mach port for which we have receive rights
+// Represents a Mach port for which we have receive rights
//
// MachPortSender:
-// Represents a mach port for which we have send rights
+// Represents a Mach port for which we have send rights
//
// Here's an example to receive a message on a server port:
//
@@ -127,7 +127,7 @@ class MachMsgPortDescriptor : public mach_msg_port_descriptor_t {
};
//==============================================================================
-// MachMessage: a wrapper for a mach message
+// MachMessage: a wrapper for a Mach message
// (mach_msg_header_t, mach_msg_body_t, extra data)
//
// This considerably simplifies the construction of a message for sending
@@ -165,7 +165,7 @@ class MachMessage {
int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); }
- // Adds a descriptor (typically a mach port) to be translated
+ // Adds a descriptor (typically a Mach port) to be translated
// returns true if successful, otherwise not enough space
bool AddDescriptor(const MachMsgPortDescriptor &desc);
@@ -175,7 +175,7 @@ class MachMessage {
MachMsgPortDescriptor *GetDescriptor(int n);
- // Convenience method which gets the mach port described by the descriptor
+ // Convenience method which gets the Mach port described by the descriptor
mach_port_t GetTranslatedPort(int n);
// A simple message is one with no descriptors
@@ -212,7 +212,7 @@ class MachMessage {
int CalculateSize();
// Returns total storage size that this object can grow to, this is inclusive
- // of the mach header.
+ // of the Mach header.
size_t MaxSize() const { return storage_length_bytes_; }
protected:
@@ -242,7 +242,7 @@ class MachMessage {
//==============================================================================
// MachReceiveMessage and MachSendMessage are useful to separate the idea
-// of a mach message being sent and being received, and adds increased type
+// of a Mach message being sent and being received, and adds increased type
// safety:
// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage
// MachPortSender::SendMessage() only accepts a MachSendMessage
@@ -271,26 +271,27 @@ class MachSendMessage : public MachMessage {
};
//==============================================================================
-// Represents a mach port for which we have receive rights
+// Represents a Mach port for which we have receive rights
class ReceivePort {
public:
- // Creates a new mach port for receiving messages and registers a name for it
+ // Creates a new Mach port for receiving messages and registers a name for it
explicit ReceivePort(const char *receive_port_name);
- // Given an already existing mach port, use it. We take ownership of the
+ // Given an already existing Mach port, use it. We take ownership of the
// port and deallocate it in our destructor.
explicit ReceivePort(mach_port_t receive_port);
- // Create a new mach port for receiving messages
+ // Create a new Mach port for receiving messages
ReceivePort();
~ReceivePort();
- // Waits on the mach port until message received or timeout
+ // Waits on the Mach port until message received or timeout. If |timeout| is
+ // MACH_MSG_TIMEOUT_NONE, this method waits forever.
kern_return_t WaitForMessage(MachReceiveMessage *out_message,
mach_msg_timeout_t timeout);
- // The underlying mach port that we wrap
+ // The underlying Mach port that we wrap
mach_port_t GetPort() const { return port_; }
private:
@@ -301,14 +302,14 @@ class ReceivePort {
};
//==============================================================================
-// Represents a mach port for which we have send rights
+// Represents a Mach port for which we have send rights
class MachPortSender {
public:
// get a port with send rights corresponding to a named registered service
explicit MachPortSender(const char *receive_port_name);
- // Given an already existing mach port, use it. Does not take ownership of
+ // Given an already existing Mach port, use it. Does not take ownership of
// |send_port|.
explicit MachPortSender(mach_port_t send_port);
diff --git a/base/mach_ipc_mac.mm b/base/mach_ipc_mac.mm
index 973b66a..a0bfdc8 100644
--- a/base/mach_ipc_mac.mm
+++ b/base/mach_ipc_mac.mm
@@ -198,7 +198,12 @@ ReceivePort::ReceivePort(const char *receive_port_name) {
if (init_result_ != KERN_SUCCESS)
return;
- NSPort *ns_port = [NSMachPort portWithMachPort:port_];
+ // Without |NSMachPortDeallocateNone|, the NSMachPort seems to deallocate
+ // receive rights on port when it is eventually released. It is not necessary
+ // to deallocate any rights here as |port_| is fully deallocated in the
+ // ReceivePort destructor.
+ NSPort *ns_port = [NSMachPort portWithMachPort:port_
+ options:NSMachPortDeallocateNone];
NSString *port_name = [NSString stringWithUTF8String:receive_port_name];
[[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name];
}
@@ -252,8 +257,12 @@ kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
out_message->Head()->msgh_reserved = 0;
out_message->Head()->msgh_id = 0;
+ mach_msg_option_t rcv_options = MACH_RCV_MSG;
+ if (timeout != MACH_MSG_TIMEOUT_NONE)
+ rcv_options |= MACH_RCV_TIMEOUT;
+
kern_return_t result = mach_msg(out_message->Head(),
- MACH_RCV_MSG | MACH_RCV_TIMEOUT,
+ rcv_options,
0,
out_message->MaxSize(),
port_,
diff --git a/base/process_util.h b/base/process_util.h
index 4699c9e2..e64d0a7 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -235,18 +235,6 @@ bool LaunchApp(const std::vector<std::string>& argv,
// The returned array is allocated using new[] and must be freed by the caller.
char** AlterEnvironment(const environment_vector& changes,
const char* const* const env);
-
-#if defined(OS_MACOSX)
-// Similar to the above, but also returns the new process's task_t if
-// |task_handle| is not NULL. If |task_handle| is not NULL, the caller is
-// responsible for calling |mach_port_deallocate()| on the returned handle.
-bool LaunchAppAndGetTask(const std::vector<std::string>& argv,
- const environment_vector& environ,
- const file_handle_mapping_vector& fds_to_remap,
- bool wait,
- task_t* task_handle,
- ProcessHandle* process_handle);
-#endif // defined(OS_MACOSX)
#endif // defined(OS_POSIX)
// Executes the application specified by cl. This function delegates to one
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc
index 8fe64d0..d6171b1 100644
--- a/base/process_util_posix.cc
+++ b/base/process_util_posix.cc
@@ -32,8 +32,6 @@
#if defined(OS_MACOSX)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
-#include "base/mach_ipc_mac.h"
-#include "base/rand_util.h"
#else
extern char** environ;
#endif
@@ -301,82 +299,6 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
}
}
-#if defined(OS_MACOSX)
-static std::string MachErrorCode(kern_return_t err) {
- return StringPrintf("0x%x %s", err, mach_error_string(err));
-}
-
-// Forks the current process and returns the child's |task_t| in the parent
-// process.
-static pid_t fork_and_get_task(task_t* child_task) {
- const int kTimeoutMs = 100;
- kern_return_t err;
-
- // Put a random number into the channel name, so that a compromised renderer
- // can't pretend being the child that's forked off.
- std::string mach_connection_name = StringPrintf(
- "com.google.Chrome.samplingfork.%p.%d",
- child_task, base::RandInt(0, std::numeric_limits<int>::max()));
-
- // Create the mach receive port before forking to ensure that it exists when
- // the child tries to connect. Mach ports are not duped into the child, so
- // this is safe to set up here.
- ReceivePort parent_recv_port(mach_connection_name.c_str());
-
- // Error handling philosophy: If Mach IPC fails, don't touch |child_task| but
- // return a valid pid. If IPC fails in the child, the parent will have to wait
- // until kTimeoutMs is over. This is not optimal, but I've never seen it
- // happen, and stuff should still mostly work.
- pid_t pid = fork();
- switch (pid) {
- case -1:
- return pid;
- case 0: { // child
- // Must reset signal handlers before doing any mach IPC, as the mach IPC
- // calls can potentially hang forever.
- ResetChildSignalHandlersToDefaults();
- MachSendMessage child_message(/* id= */0);
- if (!child_message.AddDescriptor(mach_task_self())) {
- LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed.";
- return pid;
- }
-
- MachPortSender child_sender(mach_connection_name.c_str());
- err = child_sender.SendMessage(child_message, kTimeoutMs);
- if (err != KERN_SUCCESS) {
- LOG(ERROR) << "child SendMessage() failed: " << MachErrorCode(err);
- return pid;
- }
- break;
- }
- default: { // parent
- MachReceiveMessage child_message;
- err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
- if (err != KERN_SUCCESS) {
- LOG(ERROR) << "parent WaitForMessage() failed: " << MachErrorCode(err);
- return pid;
- }
-
- if (child_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
- LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
- return pid;
- }
- *child_task = child_message.GetTranslatedPort(0);
- break;
- }
- }
- return pid;
-}
-
-bool LaunchApp(const std::vector<std::string>& argv,
- const environment_vector& env_changes,
- const file_handle_mapping_vector& fds_to_remap,
- bool wait, ProcessHandle* process_handle) {
- return LaunchAppAndGetTask(
- argv, env_changes, fds_to_remap, wait, NULL, process_handle);
-}
-#endif // defined(OS_MACOSX)
-
char** AlterEnvironment(const environment_vector& changes,
const char* const* const env) {
unsigned count = 0;
@@ -498,18 +420,11 @@ char** AlterEnvironment(const environment_vector& changes,
return ret;
}
-#if defined(OS_MACOSX)
-bool LaunchAppAndGetTask(
-#else
bool LaunchApp(
-#endif
const std::vector<std::string>& argv,
const environment_vector& env_changes,
const file_handle_mapping_vector& fds_to_remap,
bool wait,
-#if defined(OS_MACOSX)
- task_t* task_handle,
-#endif
ProcessHandle* process_handle) {
pid_t pid;
InjectiveMultimap fd_shuffle1, fd_shuffle2;
@@ -518,20 +433,7 @@ bool LaunchApp(
scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
scoped_array<char*> new_environ(AlterEnvironment(env_changes, environ));
-#if defined(OS_MACOSX)
- if (task_handle == NULL) {
- pid = fork();
- } else {
- // On OS X, the task_t for a process is needed for several reasons. Sadly,
- // the function task_for_pid() requires privileges a normal user doesn't
- // have. Instead, a short-lived Mach IPC connection is opened between parent
- // and child, and the child sends its task_t to the parent at fork time.
- *task_handle = MACH_PORT_NULL;
- pid = fork_and_get_task(task_handle);
- }
-#else
pid = fork();
-#endif
if (pid < 0)
return false;
@@ -541,10 +443,7 @@ bool LaunchApp(
RestoreDefaultExceptionHandler();
#endif
- // On mac, the signal handlers are reset in |fork_and_get_task()|.
-#if !defined(OS_MACOSX)
ResetChildSignalHandlersToDefaults();
-#endif
#if 0
// When debugging it can be helpful to check that we really aren't making
diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc
index 68e8048..70dacf7 100644
--- a/chrome/app/chrome_dll_main.cc
+++ b/chrome/app/chrome_dll_main.cc
@@ -80,8 +80,10 @@
#if defined(OS_MACOSX)
#include "app/l10n_util_mac.h"
#include "base/mac_util.h"
-#include "chrome/common/chrome_paths_internal.h"
+#include "base/mach_ipc_mac.h"
#include "chrome/app/breakpad_mac.h"
+#include "chrome/browser/mach_broker_mac.h"
+#include "chrome/common/chrome_paths_internal.h"
#include "grit/chromium_strings.h"
#include "third_party/WebKit/WebKit/mac/WebCoreSupport/WebSystemInterface.h"
#endif
@@ -409,6 +411,29 @@ void SetMacProcessName(const std::string& process_type) {
mac_util::SetProcessName(reinterpret_cast<CFStringRef>(app_name));
}
}
+
+// Completes the Mach IPC handshake by sending this process' task port to the
+// parent process. The parent is listening on the Mach port given by
+// |GetMachPortName()|. The task port is used by the parent to get CPU/memory
+// stats to display in the task manager.
+void SendTaskPortToParentProcess() {
+ const mach_msg_timeout_t kTimeoutMs = 100;
+ const int32_t kMessageId = 0;
+ std::string mach_port_name = MachBroker::GetMachPortName();
+
+ base::MachSendMessage child_message(kMessageId);
+ if (!child_message.AddDescriptor(mach_task_self())) {
+ LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed.";
+ return;
+ }
+
+ base::MachPortSender child_sender(mach_port_name.c_str());
+ kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs);
+ if (err != KERN_SUCCESS) {
+ LOG(ERROR) << StringPrintf("child SendMessage() failed: 0x%x %s", err,
+ mach_error_string(err));
+ }
+}
#endif // defined(OS_MACOSX)
void InitializeStatsTable(base::ProcessId browser_pid,
@@ -588,6 +613,7 @@ int ChromeMain(int argc, char** argv) {
DCHECK_NE(browser_pid, 0u);
#elif defined(OS_MACOSX)
browser_pid = base::GetCurrentProcId();
+ SendTaskPortToParentProcess();
#elif defined(OS_POSIX)
// On linux, we're in the zygote here; so we need the parent process' id.
browser_pid = base::GetParentProcessId(base::GetCurrentProcId());
diff --git a/chrome/browser/child_process_launcher.cc b/chrome/browser/child_process_launcher.cc
index f24244f..ad464c1 100644
--- a/chrome/browser/child_process_launcher.cc
+++ b/chrome/browser/child_process_launcher.cc
@@ -7,6 +7,7 @@
#include <utility> // For std::pair.
#include "base/command_line.h"
+#include "base/lock.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/thread.h"
@@ -161,20 +162,29 @@ class ChildProcessLauncher::Context
}
#endif // defined(OS_LINUX)
- // Actually launch the app.
- bool launched;
+ bool launched = false;
#if defined(OS_MACOSX)
- task_t child_task;
- launched = base::LaunchAppAndGetTask(
- cmd_line->argv(), env, fds_to_map, false, &child_task, &handle);
- if (launched && child_task != MACH_PORT_NULL) {
- MachBroker::instance()->RegisterPid(
- handle,
- MachBroker::MachInfo().SetTask(child_task));
- }
-#else
- launched = base::LaunchApp(cmd_line->argv(), env, fds_to_map,
- /* wait= */false, &handle);
+ // It is possible for the child process to die immediately after
+ // launching. To prevent leaking MachBroker map entries in this case,
+ // lock around all of LaunchApp(). If the child dies, the death
+ // notification will be processed by the MachBroker after the call to
+ // AddPlaceholderForPid(), enabling proper cleanup.
+ { // begin scope for AutoLock
+ MachBroker* broker = MachBroker::instance();
+ AutoLock lock(broker->GetLock());
+
+ // This call to |PrepareForFork()| will start the MachBroker listener
+ // thread, if it is not already running. Therefore the browser process
+ // will be listening for Mach IPC before LaunchApp() is called.
+ broker->PrepareForFork();
+#endif
+ // Actually launch the app.
+ launched = base::LaunchApp(cmd_line->argv(), env, fds_to_map,
+ /* wait= */false, &handle);
+#if defined(OS_MACOSX)
+ if (launched)
+ broker->AddPlaceholderForPid(handle);
+ } // end scope for AutoLock
#endif
if (!launched)
handle = base::kNullProcessHandle;
diff --git a/chrome/browser/mach_broker_mac.cc b/chrome/browser/mach_broker_mac.cc
index 39f3fce..6c24541 100644
--- a/chrome/browser/mach_broker_mac.cc
+++ b/chrome/browser/mach_broker_mac.cc
@@ -4,13 +4,26 @@
#include "chrome/browser/mach_broker_mac.h"
+#include "base/command_line.h"
#include "base/logging.h"
+#include "base/mach_ipc_mac.h"
+#include "base/platform_thread.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/common/child_process_info.h"
+#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_service.h"
+namespace {
+// Prints a string representation of a Mach error code.
+std::string MachErrorCode(kern_return_t err) {
+ return StringPrintf("0x%x %s", err, mach_error_string(err));
+}
+} // namespace
+
// Required because notifications happen on the UI thread.
class RegisterNotificationTask : public Task {
public:
@@ -38,28 +51,124 @@ class RegisterNotificationTask : public Task {
private:
MachBroker* broker_;
+ DISALLOW_COPY_AND_ASSIGN(RegisterNotificationTask);
};
-MachBroker::MachBroker() {
- ChromeThread::PostTask(
- ChromeThread::UI, FROM_HERE, new RegisterNotificationTask(this));
-}
+class MachListenerThreadDelegate : public PlatformThread::Delegate {
+ public:
+ MachListenerThreadDelegate(MachBroker* broker) : broker_(broker) {
+ DCHECK(broker_);
+ std::string port_name = MachBroker::GetMachPortName();
+
+ // Create the receive port in the constructor, not in ThreadMain(). It is
+ // important to create and register the receive port before starting the
+ // thread so that child processes will always have someone who's listening.
+ receive_port_.reset(new base::ReceivePort(port_name.c_str()));
+ }
+
+ // Implement |PlatformThread::Delegate|.
+ void ThreadMain() {
+ base::MachReceiveMessage message;
+ kern_return_t err;
+ while ((err = receive_port_->WaitForMessage(&message,
+ MACH_MSG_TIMEOUT_NONE)) ==
+ KERN_SUCCESS) {
+ // 0 was the secret message id. Reject any messages that don't have it.
+ if (message.GetMessageID() != 0) {
+ LOG(ERROR) << "Received message with incorrect id: "
+ << message.GetMessageID();
+ continue;
+ }
+
+ const task_t child_task = message.GetTranslatedPort(0);
+ if (child_task == MACH_PORT_NULL) {
+ LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
+ continue;
+ }
+
+ // It is possible for the child process to die after the call to
+ // |pid_for_task()| but before the call to |FinalizePid()|. To prevent
+ // leaking MachBroker map entries in this case, lock around both these
+ // calls. If the child dies, the death notification will be processed
+ // after the call to FinalizePid(), ensuring proper cleanup.
+ AutoLock lock(broker_->GetLock());
+
+ int pid;
+ err = pid_for_task(child_task, &pid);
+ if (err == KERN_SUCCESS) {
+ broker_->FinalizePid(pid,
+ MachBroker::MachInfo().SetTask(child_task));
+ } else {
+ LOG(ERROR) << "Error getting pid for task " << child_task
+ << ": " << MachErrorCode(err);
+ }
+ }
+
+ LOG(ERROR) << "Mach listener thread exiting; "
+ << "parent WaitForMessage() likely failed: "
+ << MachErrorCode(err);
+ }
+
+ private:
+ // The Mach port to listen on. Created on thread startup.
+ scoped_ptr<base::ReceivePort> receive_port_;
+
+ // The MachBroker to use when new child task rights are received. Can be
+ // NULL.
+ MachBroker* broker_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(MachListenerThreadDelegate);
+};
// Returns the global MachBroker.
MachBroker* MachBroker::instance() {
- return Singleton<MachBroker>::get();
+ return Singleton<MachBroker, LeakySingletonTraits<MachBroker> >::get();
}
-// Adds mach info for a given pid.
-void MachBroker::RegisterPid(
- base::ProcessHandle pid, const MachInfo& mach_info) {
- AutoLock lock(lock_);
+MachBroker::MachBroker() : listener_thread_started_(false) {
+}
+
+void MachBroker::PrepareForFork() {
+ if (!listener_thread_started_) {
+ listener_thread_started_ = true;
+
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE, new RegisterNotificationTask(this));
+
+ // Intentional leak. This thread is never joined or reaped.
+ PlatformThread::CreateNonJoinable(0, new MachListenerThreadDelegate(this));
+ }
+}
+
+// Adds a placeholder to the map for the given pid with MACH_PORT_NULL.
+void MachBroker::AddPlaceholderForPid(base::ProcessHandle pid) {
+ lock_.AssertAcquired();
+
+ MachInfo mach_info;
DCHECK_EQ(0u, mach_map_.count(pid));
mach_map_[pid] = mach_info;
}
+// Updates the mapping for |pid| to include the given |mach_info|.
+void MachBroker::FinalizePid(base::ProcessHandle pid,
+ const MachInfo& mach_info) {
+ lock_.AssertAcquired();
+
+ const int count = mach_map_.count(pid);
+ if (count == 0) {
+ // Do nothing for unknown pids.
+ LOG(ERROR) << "Unknown process " << pid << " is sending Mach IPC messages!";
+ return;
+ }
+
+ DCHECK_EQ(1, count);
+ DCHECK(mach_map_[pid].mach_task_ == MACH_PORT_NULL);
+ if (mach_map_[pid].mach_task_ == MACH_PORT_NULL)
+ mach_map_[pid] = mach_info;
+}
+
// Removes all mappings belonging to |pid| from the broker.
-void MachBroker::Invalidate(base::ProcessHandle pid) {
+void MachBroker::InvalidatePid(base::ProcessHandle pid) {
AutoLock lock(lock_);
MachBroker::MachMap::iterator it = mach_map_.find(pid);
if (it == mach_map_.end())
@@ -69,10 +178,14 @@ void MachBroker::Invalidate(base::ProcessHandle pid) {
it->second.mach_task_);
LOG_IF(WARNING, kr != KERN_SUCCESS)
<< "Failed to mach_port_deallocate mach task " << it->second.mach_task_
- << ", error " << kr;
+ << ", error " << MachErrorCode(kr);
mach_map_.erase(it);
}
+Lock& MachBroker::GetLock() {
+ return lock_;
+}
+
// Returns the mach task belonging to |pid|.
mach_port_t MachBroker::TaskForPid(base::ProcessHandle pid) const {
AutoLock lock(lock_);
@@ -85,6 +198,9 @@ mach_port_t MachBroker::TaskForPid(base::ProcessHandle pid) const {
void MachBroker::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
+ // TODO(rohitrao): These notifications do not always carry the proper PIDs,
+ // especially when the renderer is already gone or has crashed. Find a better
+ // way to listen for child process deaths. http://crbug.com/55734
base::ProcessHandle handle = 0;
switch (type.value) {
case NotificationType::RENDERER_PROCESS_CLOSED:
@@ -103,5 +219,23 @@ void MachBroker::Observe(NotificationType type,
NOTREACHED() << "Unexpected notification";
break;
}
- Invalidate(handle);
+ InvalidatePid(handle);
+}
+
+// static
+std::string MachBroker::GetMachPortName() {
+ static const char kFormatString[] =
+#if defined(GOOGLE_CHROME_BUILD)
+ "com.google.Chrome"
+#else
+ "org.chromium.Chromium"
+#endif
+ ".rohitfork.%d";
+
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ const bool is_child = command_line.HasSwitch(switches::kProcessType);
+
+ // In non-browser (child) processes, use the parent's pid.
+ const pid_t pid = is_child ? getppid() : getpid();
+ return StringPrintf(kFormatString, pid);
}
diff --git a/chrome/browser/mach_broker_mac.h b/chrome/browser/mach_broker_mac.h
index 58ef3e1..7b0257f 100644
--- a/chrome/browser/mach_broker_mac.h
+++ b/chrome/browser/mach_broker_mac.h
@@ -7,6 +7,7 @@
#pragma once
#include <map>
+#include <string>
#include <mach/mach.h>
@@ -37,6 +38,10 @@ class MachBroker : public base::ProcessMetrics::PortProvider,
// Returns the global MachBroker.
static MachBroker* instance();
+ // Performs any necessary setup that cannot happen in the constructor.
+ // Clients MUST call this method before fork()ing any children.
+ void PrepareForFork();
+
struct MachInfo {
MachInfo() : mach_task_(MACH_PORT_NULL) {}
@@ -48,11 +53,28 @@ class MachBroker : public base::ProcessMetrics::PortProvider,
mach_port_t mach_task_;
};
- // Adds mach info for a given pid.
- void RegisterPid(base::ProcessHandle pid, const MachInfo& mach_info);
+ // Adds a placeholder to the map for the given pid with MACH_PORT_NULL.
+ // Callers are expected to later update the port with FinalizePid(). Callers
+ // MUST acquire the lock given by GetLock() before calling this method (and
+ // release the lock afterwards).
+ void AddPlaceholderForPid(base::ProcessHandle pid);
+
+ // Updates the mapping for |pid| to include the given |mach_info|. Does
+ // nothing if PlaceholderForPid() has not already been called for the given
+ // |pid|. Callers MUST acquire the lock given by GetLock() before calling
+ // this method (and release the lock afterwards).
+ void FinalizePid(base::ProcessHandle pid, const MachInfo& mach_info);
// Removes all mappings belonging to |pid| from the broker.
- void Invalidate(base::ProcessHandle pid);
+ void InvalidatePid(base::ProcessHandle pid);
+
+ // The lock that protects this MachBroker object. Clients MUST acquire and
+ // release this lock around calls to PlaceholderForPid() and FinalizePid().
+ Lock& GetLock();
+
+ // Returns the Mach port name to use when sending or receiving messages.
+ // Does the Right Thing in the browser and in child processes.
+ static std::string GetMachPortName();
// Implement |ProcessMetrics::PortProvider|.
virtual mach_port_t TaskForPid(base::ProcessHandle process) const;
@@ -65,13 +87,13 @@ class MachBroker : public base::ProcessMetrics::PortProvider,
// Private constructor.
MachBroker();
+ // True if the listener thread has been started.
+ bool listener_thread_started_;
+
// Used to register for notifications received by NotificationObserver.
// Accessed only on the UI thread.
NotificationRegistrar registrar_;
- friend struct DefaultSingletonTraits<MachBroker>;
- friend class MachBrokerTest;
-
// Stores mach info for every process in the broker.
typedef std::map<base::ProcessHandle, MachInfo> MachMap;
MachMap mach_map_;
@@ -79,7 +101,10 @@ class MachBroker : public base::ProcessMetrics::PortProvider,
// Mutex that guards |mach_map_|.
mutable Lock lock_;
+ friend class MachBrokerTest;
friend class RegisterNotificationTask;
+ // Needed in order to make the constructor private.
+ friend struct DefaultSingletonTraits<MachBroker>;
DISALLOW_COPY_AND_ASSIGN(MachBroker);
};
diff --git a/chrome/browser/mach_broker_mac_unittest.cc b/chrome/browser/mach_broker_mac_unittest.cc
index 56cc2ae2..b0e5b05 100644
--- a/chrome/browser/mach_broker_mac_unittest.cc
+++ b/chrome/browser/mach_broker_mac_unittest.cc
@@ -4,21 +4,57 @@
#include "chrome/browser/mach_broker_mac.h"
+#include "base/lock.h"
#include "testing/gtest/include/gtest/gtest.h"
class MachBrokerTest : public testing::Test {
public:
+ // Helper function to acquire/release locks and call |PlaceholderForPid()|.
+ void AddPlaceholderForPid(base::ProcessHandle pid) {
+ AutoLock lock(broker_.GetLock());
+ broker_.AddPlaceholderForPid(pid);
+ }
+
+ // Helper function to acquire/release locks and call |FinalizePid()|.
+ void FinalizePid(base::ProcessHandle pid,
+ const MachBroker::MachInfo& mach_info) {
+ AutoLock lock(broker_.GetLock());
+ broker_.FinalizePid(pid, mach_info);
+ }
+
+ protected:
MachBroker broker_;
};
-TEST_F(MachBrokerTest, Setter) {
- broker_.RegisterPid(1u, MachBroker::MachInfo().SetTask(2u));
- EXPECT_EQ(2u, broker_.TaskForPid(1));
+TEST_F(MachBrokerTest, Locks) {
+ // Acquire and release the locks. Nothing bad should happen.
+ AutoLock lock(broker_.GetLock());
+}
+
+TEST_F(MachBrokerTest, AddPlaceholderAndFinalize) {
+ // Add a placeholder for PID 1.
+ AddPlaceholderForPid(1);
+ EXPECT_EQ(0u, broker_.TaskForPid(1));
+
+ // Finalize PID 1.
+ FinalizePid(1, MachBroker::MachInfo().SetTask(100u));
+ EXPECT_EQ(100u, broker_.TaskForPid(1));
+
+ // Should be no entry for PID 2.
EXPECT_EQ(0u, broker_.TaskForPid(2));
}
TEST_F(MachBrokerTest, Invalidate) {
- broker_.RegisterPid(1u, MachBroker::MachInfo().SetTask(2));
- broker_.Invalidate(1u);
+ AddPlaceholderForPid(1);
+ FinalizePid(1, MachBroker::MachInfo().SetTask(100u));
+
+ EXPECT_EQ(100u, broker_.TaskForPid(1));
+ broker_.InvalidatePid(1u);
EXPECT_EQ(0u, broker_.TaskForPid(1));
}
+
+TEST_F(MachBrokerTest, FinalizeUnknownPid) {
+ // Finalizing an entry for an unknown pid should not add it to the map.
+ FinalizePid(1u, MachBroker::MachInfo().SetTask(100u));
+ EXPECT_EQ(0u, broker_.TaskForPid(1u));
+}