OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/multi_process_notification.h" |
| 6 |
| 7 #import <Foundation/Foundation.h> |
| 8 #include <notify.h> |
| 9 #include <sys/select.h> |
| 10 #include <sys/socket.h> |
| 11 #include <sys/types.h> |
| 12 #include <unistd.h> |
| 13 |
| 14 #include <algorithm> |
| 15 |
| 16 #include "base/basictypes.h" |
| 17 #include "base/eintr_wrapper.h" |
| 18 #include "base/file_path.h" |
| 19 #include "base/logging.h" |
| 20 #include "base/mac/mac_util.h" |
| 21 #include "base/mac/scoped_nsautorelease_pool.h" |
| 22 #include "base/message_loop_proxy.h" |
| 23 #include "base/message_pump_libevent.h" |
| 24 #include "base/path_service.h" |
| 25 #include "base/ref_counted.h" |
| 26 #include "base/stringprintf.h" |
| 27 #include "base/synchronization/lock.h" |
| 28 #include "base/sys_string_conversions.h" |
| 29 #include "base/sys_info.h" |
| 30 #include "base/threading/simple_thread.h" |
| 31 #include "chrome/browser/browser_thread.h" |
| 32 #include "chrome/common/chrome_paths.h" |
| 33 |
| 34 // Enable this to build with leopard_switchboard_thread |
| 35 // #define USE_LEOPARD_SWITCHBOARD_THREAD 1 |
| 36 |
| 37 namespace { |
| 38 |
| 39 std::string AddPrefixToNotification(const std::string& name, |
| 40 multi_process_notification::Domain domain) { |
| 41 // The ordering of the components in the string returned by this function |
| 42 // is important. Read "NAMESPACE CONVENTIONS" in 'man 3 notify' for details. |
| 43 base::mac::ScopedNSAutoreleasePool pool; |
| 44 NSBundle* bundle = base::mac::MainAppBundle(); |
| 45 NSString* ns_bundle_id = [bundle bundleIdentifier]; |
| 46 std::string bundle_id = base::SysNSStringToUTF8(ns_bundle_id); |
| 47 std::string domain_string; |
| 48 switch (domain) { |
| 49 case multi_process_notification::ProfileDomain: { |
| 50 FilePath user_data_dir; |
| 51 if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) { |
| 52 NOTREACHED(); |
| 53 } |
| 54 domain_string = StringPrintf("user.uid.%u.%s.", |
| 55 getuid(), user_data_dir.value().c_str()); |
| 56 break; |
| 57 } |
| 58 |
| 59 case multi_process_notification::UserDomain: |
| 60 domain_string = StringPrintf("user.uid.%u.", getuid()); |
| 61 break; |
| 62 |
| 63 case multi_process_notification::SystemDomain: |
| 64 break; |
| 65 } |
| 66 return domain_string + bundle_id + "." + name; |
| 67 } |
| 68 |
| 69 bool UseLeopardSwitchboardThread() { |
| 70 #if USE_LEOPARD_SWITCHBOARD_THREAD |
| 71 return true; |
| 72 #endif // USE_LEOPARD_SWITCHBOARD_THREAD |
| 73 int32 major_version, minor_version, bugfix_version; |
| 74 base::SysInfo::OperatingSystemVersionNumbers( |
| 75 &major_version, &minor_version, &bugfix_version); |
| 76 return major_version < 10 || (major_version == 10 && minor_version <= 5); |
| 77 } |
| 78 |
| 79 } // namespace |
| 80 |
| 81 namespace multi_process_notification { |
| 82 |
| 83 bool Post(const std::string& name, Domain domain) { |
| 84 std::string notification = AddPrefixToNotification(name, domain); |
| 85 uint32_t status = notify_post(notification.c_str()); |
| 86 DCHECK_EQ(status, static_cast<uint32_t>(NOTIFY_STATUS_OK)); |
| 87 return status == NOTIFY_STATUS_OK; |
| 88 } |
| 89 |
| 90 #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 |
| 91 #error LeopardSwitchboardThread can be removed |
| 92 #endif // MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 |
| 93 |
| 94 // LeopardSwitchboardThread exists because the file descriptors returned by |
| 95 // notify_register_file_descriptor can't be monitored using kqueues on 10.5 |
| 96 // ( http://openradar.appspot.com/8854692 ) and libevent uses kqueue to watch |
| 97 // file descriptors in IOMessageLoop. |
| 98 // This solution is to have a separate thread that monitors the file descriptor |
| 99 // returned by notify_register_file_descriptor using select, and then to |
| 100 // notify the MessageLoopForIO using a different file descriptor allocated by |
| 101 // socketpair that can be monitored using kqueues in libevent. This thread |
| 102 // only runs on 10.5, as 10.6 kqueues can monitor the notify file descriptors |
| 103 // without any problems. |
| 104 |
| 105 // LeopardSwitchboardThread creates three file descriptors: |
| 106 // internal_fd_: which communicates from the switchboard thread to other threads |
| 107 // external_fd_: which communicates from other threads to the switchboard thread |
| 108 // notify_fd_: which is the file descriptor returned from |
| 109 // notify_register_file_descriptor |
| 110 // |
| 111 // The thread itself sits in a select loop waiting on internal_fd_, and |
| 112 // notify_fd_ for input. If it gets ANY input on internal_fd_ it exits. |
| 113 // If it gets input on notify_fd_ it sends the input through to external_fd_. |
| 114 // External_fd_ is monitored by MessageLoopForIO so that the lookup of any |
| 115 // matching listeners in entries_, and the triggering of those listeners, |
| 116 // occurs in the MessageLoopForIO thread. |
| 117 // |
| 118 // Lookups are linear right now, and could be optimized if they ever become |
| 119 // a performance issue. |
| 120 class LeopardSwitchboardThread |
| 121 : public base::MessagePumpLibevent::Watcher, |
| 122 public base::SimpleThread, |
| 123 public MessageLoop::DestructionObserver { |
| 124 public: |
| 125 LeopardSwitchboardThread(); |
| 126 virtual ~LeopardSwitchboardThread(); |
| 127 |
| 128 bool Init(); |
| 129 |
| 130 bool AddListener(ListenerImpl* listener, |
| 131 const std::string& notification); |
| 132 bool RemoveListener(ListenerImpl* listener, const std::string& notification); |
| 133 |
| 134 bool finished() const { return finished_; } |
| 135 |
| 136 // SimpleThread overrides |
| 137 virtual void Run(); |
| 138 |
| 139 // Watcher overrides |
| 140 virtual void OnFileCanReadWithoutBlocking(int fd); |
| 141 virtual void OnFileCanWriteWithoutBlocking(int fd); |
| 142 |
| 143 // DestructionObserver overrides |
| 144 virtual void WillDestroyCurrentMessageLoop(); |
| 145 |
| 146 private: |
| 147 // Used to match tokens to notifications and vice-versa. |
| 148 struct SwitchboardEntry { |
| 149 int token_; |
| 150 std::string notification_; |
| 151 ListenerImpl* listener_; |
| 152 }; |
| 153 |
| 154 enum { |
| 155 kKillThreadMessage = 0xdecea5e |
| 156 }; |
| 157 |
| 158 int internal_fd_; |
| 159 int external_fd_; |
| 160 int notify_fd_; |
| 161 int notify_fd_token_; |
| 162 mutable bool finished_; |
| 163 fd_set fd_set_; |
| 164 |
| 165 // all accesses to entries_ must be controlled by entries_lock_. |
| 166 std::vector<SwitchboardEntry> entries_; |
| 167 Lock entries_lock_; |
| 168 base::MessagePumpLibevent::FileDescriptorWatcher watcher_; |
| 169 }; |
| 170 |
| 171 class ListenerImpl : public base::MessagePumpLibevent::Watcher { |
| 172 public: |
| 173 ListenerImpl(const std::string& name, |
| 174 Domain domain, |
| 175 Listener::Delegate* delegate); |
| 176 virtual ~ListenerImpl(); |
| 177 |
| 178 bool Start(); |
| 179 void OnListen(); |
| 180 |
| 181 // Watcher overrides |
| 182 virtual void OnFileCanReadWithoutBlocking(int fd); |
| 183 virtual void OnFileCanWriteWithoutBlocking(int fd); |
| 184 |
| 185 private: |
| 186 std::string name_; |
| 187 Domain domain_; |
| 188 Listener::Delegate* delegate_; |
| 189 int fd_; |
| 190 int token_; |
| 191 Lock switchboard_lock_; |
| 192 static LeopardSwitchboardThread* g_switchboard_thread_; |
| 193 base::MessagePumpLibevent::FileDescriptorWatcher watcher_; |
| 194 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; |
| 195 |
| 196 void StartLeopard(); |
| 197 void StartSnowLeopard(); |
| 198 DISALLOW_COPY_AND_ASSIGN(ListenerImpl); |
| 199 }; |
| 200 |
| 201 |
| 202 LeopardSwitchboardThread::LeopardSwitchboardThread() |
| 203 : base::SimpleThread("LeopardSwitchboardThread"), internal_fd_(-1), |
| 204 external_fd_(-1), notify_fd_(-1), notify_fd_token_(-1), finished_(false) { |
| 205 } |
| 206 |
| 207 LeopardSwitchboardThread::~LeopardSwitchboardThread() { |
| 208 if (internal_fd_ != -1) { |
| 209 close(internal_fd_); |
| 210 } |
| 211 if (external_fd_ != -1) { |
| 212 close(external_fd_); |
| 213 } |
| 214 if (notify_fd_ != -1) { |
| 215 // Cancelling this notification takes care of closing notify_fd_. |
| 216 uint32_t status = notify_cancel(notify_fd_token_); |
| 217 DCHECK_EQ(status, static_cast<uint32_t>(NOTIFY_STATUS_OK)); |
| 218 } |
| 219 } |
| 220 |
| 221 bool LeopardSwitchboardThread::Init() { |
| 222 // Create a pair of sockets for communicating with the thread |
| 223 // The file descriptors returned from socketpair can be kqueue'd on 10.5. |
| 224 int sockets[2]; |
| 225 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) { |
| 226 PLOG(ERROR) << "socketpair"; |
| 227 return false; |
| 228 } |
| 229 internal_fd_ = sockets[0]; |
| 230 external_fd_ = sockets[1]; |
| 231 |
| 232 // Register a bogus notification so that there is single notify_fd_ to |
| 233 // monitor. This runs a small risk of overflowing the notification buffer |
| 234 // if notifications are used heavily (see man 3 notify), however it greatly |
| 235 // simplifies the select loop code as there are only 2 file descriptors |
| 236 // that need to be monitored, and there is no need to add/remove file |
| 237 // descriptors from fd_set_ as listeners are added and removed. |
| 238 // This also keep the total fd usage on 10.5 to three for all |
| 239 // notifications. The 10.6 implementation will use one fd per notification, |
| 240 // but doesn't run the risk of notification buffer overflow. If fds ever |
| 241 // become tight, the 10.6 code could be changed to use only one fd for |
| 242 // all notifications. |
| 243 std::string notification = StringPrintf("LeopardSwitchboardThread.%d", |
| 244 getpid()); |
| 245 notification = AddPrefixToNotification(notification, ProfileDomain); |
| 246 uint32_t status = notify_register_file_descriptor( |
| 247 notification.c_str(), ¬ify_fd_, 0, ¬ify_fd_token_); |
| 248 if (status != NOTIFY_STATUS_OK) { |
| 249 return false; |
| 250 } |
| 251 |
| 252 FD_ZERO(&fd_set_); |
| 253 FD_SET(internal_fd_, &fd_set_); |
| 254 FD_SET(notify_fd_, &fd_set_); |
| 255 |
| 256 MessageLoopForIO* io_loop = MessageLoopForIO::current(); |
| 257 // Watch for destruction of the BrowserThread::IO message loop so that |
| 258 // the thread can be stopped cleanly. |
| 259 io_loop->AddDestructionObserver(this); |
| 260 return io_loop->WatchFileDescriptor( |
| 261 external_fd_, true, MessageLoopForIO::WATCH_READ, &watcher_, this); |
| 262 } |
| 263 |
| 264 void LeopardSwitchboardThread::WillDestroyCurrentMessageLoop() { |
| 265 DCHECK_EQ(MessageLoop::current(), MessageLoopForIO::current()); |
| 266 watcher_.StopWatchingFileDescriptor(); |
| 267 |
| 268 // Send the appropriate message to end our thread, and then wait for it |
| 269 // to finish before continuing. |
| 270 int message = kKillThreadMessage; |
| 271 write(external_fd_, &message, sizeof(message)); |
| 272 Join(); |
| 273 } |
| 274 |
| 275 void LeopardSwitchboardThread::Run() { |
| 276 DCHECK(!finished_); |
| 277 int nfds = std::max(internal_fd_, notify_fd_) + 1; |
| 278 while (1) { |
| 279 fd_set working_set; |
| 280 FD_COPY(&fd_set_, &working_set); |
| 281 int count = HANDLE_EINTR(select(nfds, &working_set, NULL, NULL, NULL)); |
| 282 if (count < 0) { |
| 283 PLOG(ERROR) << "select"; |
| 284 break; |
| 285 } else if (count == 0) { |
| 286 DLOG(INFO) << "select timed out"; |
| 287 continue; |
| 288 } |
| 289 if (FD_ISSET(notify_fd_, &working_set)) { |
| 290 int token; |
| 291 int status = HANDLE_EINTR(read(notify_fd_, &token, sizeof(token))); |
| 292 if (status < 0) { |
| 293 PLOG(ERROR) << "read"; |
| 294 break; |
| 295 } else if (status == 0) { |
| 296 LOG(ERROR) << "notify fd closed"; |
| 297 break; |
| 298 } else if (status != sizeof(token)) { |
| 299 LOG(ERROR) << "read wrong size: " << status; |
| 300 break; |
| 301 } else if (token == notify_fd_token_) { |
| 302 LOG(ERROR) << "invalid token: " << token; |
| 303 } |
| 304 status = HANDLE_EINTR(write(internal_fd_, &token, sizeof(token))); |
| 305 if (status < 0) { |
| 306 PLOG(ERROR) << "write"; |
| 307 break; |
| 308 } else if (status == 0) { |
| 309 LOG(ERROR) << "external_fd_ closed"; |
| 310 break; |
| 311 } else if (status != sizeof(token)) { |
| 312 LOG(ERROR) << "write wrong size: " << status; |
| 313 break; |
| 314 } |
| 315 } |
| 316 if (FD_ISSET(internal_fd_, &working_set)) { |
| 317 int value; |
| 318 int status = HANDLE_EINTR(read(internal_fd_, &value, sizeof(value))); |
| 319 if (status < 0) { |
| 320 PLOG(ERROR) << "read"; |
| 321 } else if (status == 0) { |
| 322 LOG(ERROR) << "internal_fd_ closed"; |
| 323 } else if (value != kKillThreadMessage) { |
| 324 LOG(ERROR) << "unknown message sent: " << value; |
| 325 } |
| 326 break; |
| 327 } |
| 328 } |
| 329 finished_ = true; |
| 330 } |
| 331 |
| 332 bool LeopardSwitchboardThread::AddListener(ListenerImpl* listener, |
| 333 const std::string& notification) { |
| 334 DCHECK(!finished()); |
| 335 base::AutoLock autolock(entries_lock_); |
| 336 for (std::vector<SwitchboardEntry>::iterator i = entries_.begin(); |
| 337 i < entries_.end(); ++i) { |
| 338 if (i->listener_ == listener && i->notification_ == notification) { |
| 339 LOG(ERROR) << "listener " << listener |
| 340 << " already registered for '" << notification << "'"; |
| 341 return false; |
| 342 } |
| 343 } |
| 344 int token; |
| 345 uint32_t status = notify_register_file_descriptor( |
| 346 notification.c_str(), ¬ify_fd_, NOTIFY_REUSE, &token); |
| 347 if (status != NOTIFY_STATUS_OK) { |
| 348 LOG(ERROR) << "unable to notify_register_file_descriptor for '" |
| 349 << notification << "' status: " << status; |
| 350 return false; |
| 351 } |
| 352 SwitchboardEntry entry; |
| 353 entry.token_ = token; |
| 354 entry.notification_ = notification; |
| 355 entry.listener_ = listener; |
| 356 entries_.push_back(entry); |
| 357 return true; |
| 358 } |
| 359 |
| 360 bool LeopardSwitchboardThread::RemoveListener(ListenerImpl* listener, |
| 361 const std::string& notification) { |
| 362 DCHECK(!finished()); |
| 363 base::AutoLock autolock(entries_lock_); |
| 364 for (std::vector<SwitchboardEntry>::iterator i = entries_.begin(); |
| 365 i < entries_.end(); ++i) { |
| 366 if (i->listener_ == listener && i->notification_ == notification) { |
| 367 uint32_t status = notify_cancel(i->token_); |
| 368 DCHECK_EQ(status, static_cast<uint32_t>(NOTIFY_STATUS_OK)); |
| 369 entries_.erase(i); |
| 370 return true; |
| 371 } |
| 372 } |
| 373 LOG(ERROR) << "unable to remove listener '" << listener |
| 374 << "' for '" << notification << "'."; |
| 375 return false; |
| 376 } |
| 377 |
| 378 void LeopardSwitchboardThread::OnFileCanReadWithoutBlocking(int fd) { |
| 379 DCHECK_EQ(MessageLoop::current(), MessageLoopForIO::current()); |
| 380 DCHECK_EQ(fd, external_fd_); |
| 381 int token; |
| 382 int status = HANDLE_EINTR(read(fd, &token, sizeof(token))); |
| 383 if (status < 0) { |
| 384 PLOG(ERROR) << "read"; |
| 385 } else if (status == 0) { |
| 386 LOG(ERROR) << "external_fd_ closed"; |
| 387 } else if (status != sizeof(token)) { |
| 388 LOG(ERROR) << "unexpected read size " << status; |
| 389 } else { |
| 390 // Have to swap to native endianness <http://openradar.appspot.com/8821081>. |
| 391 token = static_cast<int>(ntohl(token)); |
| 392 base::AutoLock autolock(entries_lock_); |
| 393 bool found_token = false; |
| 394 for (std::vector<SwitchboardEntry>::iterator i = entries_.begin(); |
| 395 i < entries_.end(); ++i) { |
| 396 if (i->token_ == token) { |
| 397 found_token = true; |
| 398 i->listener_->OnListen(); |
| 399 } |
| 400 } |
| 401 if (!found_token) { |
| 402 LOG(ERROR) << "read unknown token " << token; |
| 403 } |
| 404 } |
| 405 } |
| 406 |
| 407 void LeopardSwitchboardThread::OnFileCanWriteWithoutBlocking(int fd) { |
| 408 NOTREACHED(); |
| 409 } |
| 410 |
| 411 LeopardSwitchboardThread* ListenerImpl::g_switchboard_thread_ = NULL; |
| 412 |
| 413 ListenerImpl::ListenerImpl( |
| 414 const std::string& name, Domain domain, Listener::Delegate* delegate) |
| 415 : name_(name), domain_(domain), delegate_(delegate), fd_(-1), token_(-1) { |
| 416 } |
| 417 |
| 418 ListenerImpl::~ListenerImpl() { |
| 419 if (!UseLeopardSwitchboardThread()) { |
| 420 if (fd_ != -1) { |
| 421 uint32_t status = notify_cancel(token_); |
| 422 DCHECK_EQ(status, static_cast<uint32_t>(NOTIFY_STATUS_OK)); |
| 423 } |
| 424 } else { |
| 425 base::AutoLock autolock(switchboard_lock_); |
| 426 if (g_switchboard_thread_) { |
| 427 std::string notification = AddPrefixToNotification(name_, domain_); |
| 428 CHECK(g_switchboard_thread_->RemoveListener(this, notification)); |
| 429 } |
| 430 } |
| 431 } |
| 432 |
| 433 bool ListenerImpl::Start() { |
| 434 DCHECK_EQ(fd_, -1); |
| 435 DCHECK_EQ(token_, -1); |
| 436 message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread(); |
| 437 Task* task; |
| 438 if(UseLeopardSwitchboardThread()) { |
| 439 task = NewRunnableMethod(this, &ListenerImpl::StartLeopard); |
| 440 } else { |
| 441 task = NewRunnableMethod(this, &ListenerImpl::StartSnowLeopard); |
| 442 } |
| 443 return BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, task); |
| 444 } |
| 445 |
| 446 void ListenerImpl::StartLeopard() { |
| 447 DCHECK(UseLeopardSwitchboardThread()); |
| 448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 449 bool success = true; |
| 450 { |
| 451 base::AutoLock autolock(switchboard_lock_); |
| 452 if (g_switchboard_thread_ && g_switchboard_thread_->HasBeenJoined()) { |
| 453 // The only time this should ever occur is in unit tests. |
| 454 delete g_switchboard_thread_; |
| 455 g_switchboard_thread_ = NULL; |
| 456 } |
| 457 DCHECK(!(g_switchboard_thread_ && g_switchboard_thread_->finished())); |
| 458 if (!g_switchboard_thread_) { |
| 459 g_switchboard_thread_ = new LeopardSwitchboardThread(); |
| 460 success = g_switchboard_thread_->Init(); |
| 461 if (success) { |
| 462 g_switchboard_thread_->Start(); |
| 463 } |
| 464 } |
| 465 if (success) { |
| 466 std::string notification = AddPrefixToNotification(name_, domain_); |
| 467 success = g_switchboard_thread_->AddListener(this, notification); |
| 468 } |
| 469 } |
| 470 Task* task = |
| 471 new Listener::ListenerStartedTask(name_, domain_, delegate_, success); |
| 472 CHECK(message_loop_proxy_->PostTask(FROM_HERE, task)); |
| 473 } |
| 474 |
| 475 void ListenerImpl::StartSnowLeopard() { |
| 476 DCHECK(!UseLeopardSwitchboardThread()); |
| 477 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 478 bool success = true; |
| 479 std::string notification = AddPrefixToNotification(name_, domain_); |
| 480 uint32_t status = notify_register_file_descriptor( |
| 481 notification.c_str(), &fd_, 0, &token_); |
| 482 if (status != NOTIFY_STATUS_OK) { |
| 483 LOG(ERROR) << "unable to notify_register_file_descriptor for '" |
| 484 << notification << "' Status: " << status; |
| 485 success = false; |
| 486 } |
| 487 if (success) { |
| 488 MessageLoopForIO* io_loop = MessageLoopForIO::current(); |
| 489 success = io_loop->WatchFileDescriptor( |
| 490 fd_, true, MessageLoopForIO::WATCH_READ, &watcher_, this); |
| 491 } |
| 492 Task* task = |
| 493 new Listener::ListenerStartedTask(name_, domain_, delegate_, success); |
| 494 CHECK(message_loop_proxy_->PostTask(FROM_HERE, task)); |
| 495 } |
| 496 |
| 497 void ListenerImpl::OnFileCanReadWithoutBlocking(int fd) { |
| 498 DCHECK(!UseLeopardSwitchboardThread()); |
| 499 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 500 DCHECK_EQ(fd, fd_); |
| 501 int token; |
| 502 int status = HANDLE_EINTR(read(fd, &token, sizeof(token))); |
| 503 if (status < 0) { |
| 504 PLOG(ERROR) << "read"; |
| 505 } else if (status == 0) { |
| 506 LOG(ERROR) << "external_fd_ closed"; |
| 507 } else if (status != sizeof(token)) { |
| 508 LOG(ERROR) << "unexpected read size " << status; |
| 509 } else { |
| 510 // Have to swap to native endianness <http://openradar.appspot.com/8821081>. |
| 511 token = static_cast<int>(ntohl(token)); |
| 512 if (token == token_) { |
| 513 OnListen(); |
| 514 } else { |
| 515 LOG(ERROR) << "unexpected value " << token; |
| 516 } |
| 517 } |
| 518 } |
| 519 |
| 520 void ListenerImpl::OnListen() { |
| 521 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 522 Task* task = |
| 523 new Listener::NotificationReceivedTask(name_, domain_, delegate_); |
| 524 CHECK(message_loop_proxy_->PostTask(FROM_HERE, task)); |
| 525 } |
| 526 |
| 527 void ListenerImpl::OnFileCanWriteWithoutBlocking(int fd) { |
| 528 NOTREACHED(); |
| 529 } |
| 530 |
| 531 Listener::Listener( |
| 532 const std::string& name, Domain domain, Listener::Delegate* delegate) |
| 533 : impl_(new ListenerImpl(name, domain, delegate)) { |
| 534 } |
| 535 |
| 536 Listener::~Listener() { |
| 537 } |
| 538 |
| 539 bool Listener::Start() { |
| 540 return impl_->Start(); |
| 541 } |
| 542 |
| 543 } // namespace multi_process_notification |
| 544 |
| 545 DISABLE_RUNNABLE_METHOD_REFCOUNT(multi_process_notification::ListenerImpl); |
OLD | NEW |