| 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 <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <math.h> | 8 #include <math.h> |
| 9 | 9 |
| 10 #include "base/eintr_wrapper.h" | 10 #include "base/eintr_wrapper.h" |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 // system. File descriptors can be attached to the sources. The poll may block | 43 // system. File descriptors can be attached to the sources. The poll may block |
| 44 // if none of the Prepare calls returned TRUE. It will block indefinitely, or | 44 // if none of the Prepare calls returned TRUE. It will block indefinitely, or |
| 45 // by the minimum time returned by a source in Prepare. | 45 // by the minimum time returned by a source in Prepare. |
| 46 // After the poll, GLib calls Check for each source that returned FALSE | 46 // After the poll, GLib calls Check for each source that returned FALSE |
| 47 // from Prepare. The return value of Check has the same meaning as for Prepare, | 47 // from Prepare. The return value of Check has the same meaning as for Prepare, |
| 48 // making Check a second chance to tell GLib we are ready for Dispatch. | 48 // making Check a second chance to tell GLib we are ready for Dispatch. |
| 49 // Finally, GLib calls Dispatch for each source that is ready. If Dispatch | 49 // Finally, GLib calls Dispatch for each source that is ready. If Dispatch |
| 50 // returns FALSE, GLib will destroy the source. Dispatch calls may be recursive | 50 // returns FALSE, GLib will destroy the source. Dispatch calls may be recursive |
| 51 // (i.e., you can call Run from them), but Prepare and Check cannot. | 51 // (i.e., you can call Run from them), but Prepare and Check cannot. |
| 52 // Finalize is called when the source is destroyed. | 52 // Finalize is called when the source is destroyed. |
| 53 // NOTE: It is common for subsytems to want to process pending events while |
| 54 // doing intensive work, for example the flash plugin. They usually use the |
| 55 // following pattern (recommended by the GTK docs): |
| 56 // while (gtk_events_pending()) { |
| 57 // gtk_main_iteration(); |
| 58 // } |
| 59 // |
| 60 // gtk_events_pending just calls g_main_context_pending, which does the |
| 61 // following: |
| 62 // - Call prepare on all the sources. |
| 63 // - Do the poll with a timeout of 0 (not blocking). |
| 64 // - Call check on all the sources. |
| 65 // - *Does not* call dispatch on the sources. |
| 66 // - Return true if any of prepare() or check() returned true. |
| 67 // |
| 68 // gtk_main_iteration just calls g_main_context_iteration, which does the whole |
| 69 // thing, respecting the timeout for the poll (and block, although it is |
| 70 // expected not to if gtk_events_pending returned true), and call dispatch. |
| 71 // |
| 72 // Thus it is important to only return true from prepare or check if we |
| 73 // actually have events or work to do. We also need to make sure we keep |
| 74 // internal state consistent so that if prepare/check return true when called |
| 75 // from gtk_events_pending, they will still return true when called right |
| 76 // after, from gtk_main_iteration. |
| 77 // |
| 78 // For the GLib pump we try to follow the Windows UI pump model: |
| 79 // - Whenever we receive a wakeup event or the timer for delayed work expires, |
| 80 // we run DoWork and/or DoDelayedWork. That part will also run in the other |
| 81 // event pumps. |
| 82 // - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main |
| 83 // loop, around event handling. |
| 53 | 84 |
| 54 struct WorkSource : public GSource { | 85 struct WorkSource : public GSource { |
| 55 base::MessagePumpForUI* pump; | 86 base::MessagePumpForUI* pump; |
| 56 }; | 87 }; |
| 57 | 88 |
| 58 gboolean WorkSourcePrepare(GSource* source, | 89 gboolean WorkSourcePrepare(GSource* source, |
| 59 gint* timeout_ms) { | 90 gint* timeout_ms) { |
| 60 *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare(); | 91 *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare(); |
| 61 // We always return FALSE, so that our timeout is honored. If we were | 92 // We always return FALSE, so that our timeout is honored. If we were |
| 62 // to return TRUE, the timeout would be considered to be 0 and the poll | 93 // to return TRUE, the timeout would be considered to be 0 and the poll |
| 63 // would never block. Once the poll is finished, Check will be called. | 94 // would never block. Once the poll is finished, Check will be called. |
| 64 return FALSE; | 95 return FALSE; |
| 65 } | 96 } |
| 66 | 97 |
| 67 gboolean WorkSourceCheck(GSource* source) { | 98 gboolean WorkSourceCheck(GSource* source) { |
| 68 // Always return TRUE, and Dispatch will be called. | 99 // Only return TRUE if Dispatch should be called. |
| 69 return TRUE; | 100 return static_cast<WorkSource*>(source)->pump->HandleCheck(); |
| 70 } | 101 } |
| 71 | 102 |
| 72 gboolean WorkSourceDispatch(GSource* source, | 103 gboolean WorkSourceDispatch(GSource* source, |
| 73 GSourceFunc unused_func, | 104 GSourceFunc unused_func, |
| 74 gpointer unused_data) { | 105 gpointer unused_data) { |
| 75 | 106 |
| 76 static_cast<WorkSource*>(source)->pump->HandleDispatch(); | 107 static_cast<WorkSource*>(source)->pump->HandleDispatch(); |
| 77 // Always return TRUE so our source stays registered. | 108 // Always return TRUE so our source stays registered. |
| 78 return TRUE; | 109 return TRUE; |
| 79 } | 110 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 129 static PlatformThreadId thread_id = PlatformThread::CurrentId(); | 160 static PlatformThreadId thread_id = PlatformThread::CurrentId(); |
| 130 DCHECK(thread_id == PlatformThread::CurrentId()) << | 161 DCHECK(thread_id == PlatformThread::CurrentId()) << |
| 131 "Running MessagePumpForUI on two different threads; " | 162 "Running MessagePumpForUI on two different threads; " |
| 132 "this is unsupported by GLib!"; | 163 "this is unsupported by GLib!"; |
| 133 #endif | 164 #endif |
| 134 | 165 |
| 135 RunState state; | 166 RunState state; |
| 136 state.delegate = delegate; | 167 state.delegate = delegate; |
| 137 state.should_quit = false; | 168 state.should_quit = false; |
| 138 state.run_depth = state_ ? state_->run_depth + 1 : 1; | 169 state.run_depth = state_ ? state_->run_depth + 1 : 1; |
| 170 state.has_work = false; |
| 171 |
| 172 RunState* previous_state = state_; |
| 173 state_ = &state; |
| 174 |
| 139 // We really only do a single task for each iteration of the loop. If we | 175 // We really only do a single task for each iteration of the loop. If we |
| 140 // have done something, assume there is likely something more to do. This | 176 // have done something, assume there is likely something more to do. This |
| 141 // will mean that we don't block on the message pump until there was nothing | 177 // will mean that we don't block on the message pump until there was nothing |
| 142 // more to do. We also set this to true to make sure not to block on the | 178 // more to do. We also set this to true to make sure not to block on the |
| 143 // first iteration of the loop, so RunAllPending() works correctly. | 179 // first iteration of the loop, so RunAllPending() works correctly. |
| 144 state.more_work_is_plausible = true; | 180 bool more_work_is_plausible = true; |
| 145 | |
| 146 RunState* previous_state = state_; | |
| 147 state_ = &state; | |
| 148 | 181 |
| 149 // We run our own loop instead of using g_main_loop_quit in one of the | 182 // We run our own loop instead of using g_main_loop_quit in one of the |
| 150 // callbacks. This is so we only quit our own loops, and we don't quit | 183 // callbacks. This is so we only quit our own loops, and we don't quit |
| 151 // nested loops run by others. TODO(deanm): Is this what we want? | 184 // nested loops run by others. TODO(deanm): Is this what we want? |
| 152 while (!state_->should_quit) | 185 for (;;) { |
| 153 g_main_context_iteration(context_, true); | 186 // Don't block if we think we have more work to do. |
| 187 bool block = !more_work_is_plausible; |
| 188 |
| 189 // g_main_context_iteration returns true if events have been dispatched. |
| 190 more_work_is_plausible = g_main_context_iteration(context_, block); |
| 191 if (state_->should_quit) |
| 192 break; |
| 193 |
| 194 more_work_is_plausible |= state_->delegate->DoWork(); |
| 195 if (state_->should_quit) |
| 196 break; |
| 197 |
| 198 more_work_is_plausible |= |
| 199 state_->delegate->DoDelayedWork(&delayed_work_time_); |
| 200 if (state_->should_quit) |
| 201 break; |
| 202 |
| 203 if (more_work_is_plausible) |
| 204 continue; |
| 205 |
| 206 more_work_is_plausible = state_->delegate->DoIdleWork(); |
| 207 if (state_->should_quit) |
| 208 break; |
| 209 } |
| 154 | 210 |
| 155 state_ = previous_state; | 211 state_ = previous_state; |
| 156 } | 212 } |
| 157 | 213 |
| 158 // Return the timeout we want passed to poll. | 214 // Return the timeout we want passed to poll. |
| 159 int MessagePumpForUI::HandlePrepare() { | 215 int MessagePumpForUI::HandlePrepare() { |
| 160 // If it's likely that we have more work, don't let the pump | 216 // We know we have work, but we haven't called HandleDispatch yet. Don't let |
| 161 // block so that we can do some processing. | 217 // the pump block so that we can do some processing. |
| 162 if (state_->more_work_is_plausible) | 218 if (state_->has_work) |
| 163 return 0; | 219 return 0; |
| 164 | 220 |
| 165 // Work wasn't plausible, so we'll block. In the case where glib fires | |
| 166 // our Dispatch(), |more_work_is_plausible| will be reset to whatever it | |
| 167 // should be. However, so we don't get starved by more important work, | |
| 168 // we set |more_work_is_plausible| to true. This means if we come back | |
| 169 // here without having been through Dispatch(), we will get a chance to be | |
| 170 // fired and properly do our work in Dispatch(). | |
| 171 state_->more_work_is_plausible = true; | |
| 172 | |
| 173 // We don't think we have work to do, but make sure not to block | 221 // We don't think we have work to do, but make sure not to block |
| 174 // longer than the next time we need to run delayed work. | 222 // longer than the next time we need to run delayed work. |
| 175 return GetTimeIntervalMilliseconds(delayed_work_time_); | 223 return GetTimeIntervalMilliseconds(delayed_work_time_); |
| 176 } | 224 } |
| 177 | 225 |
| 178 void MessagePumpForUI::HandleDispatch() { | 226 bool MessagePumpForUI::HandleCheck() { |
| 179 // We should only ever have a single message on the wakeup pipe, since we | 227 // We should only ever have a single message on the wakeup pipe, since we |
| 180 // are only signaled when the queue went from empty to non-empty. The glib | 228 // are only signaled when the queue went from empty to non-empty. The glib |
| 181 // poll will tell us whether there was data, so this read shouldn't block. | 229 // poll will tell us whether there was data, so this read shouldn't block. |
| 182 if (wakeup_gpollfd_.revents & G_IO_IN) { | 230 if (wakeup_gpollfd_.revents & G_IO_IN) { |
| 183 char msg; | 231 char msg; |
| 184 if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') { | 232 if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') { |
| 185 NOTREACHED() << "Error reading from the wakeup pipe."; | 233 NOTREACHED() << "Error reading from the wakeup pipe."; |
| 186 } | 234 } |
| 235 // Since we ate the message, we need to record that we have more work, |
| 236 // because HandleCheck() may be called without HandleDispatch being called |
| 237 // afterwards. |
| 238 state_->has_work = true; |
| 239 } |
| 240 |
| 241 if (state_->has_work) |
| 242 return true; |
| 243 |
| 244 if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) { |
| 245 // The timer has expired. That condition will stay true until we process |
| 246 // that delayed work, so we don't need to record this differently. |
| 247 return true; |
| 248 } |
| 249 |
| 250 return false; |
| 251 } |
| 252 |
| 253 void MessagePumpForUI::HandleDispatch() { |
| 254 state_->has_work = false; |
| 255 if (state_->delegate->DoWork()) { |
| 256 // NOTE: on Windows at this point we would call ScheduleWork (see |
| 257 // MessagePumpForUI::HandleWorkMessage in message_pump_win.cc). But here, |
| 258 // instead of posting a message on the wakeup pipe, we can avoid the |
| 259 // syscalls and just signal that we have more work. |
| 260 state_->has_work = true; |
| 187 } | 261 } |
| 188 | 262 |
| 189 if (state_->should_quit) | 263 if (state_->should_quit) |
| 190 return; | 264 return; |
| 191 | 265 |
| 192 state_->more_work_is_plausible = false; | 266 state_->delegate->DoDelayedWork(&delayed_work_time_); |
| 193 | |
| 194 if (state_->delegate->DoWork()) | |
| 195 state_->more_work_is_plausible = true; | |
| 196 | |
| 197 if (state_->should_quit) | |
| 198 return; | |
| 199 | |
| 200 if (state_->delegate->DoDelayedWork(&delayed_work_time_)) | |
| 201 state_->more_work_is_plausible = true; | |
| 202 if (state_->should_quit) | |
| 203 return; | |
| 204 | |
| 205 // Don't do idle work if we think there are more important things | |
| 206 // that we could be doing. | |
| 207 if (state_->more_work_is_plausible) | |
| 208 return; | |
| 209 | |
| 210 if (state_->delegate->DoIdleWork()) | |
| 211 state_->more_work_is_plausible = true; | |
| 212 if (state_->should_quit) | |
| 213 return; | |
| 214 } | 267 } |
| 215 | 268 |
| 216 void MessagePumpForUI::AddObserver(Observer* observer) { | 269 void MessagePumpForUI::AddObserver(Observer* observer) { |
| 217 observers_.AddObserver(observer); | 270 observers_.AddObserver(observer); |
| 218 } | 271 } |
| 219 | 272 |
| 220 void MessagePumpForUI::RemoveObserver(Observer* observer) { | 273 void MessagePumpForUI::RemoveObserver(Observer* observer) { |
| 221 observers_.RemoveObserver(observer); | 274 observers_.RemoveObserver(observer); |
| 222 } | 275 } |
| 223 | 276 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 255 } | 308 } |
| 256 | 309 |
| 257 // static | 310 // static |
| 258 void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) { | 311 void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) { |
| 259 reinterpret_cast<MessagePumpForUI*>(data)->WillProcessEvent(event); | 312 reinterpret_cast<MessagePumpForUI*>(data)->WillProcessEvent(event); |
| 260 gtk_main_do_event(event); | 313 gtk_main_do_event(event); |
| 261 reinterpret_cast<MessagePumpForUI*>(data)->DidProcessEvent(event); | 314 reinterpret_cast<MessagePumpForUI*>(data)->DidProcessEvent(event); |
| 262 } | 315 } |
| 263 | 316 |
| 264 } // namespace base | 317 } // namespace base |
| OLD | NEW |