OLD | NEW |
1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/message_pump_glib.h" | 5 #include "base/message_pump_glib.h" |
6 | 6 |
| 7 #include <errno.h> |
7 #include <fcntl.h> | 8 #include <fcntl.h> |
8 #include <math.h> | 9 #include <math.h> |
9 | 10 |
10 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
11 #include "base/logging.h" | 12 #include "base/logging.h" |
12 #include "base/platform_thread.h" | 13 #include "base/platform_thread.h" |
| 14 #include "base/scoped_ptr.h" |
13 | 15 |
14 namespace { | 16 namespace { |
15 | 17 |
16 // We send a byte across a pipe to wakeup the event loop. | 18 // We send a byte across a pipe to wakeup the event loop. |
17 const char kWorkScheduled = '\0'; | 19 const char kWorkScheduled = '\0'; |
18 | 20 |
19 // Return a timeout suitable for the glib loop, -1 to block forever, | 21 // Return a timeout suitable for the glib loop, -1 to block forever, |
20 // 0 to return right away, or a timeout in milliseconds from now. | 22 // 0 to return right away, or a timeout in milliseconds from now. |
21 int GetTimeIntervalMilliseconds(base::Time from) { | 23 int GetTimeIntervalMilliseconds(base::Time from) { |
22 if (from.is_null()) | 24 if (from.is_null()) |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
83 WorkSourceCheck, | 85 WorkSourceCheck, |
84 WorkSourceDispatch, | 86 WorkSourceDispatch, |
85 NULL | 87 NULL |
86 }; | 88 }; |
87 | 89 |
88 } // namespace | 90 } // namespace |
89 | 91 |
90 | 92 |
91 namespace base { | 93 namespace base { |
92 | 94 |
| 95 // static |
| 96 Lock MessagePumpForUI::glib_main_loop_lock_; |
| 97 |
93 MessagePumpForUI::MessagePumpForUI() | 98 MessagePumpForUI::MessagePumpForUI() |
94 : state_(NULL), | 99 : state_(NULL), |
95 context_(g_main_context_default()) { | 100 context_(g_main_context_default()) { |
96 // Create our wakeup pipe, which is used to flag when work was scheduled. | 101 // Create our wakeup pipe, which is used to flag when work was scheduled. |
97 int fds[2]; | 102 int fds[2]; |
98 CHECK(pipe(fds) == 0); | 103 CHECK(pipe(fds) == 0); |
99 wakeup_pipe_read_ = fds[0]; | 104 wakeup_pipe_read_ = fds[0]; |
100 wakeup_pipe_write_ = fds[1]; | 105 wakeup_pipe_write_ = fds[1]; |
101 wakeup_gpollfd_.fd = wakeup_pipe_read_; | 106 wakeup_gpollfd_.fd = wakeup_pipe_read_; |
102 wakeup_gpollfd_.events = G_IO_IN; | 107 wakeup_gpollfd_.events = G_IO_IN; |
103 | 108 |
104 work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); | 109 work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); |
105 static_cast<WorkSource*>(work_source_)->pump = this; | 110 static_cast<WorkSource*>(work_source_)->pump = this; |
106 g_source_add_poll(work_source_, &wakeup_gpollfd_); | 111 g_source_add_poll(work_source_, &wakeup_gpollfd_); |
107 // Use a low priority so that we let other events in the queue go first. | 112 // Use a low priority so that we let other events in the queue go first. |
108 g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE); | 113 g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE); |
109 // This is needed to allow Run calls inside Dispatch. | 114 // This is needed to allow Run calls inside Dispatch. |
110 g_source_set_can_recurse(work_source_, TRUE); | 115 g_source_set_can_recurse(work_source_, TRUE); |
111 g_source_attach(work_source_, context_); | 116 g_source_attach(work_source_, context_); |
112 } | 117 } |
113 | 118 |
114 MessagePumpForUI::~MessagePumpForUI() { | 119 MessagePumpForUI::~MessagePumpForUI() { |
115 g_source_destroy(work_source_); | 120 g_source_destroy(work_source_); |
116 g_source_unref(work_source_); | 121 g_source_unref(work_source_); |
117 close(wakeup_pipe_read_); | 122 close(wakeup_pipe_read_); |
118 close(wakeup_pipe_write_); | 123 close(wakeup_pipe_write_); |
119 } | 124 } |
120 | 125 |
| 126 // Runs a glib main loop iteration. Grabs glib_main_loop_lock_ while |
| 127 // calling glib functions, but releases it while polling. |
| 128 static void GMainIterate(GMainContext* context) { |
| 129 AutoLock locked(MessagePumpForUI::glib_main_loop_lock_); |
| 130 int fds_array_size = 50; |
| 131 scoped_array<GPollFD> fds(new GPollFD[fds_array_size]); |
| 132 |
| 133 // We use the lower-level component functions and do the poll |
| 134 // ourselves so that we can release glib_main_loop_lock_ while |
| 135 // polling, otherwise waiters might starve. |
| 136 |
| 137 // I'll assume no other thread will g_main_context_acquire the |
| 138 // context away from this thread. We cannot g_main_context_wait as |
| 139 // we can't get to the condition and mutex in the context (which is |
| 140 // externally opaque). |
| 141 |
| 142 gint max_priority, timeout; |
| 143 g_main_context_prepare(context, &max_priority); |
| 144 int number_of_fds = g_main_context_query(context, max_priority, &timeout, |
| 145 fds.get(), fds_array_size); |
| 146 // We could reallocate instead, then we'd want to cache the array size... |
| 147 CHECK(number_of_fds <= fds_array_size); |
| 148 GPollFunc poll_func = g_main_context_get_poll_func(context); |
| 149 |
| 150 { |
| 151 // Release the lock while polling |
| 152 AutoUnlock locked(MessagePumpForUI::glib_main_loop_lock_); |
| 153 |
| 154 if ((*poll_func) (fds.get(), number_of_fds, timeout) < 0) { |
| 155 if (errno != EINTR) |
| 156 LOG(ERROR) << "g_main_loop poll error: " << strerror(errno); |
| 157 // Just carry on. |
| 158 } |
| 159 } |
| 160 |
| 161 g_main_context_check(context, max_priority, fds.get(), number_of_fds); |
| 162 g_main_context_dispatch(context); |
| 163 } |
| 164 |
121 void MessagePumpForUI::Run(Delegate* delegate) { | 165 void MessagePumpForUI::Run(Delegate* delegate) { |
122 #ifndef NDEBUG | 166 #ifndef NDEBUG |
123 // Make sure we only run this on one thread. GTK only has one message pump | 167 // Make sure we only run this on one thread. GTK only has one message pump |
124 // so we can only have one UI loop per process. | 168 // so we can only have one UI loop per process. |
125 static PlatformThreadId thread_id = PlatformThread::CurrentId(); | 169 static PlatformThreadId thread_id = PlatformThread::CurrentId(); |
126 DCHECK(thread_id == PlatformThread::CurrentId()) << | 170 DCHECK(thread_id == PlatformThread::CurrentId()) << |
127 "Running MessagePumpForUI on two different threads; " | 171 "Running MessagePumpForUI on two different threads; " |
128 "this is unsupported by GLib!"; | 172 "this is unsupported by GLib!"; |
129 #endif | 173 #endif |
130 | 174 |
131 RunState state; | 175 RunState state; |
132 state.delegate = delegate; | 176 state.delegate = delegate; |
133 state.should_quit = false; | 177 state.should_quit = false; |
134 state.run_depth = state_ ? state_->run_depth + 1 : 1; | 178 state.run_depth = state_ ? state_->run_depth + 1 : 1; |
135 // We really only do a single task for each iteration of the loop. If we | 179 // We really only do a single task for each iteration of the loop. If we |
136 // have done something, assume there is likely something more to do. This | 180 // have done something, assume there is likely something more to do. This |
137 // will mean that we don't block on the message pump until there was nothing | 181 // will mean that we don't block on the message pump until there was nothing |
138 // more to do. We also set this to true to make sure not to block on the | 182 // more to do. We also set this to true to make sure not to block on the |
139 // first iteration of the loop, so RunAllPending() works correctly. | 183 // first iteration of the loop, so RunAllPending() works correctly. |
140 state.more_work_is_plausible = true; | 184 state.more_work_is_plausible = true; |
141 | 185 |
142 RunState* previous_state = state_; | 186 RunState* previous_state = state_; |
143 state_ = &state; | 187 state_ = &state; |
144 | 188 |
145 // We run our own loop instead of using g_main_loop_quit in one of the | 189 // We run our own loop instead of using g_main_loop_quit in one of the |
146 // callbacks. This is so we only quit our own loops, and we don't quit | 190 // callbacks. This is so we only quit our own loops, and we don't quit |
147 // nested loops run by others. TODO(deanm): Is this what we want? | 191 // nested loops run by others. TODO(deanm): Is this what we want? |
148 while (!state_->should_quit) | 192 while (!state_->should_quit) |
149 g_main_context_iteration(context_, true); | 193 GMainIterate(context_); |
150 | 194 |
151 state_ = previous_state; | 195 state_ = previous_state; |
152 } | 196 } |
153 | 197 |
154 // Return the timeout we want passed to poll. | 198 // Return the timeout we want passed to poll. |
155 int MessagePumpForUI::HandlePrepare() { | 199 int MessagePumpForUI::HandlePrepare() { |
156 // If it's likely that we have more work, don't let the pump | 200 // If it's likely that we have more work, don't let the pump |
157 // block so that we can do some processing. | 201 // block so that we can do some processing. |
158 if (state_->more_work_is_plausible) | 202 if (state_->more_work_is_plausible) |
159 return 0; | 203 return 0; |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 } | 272 } |
229 | 273 |
230 void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { | 274 void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { |
231 // We need to wake up the loop in case the poll timeout needs to be | 275 // We need to wake up the loop in case the poll timeout needs to be |
232 // adjusted. This will cause us to try to do work, but that's ok. | 276 // adjusted. This will cause us to try to do work, but that's ok. |
233 delayed_work_time_ = delayed_work_time; | 277 delayed_work_time_ = delayed_work_time; |
234 ScheduleWork(); | 278 ScheduleWork(); |
235 } | 279 } |
236 | 280 |
237 } // namespace base | 281 } // namespace base |
OLD | NEW |