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