blob: a940081012f49c2c1a94cff318bb14b984eac430 [file] [log] [blame]
[email protected]36987e92008-09-18 18:46:261// 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]91cb3702009-01-20 21:12:157#include <errno.h>
[email protected]157fd8522009-01-20 22:12:268#include <fcntl.h>
[email protected]89836e22008-09-25 20:33:429
[email protected]157c61b2009-05-01 21:37:3110#include "eintr_wrapper.h"
[email protected]36987e92008-09-18 18:46:2611#include "base/logging.h"
[email protected]89836e22008-09-25 20:33:4212#include "base/scoped_nsautorelease_pool.h"
[email protected]f74c8962009-04-22 20:01:3613#include "base/scoped_ptr.h"
[email protected]36987e92008-09-18 18:46:2614#include "base/time.h"
15#include "third_party/libevent/event.h"
16
[email protected]f74c8962009-04-22 20:01:3617// 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]36987e92008-09-18 18:46:2635namespace base {
36
37// Return 0 on success
38// Too small a function to bother putting in a library?
[email protected]e45e6c02008-12-15 22:02:1739static 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
46MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher()
47 : is_persistent_(false),
48 event_(NULL) {
49}
50
51MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() {
[email protected]f74c8962009-04-22 20:01:3652 if (event_) {
[email protected]e45e6c02008-12-15 22:02:1753 StopWatchingFileDescriptor();
54 }
55}
56
57void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e,
58 bool is_persistent) {
59 DCHECK(e);
[email protected]f74c8962009-04-22 20:01:3660 DCHECK(event_ == NULL);
[email protected]e45e6c02008-12-15 22:02:1761
62 is_persistent_ = is_persistent;
[email protected]f74c8962009-04-22 20:01:3663 event_ = e;
[email protected]e45e6c02008-12-15 22:02:1764}
65
66event *MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() {
[email protected]f74c8962009-04-22 20:01:3667 struct event *e = event_;
68 event_ = NULL;
69 return e;
[email protected]e45e6c02008-12-15 22:02:1770}
71
72bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() {
[email protected]f74c8962009-04-22 20:01:3673 event* e = ReleaseEvent();
74 if (e == NULL)
[email protected]e45e6c02008-12-15 22:02:1775 return true;
[email protected]e45e6c02008-12-15 22:02:1776
[email protected]f74c8962009-04-22 20:01:3677 // 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]36987e92008-09-18 18:46:2681}
82
83// Called if a byte is received on the wakeup pipe.
84void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) {
[email protected]1d2eb132008-12-08 17:36:0685 base::MessagePumpLibevent* that =
[email protected]36987e92008-09-18 18:46:2686 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]157c61b2009-05-01 21:37:3191 int nread = HANDLE_EINTR(read(socket, &buf, 1));
[email protected]3fae2282009-03-17 22:26:0692 DCHECK_EQ(nread, 1);
[email protected]36987e92008-09-18 18:46:2693 // Tell libevent to break out of inner loop.
94 event_base_loopbreak(that->event_base_);
95}
96
97MessagePumpLibevent::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
107bool MessagePumpLibevent::Init() {
108 int fds[2];
[email protected]91cb3702009-01-20 21:12:15109 if (pipe(fds)) {
110 DLOG(ERROR) << "pipe() failed, errno: " << errno;
[email protected]36987e92008-09-18 18:46:26111 return false;
[email protected]91cb3702009-01-20 21:12:15112 }
113 if (SetNonBlocking(fds[0])) {
114 DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno;
[email protected]36987e92008-09-18 18:46:26115 return false;
[email protected]91cb3702009-01-20 21:12:15116 }
117 if (SetNonBlocking(fds[1])) {
118 DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno;
[email protected]36987e92008-09-18 18:46:26119 return false;
[email protected]91cb3702009-01-20 21:12:15120 }
[email protected]36987e92008-09-18 18:46:26121 wakeup_pipe_out_ = fds[0];
122 wakeup_pipe_in_ = fds[1];
123
124 wakeup_event_ = new event;
[email protected]1d2eb132008-12-08 17:36:06125 event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST,
[email protected]e45e6c02008-12-15 22:02:17126 OnWakeup, this);
[email protected]36987e92008-09-18 18:46:26127 event_base_set(event_base_, wakeup_event_);
128
129 if (event_add(wakeup_event_, 0))
130 return false;
131 return true;
132}
133
134MessagePumpLibevent::~MessagePumpLibevent() {
135 DCHECK(wakeup_event_);
136 DCHECK(event_base_);
137 event_del(wakeup_event_);
138 delete wakeup_event_;
[email protected]c7e5e942009-02-03 03:13:53139 if (wakeup_pipe_in_ >= 0)
140 close(wakeup_pipe_in_);
141 if (wakeup_pipe_out_ >= 0)
142 close(wakeup_pipe_out_);
[email protected]36987e92008-09-18 18:46:26143 event_base_free(event_base_);
144}
145
[email protected]e45e6c02008-12-15 22:02:17146bool 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]36987e92008-09-18 18:46:26155
[email protected]e45e6c02008-12-15 22:02:17156 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]900342a2008-12-12 19:55:17162 }
[email protected]36987e92008-09-18 18:46:26163
[email protected]e45e6c02008-12-15 22:02:17164 // |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]900342a2008-12-12 19:55:17175
[email protected]e45e6c02008-12-15 22:02:17176 // 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]f74c8962009-04-22 20:01:36196 // Transfer ownership of evt to controller.
[email protected]e45e6c02008-12-15 22:02:17197 controller->Init(evt.release(), persistent);
198 return true;
[email protected]900342a2008-12-12 19:55:17199}
200
[email protected]900342a2008-12-12 19:55:17201
[email protected]e45e6c02008-12-15 22:02:17202void MessagePumpLibevent::OnLibeventNotification(int fd, short flags,
203 void* context) {
[email protected]36987e92008-09-18 18:46:26204 Watcher* watcher = static_cast<Watcher*>(context);
[email protected]36987e92008-09-18 18:46:26205
[email protected]e45e6c02008-12-15 22:02:17206 if (flags & EV_WRITE) {
207 watcher->OnFileCanWriteWithoutBlocking(fd);
208 }
209 if (flags & EV_READ) {
210 watcher->OnFileCanReadWithoutBlocking(fd);
211 }
[email protected]1d2eb132008-12-08 17:36:06212}
213
[email protected]36987e92008-09-18 18:46:26214// Reentrant!
215void 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]89836e22008-09-25 20:33:42222 ScopedNSAutoreleasePool autorelease_pool;
223
[email protected]36987e92008-09-18 18:46:26224 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
266void 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
273void MessagePumpLibevent::ScheduleWork() {
274 // Tell libevent (in a threadsafe way) that it should break out of its loop.
275 char buf = 0;
[email protected]157c61b2009-05-01 21:37:31276 int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1));
[email protected]3fae2282009-03-17 22:26:06277 DCHECK(nwrite == 1 || errno == EAGAIN)
278 << "[nwrite:" << nwrite << "] [errno:" << errno << "]";
[email protected]36987e92008-09-18 18:46:26279}
280
281void 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