diff options
Diffstat (limited to 'chrome/browser/process_singleton_linux.cc')
-rw-r--r-- | chrome/browser/process_singleton_linux.cc | 218 |
1 files changed, 154 insertions, 64 deletions
diff --git a/chrome/browser/process_singleton_linux.cc b/chrome/browser/process_singleton_linux.cc index 94a64c6..80149d1 100644 --- a/chrome/browser/process_singleton_linux.cc +++ b/chrome/browser/process_singleton_linux.cc @@ -11,17 +11,23 @@ #include "chrome/browser/process_singleton.h" #include <errno.h> +#include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> +#include <set> #include "base/base_paths.h" +#include "base/basictypes.h" #include "base/command_line.h" #include "base/eintr_wrapper.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/path_service.h" +#include "base/stl_util-inl.h" #include "base/string_util.h" +#include "base/time.h" +#include "base/timer.h" #include "chrome/browser/browser_init.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" @@ -31,10 +37,24 @@ #include "chrome/common/chrome_paths.h" namespace { -const char* kStartToken = "START"; + +const char kStartToken[] = "START"; const char kTokenDelimiter = '\0'; +const int kTimeOutInSeconds = 5; +const int kMaxMessageLength = 32 * 1024; + +// Return 0 on success, -1 on failure. +int SetNonBlocking(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (-1 == flags) + return flags; + if (flags & O_NONBLOCK) + return 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); } +} // namespace + /////////////////////////////////////////////////////////////////////////////// // ProcessSingleton::LinuxWatcher // A helper class for a Linux specific implementation of the process singleton. @@ -45,43 +65,15 @@ class ProcessSingleton::LinuxWatcher public MessageLoop::DestructionObserver, public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher> { public: - class SocketReader : public MessageLoopForIO::Watcher { - public: - SocketReader(ProcessSingleton::LinuxWatcher* parent, - MessageLoop* ui_message_loop) - : parent_(parent), - ui_message_loop_(ui_message_loop) {} - virtual ~SocketReader() {} - - MessageLoopForIO::FileDescriptorWatcher* fd_reader() { - return &fd_reader_; - } - - // MessageLoopForIO::Watcher impl. - virtual void OnFileCanReadWithoutBlocking(int fd); - virtual void OnFileCanWriteWithoutBlocking(int fd) { - // ProcessSingleton only watches for accept (read) events. - NOTREACHED(); - } - - private: - MessageLoopForIO::FileDescriptorWatcher fd_reader_; - - // The ProcessSingleton::LinuxWatcher that owns us. - ProcessSingleton::LinuxWatcher* parent_; - - // A reference to the UI message loop. - MessageLoop* ui_message_loop_; - - DISALLOW_COPY_AND_ASSIGN(SocketReader); - }; - // We expect to only be constructed on the UI thread. explicit LinuxWatcher(ProcessSingleton* parent) : ui_message_loop_(MessageLoop::current()), parent_(parent) { } - virtual ~LinuxWatcher() {} + + virtual ~LinuxWatcher() { + STLDeleteElements(&readers_); + } // Start listening for connections on the socket. This method should be // called from the IO thread. @@ -104,6 +96,67 @@ class ProcessSingleton::LinuxWatcher } private: + class SocketReader : public MessageLoopForIO::Watcher { + public: + SocketReader(ProcessSingleton::LinuxWatcher* parent, + MessageLoop* ui_message_loop, + int fd) + : parent_(parent), + ui_message_loop_(ui_message_loop), + fd_(fd), + bytes_read_(0) { + // Wait for reads. + MessageLoopForIO::current()->WatchFileDescriptor( + fd, true, MessageLoopForIO::WATCH_READ, &fd_reader_, this); + timer_.Start(base::TimeDelta::FromSeconds(kTimeOutInSeconds), + this, &SocketReader::OnTimerExpiry); + } + + virtual ~SocketReader() { + int rv = HANDLE_EINTR(close(fd_)); + DCHECK_EQ(0, rv) << "Error closing socket: " << strerror(errno); + } + + // MessageLoopForIO::Watcher impl. + virtual void OnFileCanReadWithoutBlocking(int fd); + virtual void OnFileCanWriteWithoutBlocking(int fd) { + // SocketReader only watches for accept (read) events. + NOTREACHED(); + } + + private: + // If we haven't completed in a reasonable amount of time, give up. + void OnTimerExpiry() { + parent_->RemoveSocketReader(this); + // We're deleted beyond this point. + } + + MessageLoopForIO::FileDescriptorWatcher fd_reader_; + + // The ProcessSingleton::LinuxWatcher that owns us. + ProcessSingleton::LinuxWatcher* const parent_; + + // A reference to the UI message loop. + MessageLoop* const ui_message_loop_; + + // The file descriptor we're reading. + const int fd_; + + // Store the message in this buffer. + char buf_[kMaxMessageLength]; + + // Tracks the number of bytes we've read in case we're getting partial + // reads. + size_t bytes_read_; + + base::OneShotTimer<SocketReader> timer_; + + DISALLOW_COPY_AND_ASSIGN(SocketReader); + }; + + // Removes and deletes the SocketReader. + void RemoveSocketReader(SocketReader* reader); + MessageLoopForIO::FileDescriptorWatcher fd_watcher_; // A reference to the UI message loop (i.e., the message loop we were @@ -111,12 +164,9 @@ class ProcessSingleton::LinuxWatcher MessageLoop* ui_message_loop_; // The ProcessSingleton that owns us. - ProcessSingleton* parent_; + ProcessSingleton* const parent_; - // The MessageLoopForIO::Watcher that actually reads data from the socket. - // TODO(tc): We shouldn't need to keep a pointer to this. That way we can - // handle multiple concurrent requests. - scoped_ptr<SocketReader> reader_; + std::set<SocketReader*> readers_; DISALLOW_COPY_AND_ASSIGN(LinuxWatcher); }; @@ -131,11 +181,10 @@ void ProcessSingleton::LinuxWatcher::OnFileCanReadWithoutBlocking(int fd) { LOG(ERROR) << "accept() failed: " << strerror(errno); return; } - reader_.reset(new SocketReader(this, ui_message_loop_)); - - // Wait for reads. - MessageLoopForIO::current()->WatchFileDescriptor(connection_socket, - true, MessageLoopForIO::WATCH_READ, reader_->fd_reader(), reader_.get()); + SocketReader* reader = new SocketReader(this, + ui_message_loop_, + connection_socket); + readers_.insert(reader); } void ProcessSingleton::LinuxWatcher::StartListening(int socket) { @@ -190,28 +239,50 @@ void ProcessSingleton::LinuxWatcher::HandleMessage(std::string current_dir, false, profile, NULL); } +void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) { + DCHECK(reader); + readers_.erase(reader); + delete reader; +} + /////////////////////////////////////////////////////////////////////////////// // ProcessSingleton::LinuxWatcher::SocketReader // void ProcessSingleton::LinuxWatcher::SocketReader::OnFileCanReadWithoutBlocking( int fd) { - const int kMaxMessageLength = 32 * 1024; - char buf[kMaxMessageLength]; - ssize_t rv = HANDLE_EINTR(read(fd, buf, sizeof(buf))); - if (rv < 0) { - LOG(ERROR) << "recv() failed: " << strerror(errno); - return; + DCHECK_EQ(fd, fd_); + while (bytes_read_ < sizeof(buf_)) { + ssize_t rv = HANDLE_EINTR( + read(fd, buf_ + bytes_read_, sizeof(buf_) - bytes_read_)); + if (rv < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + LOG(ERROR) << "read() failed: " << strerror(errno); + rv = HANDLE_EINTR(close(fd)); + DCHECK_EQ(0, rv) << "Error closing socket: " << strerror(errno); + return; + } else { + // It would block, so we just return and continue to watch for the next + // opportunity to read. + return; + } + } else if (!rv) { + // No more data to read. It's time to process the message. + break; + } else { + bytes_read_ += rv; + } } // Validate the message. The shortest message is kStartToken\0x\0x - const ssize_t kMinMessageLength = strlen(kStartToken) + 4; - if (rv < kMinMessageLength) { - LOG(ERROR) << "Invalid socket message (wrong length):" << buf; + const size_t kMinMessageLength = arraysize(kStartToken) + 4; + if (bytes_read_ < kMinMessageLength) { + buf_[bytes_read_] = 0; + LOG(ERROR) << "Invalid socket message (wrong length):" << buf_; return; } - std::string str(buf, rv); + std::string str(buf_, bytes_read_); std::vector<std::string> tokens; SplitString(str, kTokenDelimiter, &tokens); @@ -233,6 +304,9 @@ void ProcessSingleton::LinuxWatcher::SocketReader::OnFileCanReadWithoutBlocking( current_dir, tokens)); fd_reader_.StopWatchingFileDescriptor(); + + parent_->RemoveSocketReader(this); + // We are deleted beyond this point. } /////////////////////////////////////////////////////////////////////////////// @@ -282,21 +356,34 @@ bool ProcessSingleton::NotifyOtherProcess() { } // Send the message - int rv = HANDLE_EINTR(write(socket, to_send.data(), to_send.length())); - if (rv < 0) { - if (errno == EAGAIN) { - // TODO(tc): We should kill the hung process here. - NOTIMPLEMENTED() << "browser process hung, don't know how to kill it"; + int bytes_written = 0; + const int buf_len = to_send.length(); + do { + int rv = HANDLE_EINTR( + write(socket, to_send.data() + bytes_written, buf_len - bytes_written)); + if (rv < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // The socket shouldn't block, we're sending so little data. Just give + // up here, since NotifyOtherProcess() doesn't have an asynchronous api. + LOG(ERROR) << "ProcessSingleton would block on write(), so it gave up."; + return false; + } + LOG(ERROR) << "write() failed: " << strerror(errno); return false; } - LOG(ERROR) << "send() failed: " << strerror(errno); - return false; - } + bytes_written += rv; + } while (bytes_written < buf_len); - // TODO(tc): We should wait for an ACK, and if we don't get it in a certain - // time period, kill the other process. + int rv = shutdown(socket, SHUT_WR); + if (rv < 0) { + LOG(ERROR) << "shutdown() failed: " << strerror(errno); + } else { + // TODO(tc): We should wait for an ACK, and if we don't get it in a certain + // time period, kill the other process. + } - HANDLE_EINTR(close(socket)); + rv = HANDLE_EINTR(close(socket)); + DCHECK_EQ(0, rv) << strerror(errno); // Assume the other process is handling the request. return true; @@ -332,6 +419,9 @@ void ProcessSingleton::SetupSocket(int* sock, struct sockaddr_un* addr) { if (*sock < 0) LOG(FATAL) << "socket() failed: " << strerror(errno); + int rv = SetNonBlocking(*sock); + DCHECK_EQ(0, rv) << "Failed to make non-blocking socket."; + addr->sun_family = AF_UNIX; if (socket_path_.value().length() > sizeof(addr->sun_path) - 1) LOG(FATAL) << "Socket path too long: " << socket_path_.value(); |