Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Side by Side Diff: chrome/browser/mach_broker_mac.cc

Issue 3443002: [Mac] Replace the existing browser-child mach ipc with a long-lived listener ... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 10 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/mach_broker_mac.h ('k') | chrome/browser/mach_broker_mac_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/mach_broker_mac.h" 5 #include "chrome/browser/mach_broker_mac.h"
6 6
7 #include "base/command_line.h"
7 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/mach_ipc_mac.h"
10 #include "base/platform_thread.h"
11 #include "base/string_util.h"
12 #include "base/sys_string_conversions.h"
8 #include "chrome/browser/chrome_thread.h" 13 #include "chrome/browser/chrome_thread.h"
9 #include "chrome/browser/extensions/extension_host.h" 14 #include "chrome/browser/extensions/extension_host.h"
10 #include "chrome/browser/renderer_host/render_process_host.h" 15 #include "chrome/browser/renderer_host/render_process_host.h"
11 #include "chrome/common/child_process_info.h" 16 #include "chrome/common/child_process_info.h"
17 #include "chrome/common/chrome_switches.h"
12 #include "chrome/common/notification_service.h" 18 #include "chrome/common/notification_service.h"
13 19
20 namespace {
21 // Prints a string representation of a Mach error code.
22 std::string MachErrorCode(kern_return_t err) {
23 return StringPrintf("0x%x %s", err, mach_error_string(err));
24 }
25 } // namespace
26
14 // Required because notifications happen on the UI thread. 27 // Required because notifications happen on the UI thread.
15 class RegisterNotificationTask : public Task { 28 class RegisterNotificationTask : public Task {
16 public: 29 public:
17 RegisterNotificationTask( 30 RegisterNotificationTask(
18 MachBroker* broker) 31 MachBroker* broker)
19 : broker_(broker) { } 32 : broker_(broker) { }
20 33
21 virtual void Run() { 34 virtual void Run() {
22 broker_->registrar_.Add(broker_, 35 broker_->registrar_.Add(broker_,
23 NotificationType::RENDERER_PROCESS_CLOSED, 36 NotificationType::RENDERER_PROCESS_CLOSED,
24 NotificationService::AllSources()); 37 NotificationService::AllSources());
25 broker_->registrar_.Add(broker_, 38 broker_->registrar_.Add(broker_,
26 NotificationType::RENDERER_PROCESS_TERMINATED, 39 NotificationType::RENDERER_PROCESS_TERMINATED,
27 NotificationService::AllSources()); 40 NotificationService::AllSources());
28 broker_->registrar_.Add(broker_, 41 broker_->registrar_.Add(broker_,
29 NotificationType::CHILD_PROCESS_CRASHED, 42 NotificationType::CHILD_PROCESS_CRASHED,
30 NotificationService::AllSources()); 43 NotificationService::AllSources());
31 broker_->registrar_.Add(broker_, 44 broker_->registrar_.Add(broker_,
32 NotificationType::CHILD_PROCESS_HOST_DISCONNECTED, 45 NotificationType::CHILD_PROCESS_HOST_DISCONNECTED,
33 NotificationService::AllSources()); 46 NotificationService::AllSources());
34 broker_->registrar_.Add(broker_, 47 broker_->registrar_.Add(broker_,
35 NotificationType::EXTENSION_PROCESS_TERMINATED, 48 NotificationType::EXTENSION_PROCESS_TERMINATED,
36 NotificationService::AllSources()); 49 NotificationService::AllSources());
37 } 50 }
38 51
39 private: 52 private:
40 MachBroker* broker_; 53 MachBroker* broker_;
54 DISALLOW_COPY_AND_ASSIGN(RegisterNotificationTask);
41 }; 55 };
42 56
43 MachBroker::MachBroker() { 57 class MachListenerThreadDelegate : public PlatformThread::Delegate {
44 ChromeThread::PostTask( 58 public:
45 ChromeThread::UI, FROM_HERE, new RegisterNotificationTask(this)); 59 MachListenerThreadDelegate(MachBroker* broker) : broker_(broker) {
46 } 60 DCHECK(broker_);
61 std::string port_name = MachBroker::GetMachPortName();
62
63 // Create the receive port in the constructor, not in ThreadMain(). It is
64 // important to create and register the receive port before starting the
65 // thread so that child processes will always have someone who's listening.
66 receive_port_.reset(new base::ReceivePort(port_name.c_str()));
67 }
68
69 // Implement |PlatformThread::Delegate|.
70 void ThreadMain() {
71 base::MachReceiveMessage message;
72 kern_return_t err;
73 while ((err = receive_port_->WaitForMessage(&message,
74 MACH_MSG_TIMEOUT_NONE)) ==
75 KERN_SUCCESS) {
76 // 0 was the secret message id. Reject any messages that don't have it.
77 if (message.GetMessageID() != 0) {
78 LOG(ERROR) << "Received message with incorrect id: "
79 << message.GetMessageID();
80 continue;
81 }
82
83 const task_t child_task = message.GetTranslatedPort(0);
84 if (child_task == MACH_PORT_NULL) {
85 LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
86 continue;
87 }
88
89 // It is possible for the child process to die after the call to
90 // |pid_for_task()| but before the call to |FinalizePid()|. To prevent
91 // leaking MachBroker map entries in this case, lock around both these
92 // calls. If the child dies, the death notification will be processed
93 // after the call to FinalizePid(), ensuring proper cleanup.
94 AutoLock lock(broker_->GetLock());
95
96 int pid;
97 err = pid_for_task(child_task, &pid);
98 if (err == KERN_SUCCESS) {
99 broker_->FinalizePid(pid,
100 MachBroker::MachInfo().SetTask(child_task));
101 } else {
102 LOG(ERROR) << "Error getting pid for task " << child_task
103 << ": " << MachErrorCode(err);
104 }
105 }
106
107 LOG(ERROR) << "Mach listener thread exiting; "
108 << "parent WaitForMessage() likely failed: "
109 << MachErrorCode(err);
110 }
111
112 private:
113 // The Mach port to listen on. Created on thread startup.
114 scoped_ptr<base::ReceivePort> receive_port_;
115
116 // The MachBroker to use when new child task rights are received. Can be
117 // NULL.
118 MachBroker* broker_; // weak
119
120 DISALLOW_COPY_AND_ASSIGN(MachListenerThreadDelegate);
121 };
47 122
48 // Returns the global MachBroker. 123 // Returns the global MachBroker.
49 MachBroker* MachBroker::instance() { 124 MachBroker* MachBroker::instance() {
50 return Singleton<MachBroker>::get(); 125 return Singleton<MachBroker, LeakySingletonTraits<MachBroker> >::get();
51 } 126 }
52 127
53 // Adds mach info for a given pid. 128 MachBroker::MachBroker() : listener_thread_started_(false) {
54 void MachBroker::RegisterPid( 129 }
55 base::ProcessHandle pid, const MachInfo& mach_info) { 130
56 AutoLock lock(lock_); 131 void MachBroker::PrepareForFork() {
132 if (!listener_thread_started_) {
133 listener_thread_started_ = true;
134
135 ChromeThread::PostTask(
136 ChromeThread::UI, FROM_HERE, new RegisterNotificationTask(this));
137
138 // Intentional leak. This thread is never joined or reaped.
139 PlatformThread::CreateNonJoinable(0, new MachListenerThreadDelegate(this));
140 }
141 }
142
143 // Adds a placeholder to the map for the given pid with MACH_PORT_NULL.
144 void MachBroker::AddPlaceholderForPid(base::ProcessHandle pid) {
145 lock_.AssertAcquired();
146
147 MachInfo mach_info;
57 DCHECK_EQ(0u, mach_map_.count(pid)); 148 DCHECK_EQ(0u, mach_map_.count(pid));
58 mach_map_[pid] = mach_info; 149 mach_map_[pid] = mach_info;
59 } 150 }
60 151
152 // Updates the mapping for |pid| to include the given |mach_info|.
153 void MachBroker::FinalizePid(base::ProcessHandle pid,
154 const MachInfo& mach_info) {
155 lock_.AssertAcquired();
156
157 const int count = mach_map_.count(pid);
158 if (count == 0) {
159 // Do nothing for unknown pids.
160 LOG(ERROR) << "Unknown process " << pid << " is sending Mach IPC messages!";
161 return;
162 }
163
164 DCHECK_EQ(1, count);
165 DCHECK(mach_map_[pid].mach_task_ == MACH_PORT_NULL);
166 if (mach_map_[pid].mach_task_ == MACH_PORT_NULL)
167 mach_map_[pid] = mach_info;
168 }
169
61 // Removes all mappings belonging to |pid| from the broker. 170 // Removes all mappings belonging to |pid| from the broker.
62 void MachBroker::Invalidate(base::ProcessHandle pid) { 171 void MachBroker::InvalidatePid(base::ProcessHandle pid) {
63 AutoLock lock(lock_); 172 AutoLock lock(lock_);
64 MachBroker::MachMap::iterator it = mach_map_.find(pid); 173 MachBroker::MachMap::iterator it = mach_map_.find(pid);
65 if (it == mach_map_.end()) 174 if (it == mach_map_.end())
66 return; 175 return;
67 176
68 kern_return_t kr = mach_port_deallocate(mach_task_self(), 177 kern_return_t kr = mach_port_deallocate(mach_task_self(),
69 it->second.mach_task_); 178 it->second.mach_task_);
70 LOG_IF(WARNING, kr != KERN_SUCCESS) 179 LOG_IF(WARNING, kr != KERN_SUCCESS)
71 << "Failed to mach_port_deallocate mach task " << it->second.mach_task_ 180 << "Failed to mach_port_deallocate mach task " << it->second.mach_task_
72 << ", error " << kr; 181 << ", error " << MachErrorCode(kr);
73 mach_map_.erase(it); 182 mach_map_.erase(it);
74 } 183 }
75 184
185 Lock& MachBroker::GetLock() {
186 return lock_;
187 }
188
76 // Returns the mach task belonging to |pid|. 189 // Returns the mach task belonging to |pid|.
77 mach_port_t MachBroker::TaskForPid(base::ProcessHandle pid) const { 190 mach_port_t MachBroker::TaskForPid(base::ProcessHandle pid) const {
78 AutoLock lock(lock_); 191 AutoLock lock(lock_);
79 MachBroker::MachMap::const_iterator it = mach_map_.find(pid); 192 MachBroker::MachMap::const_iterator it = mach_map_.find(pid);
80 if (it == mach_map_.end()) 193 if (it == mach_map_.end())
81 return MACH_PORT_NULL; 194 return MACH_PORT_NULL;
82 return it->second.mach_task_; 195 return it->second.mach_task_;
83 } 196 }
84 197
85 void MachBroker::Observe(NotificationType type, 198 void MachBroker::Observe(NotificationType type,
86 const NotificationSource& source, 199 const NotificationSource& source,
87 const NotificationDetails& details) { 200 const NotificationDetails& details) {
201 // TODO(rohitrao): These notifications do not always carry the proper PIDs,
202 // especially when the renderer is already gone or has crashed. Find a better
203 // way to listen for child process deaths. http://crbug.com/55734
88 base::ProcessHandle handle = 0; 204 base::ProcessHandle handle = 0;
89 switch (type.value) { 205 switch (type.value) {
90 case NotificationType::RENDERER_PROCESS_CLOSED: 206 case NotificationType::RENDERER_PROCESS_CLOSED:
91 case NotificationType::RENDERER_PROCESS_TERMINATED: 207 case NotificationType::RENDERER_PROCESS_TERMINATED:
92 handle = Source<RenderProcessHost>(source)->GetHandle(); 208 handle = Source<RenderProcessHost>(source)->GetHandle();
93 break; 209 break;
94 case NotificationType::EXTENSION_PROCESS_TERMINATED: 210 case NotificationType::EXTENSION_PROCESS_TERMINATED:
95 handle = 211 handle =
96 Details<ExtensionHost>(details)->render_process_host()->GetHandle(); 212 Details<ExtensionHost>(details)->render_process_host()->GetHandle();
97 break; 213 break;
98 case NotificationType::CHILD_PROCESS_CRASHED: 214 case NotificationType::CHILD_PROCESS_CRASHED:
99 case NotificationType::CHILD_PROCESS_HOST_DISCONNECTED: 215 case NotificationType::CHILD_PROCESS_HOST_DISCONNECTED:
100 handle = Details<ChildProcessInfo>(details)->handle(); 216 handle = Details<ChildProcessInfo>(details)->handle();
101 break; 217 break;
102 default: 218 default:
103 NOTREACHED() << "Unexpected notification"; 219 NOTREACHED() << "Unexpected notification";
104 break; 220 break;
105 } 221 }
106 Invalidate(handle); 222 InvalidatePid(handle);
107 } 223 }
224
225 // static
226 std::string MachBroker::GetMachPortName() {
227 static const char kFormatString[] =
228 #if defined(GOOGLE_CHROME_BUILD)
229 "com.google.Chrome"
230 #else
231 "org.chromium.Chromium"
232 #endif
233 ".rohitfork.%d";
234
235 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
236 const bool is_child = command_line.HasSwitch(switches::kProcessType);
237
238 // In non-browser (child) processes, use the parent's pid.
239 const pid_t pid = is_child ? getppid() : getpid();
240 return StringPrintf(kFormatString, pid);
241 }
OLDNEW
« no previous file with comments | « chrome/browser/mach_broker_mac.h ('k') | chrome/browser/mach_broker_mac_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698