[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 1 | // Copyright (c) 2008 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 "base/message_pump_libevent.h" |
| 6 | |
[email protected] | 91cb370 | 2009-01-20 21:12:15 | [diff] [blame] | 7 | #include <errno.h> |
[email protected] | 157fd852 | 2009-01-20 22:12:26 | [diff] [blame] | 8 | #include <fcntl.h> |
[email protected] | 89836e2 | 2008-09-25 20:33:42 | [diff] [blame] | 9 | |
[email protected] | 157c61b | 2009-05-01 21:37:31 | [diff] [blame] | 10 | #include "eintr_wrapper.h" |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 11 | #include "base/logging.h" |
[email protected] | 89836e2 | 2008-09-25 20:33:42 | [diff] [blame] | 12 | #include "base/scoped_nsautorelease_pool.h" |
[email protected] | f74c896 | 2009-04-22 20:01:36 | [diff] [blame] | 13 | #include "base/scoped_ptr.h" |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 14 | #include "base/time.h" |
| 15 | #include "third_party/libevent/event.h" |
| 16 | |
[email protected] | f74c896 | 2009-04-22 20:01:36 | [diff] [blame] | 17 | // Lifecycle of struct event |
| 18 | // Libevent uses two main data structures: |
| 19 | // struct event_base (of which there is one per message pump), and |
| 20 | // struct event (of which there is roughly one per socket). |
| 21 | // The socket's struct event is created in |
| 22 | // MessagePumpLibevent::WatchFileDescriptor(), |
| 23 | // is owned by the FileDescriptorWatcher, and is destroyed in |
| 24 | // StopWatchingFileDescriptor(). |
| 25 | // It is moved into and out of lists in struct event_base by |
| 26 | // the libevent functions event_add() and event_del(). |
| 27 | // |
| 28 | // TODO(dkegel): |
| 29 | // At the moment bad things happen if a FileDescriptorWatcher |
| 30 | // is active after its MessagePumpLibevent has been destroyed. |
| 31 | // See MessageLoopTest.FileDescriptorWatcherOutlivesMessageLoop |
| 32 | // Not clear yet whether that situation occurs in practice, |
| 33 | // but if it does, we need to fix it. |
| 34 | |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 35 | namespace base { |
| 36 | |
| 37 | // Return 0 on success |
| 38 | // Too small a function to bother putting in a library? |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 39 | static int SetNonBlocking(int fd) { |
| 40 | int flags = fcntl(fd, F_GETFL, 0); |
| 41 | if (flags == -1) |
| 42 | flags = 0; |
| 43 | return fcntl(fd, F_SETFL, flags | O_NONBLOCK); |
| 44 | } |
| 45 | |
| 46 | MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher() |
| 47 | : is_persistent_(false), |
| 48 | event_(NULL) { |
| 49 | } |
| 50 | |
| 51 | MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() { |
[email protected] | f74c896 | 2009-04-22 20:01:36 | [diff] [blame] | 52 | if (event_) { |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 53 | StopWatchingFileDescriptor(); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e, |
| 58 | bool is_persistent) { |
| 59 | DCHECK(e); |
[email protected] | f74c896 | 2009-04-22 20:01:36 | [diff] [blame] | 60 | DCHECK(event_ == NULL); |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 61 | |
| 62 | is_persistent_ = is_persistent; |
[email protected] | f74c896 | 2009-04-22 20:01:36 | [diff] [blame] | 63 | event_ = e; |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 64 | } |
| 65 | |
| 66 | event *MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() { |
[email protected] | f74c896 | 2009-04-22 20:01:36 | [diff] [blame] | 67 | struct event *e = event_; |
| 68 | event_ = NULL; |
| 69 | return e; |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() { |
[email protected] | f74c896 | 2009-04-22 20:01:36 | [diff] [blame] | 73 | event* e = ReleaseEvent(); |
| 74 | if (e == NULL) |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 75 | return true; |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 76 | |
[email protected] | f74c896 | 2009-04-22 20:01:36 | [diff] [blame] | 77 | // event_del() is a no-op if the event isn't active. |
| 78 | int rv = event_del(e); |
| 79 | delete e; |
| 80 | return (rv == 0); |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | // Called if a byte is received on the wakeup pipe. |
| 84 | void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) { |
[email protected] | 1d2eb13 | 2008-12-08 17:36:06 | [diff] [blame] | 85 | base::MessagePumpLibevent* that = |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 86 | static_cast<base::MessagePumpLibevent*>(context); |
| 87 | DCHECK(that->wakeup_pipe_out_ == socket); |
| 88 | |
| 89 | // Remove and discard the wakeup byte. |
| 90 | char buf; |
[email protected] | 157c61b | 2009-05-01 21:37:31 | [diff] [blame] | 91 | int nread = HANDLE_EINTR(read(socket, &buf, 1)); |
[email protected] | 3fae228 | 2009-03-17 22:26:06 | [diff] [blame] | 92 | DCHECK_EQ(nread, 1); |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 93 | // Tell libevent to break out of inner loop. |
| 94 | event_base_loopbreak(that->event_base_); |
| 95 | } |
| 96 | |
| 97 | MessagePumpLibevent::MessagePumpLibevent() |
| 98 | : keep_running_(true), |
| 99 | in_run_(false), |
| 100 | event_base_(event_base_new()), |
| 101 | wakeup_pipe_in_(-1), |
| 102 | wakeup_pipe_out_(-1) { |
| 103 | if (!Init()) |
| 104 | NOTREACHED(); |
| 105 | } |
| 106 | |
| 107 | bool MessagePumpLibevent::Init() { |
| 108 | int fds[2]; |
[email protected] | 91cb370 | 2009-01-20 21:12:15 | [diff] [blame] | 109 | if (pipe(fds)) { |
| 110 | DLOG(ERROR) << "pipe() failed, errno: " << errno; |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 111 | return false; |
[email protected] | 91cb370 | 2009-01-20 21:12:15 | [diff] [blame] | 112 | } |
| 113 | if (SetNonBlocking(fds[0])) { |
| 114 | DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno; |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 115 | return false; |
[email protected] | 91cb370 | 2009-01-20 21:12:15 | [diff] [blame] | 116 | } |
| 117 | if (SetNonBlocking(fds[1])) { |
| 118 | DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno; |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 119 | return false; |
[email protected] | 91cb370 | 2009-01-20 21:12:15 | [diff] [blame] | 120 | } |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 121 | wakeup_pipe_out_ = fds[0]; |
| 122 | wakeup_pipe_in_ = fds[1]; |
| 123 | |
| 124 | wakeup_event_ = new event; |
[email protected] | 1d2eb13 | 2008-12-08 17:36:06 | [diff] [blame] | 125 | event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST, |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 126 | OnWakeup, this); |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 127 | event_base_set(event_base_, wakeup_event_); |
| 128 | |
| 129 | if (event_add(wakeup_event_, 0)) |
| 130 | return false; |
| 131 | return true; |
| 132 | } |
| 133 | |
| 134 | MessagePumpLibevent::~MessagePumpLibevent() { |
| 135 | DCHECK(wakeup_event_); |
| 136 | DCHECK(event_base_); |
| 137 | event_del(wakeup_event_); |
| 138 | delete wakeup_event_; |
[email protected] | c7e5e94 | 2009-02-03 03:13:53 | [diff] [blame] | 139 | if (wakeup_pipe_in_ >= 0) |
| 140 | close(wakeup_pipe_in_); |
| 141 | if (wakeup_pipe_out_ >= 0) |
| 142 | close(wakeup_pipe_out_); |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 143 | event_base_free(event_base_); |
| 144 | } |
| 145 | |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 146 | bool MessagePumpLibevent::WatchFileDescriptor(int fd, |
| 147 | bool persistent, |
| 148 | Mode mode, |
| 149 | FileDescriptorWatcher *controller, |
| 150 | Watcher *delegate) { |
| 151 | DCHECK(fd > 0); |
| 152 | DCHECK(controller); |
| 153 | DCHECK(delegate); |
| 154 | DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE); |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 155 | |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 156 | int event_mask = persistent ? EV_PERSIST : 0; |
| 157 | if ((mode & WATCH_READ) != 0) { |
| 158 | event_mask |= EV_READ; |
| 159 | } |
| 160 | if ((mode & WATCH_WRITE) != 0) { |
| 161 | event_mask |= EV_WRITE; |
[email protected] | 900342a | 2008-12-12 19:55:17 | [diff] [blame] | 162 | } |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 163 | |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 164 | // |should_delete_event| is true if we're modifying an event that's currently |
| 165 | // active in |controller|. |
| 166 | // If we're modifying an existing event and there's an error then we need to |
| 167 | // tell libevent to clean it up via event_delete() before returning. |
| 168 | bool should_delete_event = true; |
| 169 | scoped_ptr<event> evt(controller->ReleaseEvent()); |
| 170 | if (evt.get() == NULL) { |
| 171 | should_delete_event = false; |
| 172 | // Ownership is transferred to the controller. |
| 173 | evt.reset(new event); |
| 174 | } |
[email protected] | 900342a | 2008-12-12 19:55:17 | [diff] [blame] | 175 | |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 176 | // Set current interest mask and message pump for this event. |
| 177 | event_set(evt.get(), fd, event_mask, OnLibeventNotification, |
| 178 | delegate); |
| 179 | |
| 180 | // Tell libevent which message pump this socket will belong to when we add it. |
| 181 | if (event_base_set(event_base_, evt.get()) != 0) { |
| 182 | if (should_delete_event) { |
| 183 | event_del(evt.get()); |
| 184 | } |
| 185 | return false; |
| 186 | } |
| 187 | |
| 188 | // Add this socket to the list of monitored sockets. |
| 189 | if (event_add(evt.get(), NULL) != 0) { |
| 190 | if (should_delete_event) { |
| 191 | event_del(evt.get()); |
| 192 | } |
| 193 | return false; |
| 194 | } |
| 195 | |
[email protected] | f74c896 | 2009-04-22 20:01:36 | [diff] [blame] | 196 | // Transfer ownership of evt to controller. |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 197 | controller->Init(evt.release(), persistent); |
| 198 | return true; |
[email protected] | 900342a | 2008-12-12 19:55:17 | [diff] [blame] | 199 | } |
| 200 | |
[email protected] | 900342a | 2008-12-12 19:55:17 | [diff] [blame] | 201 | |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 202 | void MessagePumpLibevent::OnLibeventNotification(int fd, short flags, |
| 203 | void* context) { |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 204 | Watcher* watcher = static_cast<Watcher*>(context); |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 205 | |
[email protected] | e45e6c0 | 2008-12-15 22:02:17 | [diff] [blame] | 206 | if (flags & EV_WRITE) { |
| 207 | watcher->OnFileCanWriteWithoutBlocking(fd); |
| 208 | } |
| 209 | if (flags & EV_READ) { |
| 210 | watcher->OnFileCanReadWithoutBlocking(fd); |
| 211 | } |
[email protected] | 1d2eb13 | 2008-12-08 17:36:06 | [diff] [blame] | 212 | } |
| 213 | |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 214 | // Reentrant! |
| 215 | void MessagePumpLibevent::Run(Delegate* delegate) { |
| 216 | DCHECK(keep_running_) << "Quit must have been called outside of Run!"; |
| 217 | |
| 218 | bool old_in_run = in_run_; |
| 219 | in_run_ = true; |
| 220 | |
| 221 | for (;;) { |
[email protected] | 89836e2 | 2008-09-25 20:33:42 | [diff] [blame] | 222 | ScopedNSAutoreleasePool autorelease_pool; |
| 223 | |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 224 | bool did_work = delegate->DoWork(); |
| 225 | if (!keep_running_) |
| 226 | break; |
| 227 | |
| 228 | did_work |= delegate->DoDelayedWork(&delayed_work_time_); |
| 229 | if (!keep_running_) |
| 230 | break; |
| 231 | |
| 232 | if (did_work) |
| 233 | continue; |
| 234 | |
| 235 | did_work = delegate->DoIdleWork(); |
| 236 | if (!keep_running_) |
| 237 | break; |
| 238 | |
| 239 | if (did_work) |
| 240 | continue; |
| 241 | |
| 242 | // EVLOOP_ONCE tells libevent to only block once, |
| 243 | // but to service all pending events when it wakes up. |
| 244 | if (delayed_work_time_.is_null()) { |
| 245 | event_base_loop(event_base_, EVLOOP_ONCE); |
| 246 | } else { |
| 247 | TimeDelta delay = delayed_work_time_ - Time::Now(); |
| 248 | if (delay > TimeDelta()) { |
| 249 | struct timeval poll_tv; |
| 250 | poll_tv.tv_sec = delay.InSeconds(); |
| 251 | poll_tv.tv_usec = delay.InMicroseconds() % Time::kMicrosecondsPerSecond; |
| 252 | event_base_loopexit(event_base_, &poll_tv); |
| 253 | event_base_loop(event_base_, EVLOOP_ONCE); |
| 254 | } else { |
| 255 | // It looks like delayed_work_time_ indicates a time in the past, so we |
| 256 | // need to call DoDelayedWork now. |
| 257 | delayed_work_time_ = Time(); |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | keep_running_ = true; |
| 263 | in_run_ = old_in_run; |
| 264 | } |
| 265 | |
| 266 | void MessagePumpLibevent::Quit() { |
| 267 | DCHECK(in_run_); |
| 268 | // Tell both libevent and Run that they should break out of their loops. |
| 269 | keep_running_ = false; |
| 270 | ScheduleWork(); |
| 271 | } |
| 272 | |
| 273 | void MessagePumpLibevent::ScheduleWork() { |
| 274 | // Tell libevent (in a threadsafe way) that it should break out of its loop. |
| 275 | char buf = 0; |
[email protected] | 157c61b | 2009-05-01 21:37:31 | [diff] [blame] | 276 | int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1)); |
[email protected] | 3fae228 | 2009-03-17 22:26:06 | [diff] [blame] | 277 | DCHECK(nwrite == 1 || errno == EAGAIN) |
| 278 | << "[nwrite:" << nwrite << "] [errno:" << errno << "]"; |
[email protected] | 36987e9 | 2008-09-18 18:46:26 | [diff] [blame] | 279 | } |
| 280 | |
| 281 | void MessagePumpLibevent::ScheduleDelayedWork(const Time& delayed_work_time) { |
| 282 | // We know that we can't be blocked on Wait right now since this method can |
| 283 | // only be called on the same thread as Run, so we only need to update our |
| 284 | // record of how long to sleep when we do sleep. |
| 285 | delayed_work_time_ = delayed_work_time; |
| 286 | } |
| 287 | |
| 288 | } // namespace base |