Chromium Code Reviews| 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/lazy_instance.h" | 10 #include "base/lazy_instance.h" |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 44 // by the minimum time returned by a source in Prepare. | 44 // by the minimum time returned by a source in Prepare. |
| 45 // After the poll, GLib calls Check for each source that returned FALSE | 45 // After the poll, GLib calls Check for each source that returned FALSE |
| 46 // from Prepare. The return value of Check has the same meaning as for Prepare, | 46 // from Prepare. The return value of Check has the same meaning as for Prepare, |
| 47 // making Check a second chance to tell GLib we are ready for Dispatch. | 47 // making Check a second chance to tell GLib we are ready for Dispatch. |
| 48 // Finally, GLib calls Dispatch for each source that is ready. If Dispatch | 48 // Finally, GLib calls Dispatch for each source that is ready. If Dispatch |
| 49 // returns FALSE, GLib will destroy the source. Dispatch calls may be recursive | 49 // returns FALSE, GLib will destroy the source. Dispatch calls may be recursive |
| 50 // (i.e., you can call Run from them), but Prepare and Check cannot. | 50 // (i.e., you can call Run from them), but Prepare and Check cannot. |
| 51 // Finalize is called when the source is destroyed. | 51 // Finalize is called when the source is destroyed. |
| 52 | 52 |
| 53 struct WorkSource : public GSource { | 53 struct WorkSource : public GSource { |
| 54 int timeout_ms; | 54 base::MessagePumpForUI* pump; |
| 55 }; | 55 }; |
| 56 | 56 |
| 57 gboolean WorkSourcePrepare(GSource* source, | 57 gboolean WorkSourcePrepare(GSource* source, |
| 58 gint* timeout_ms) { | 58 gint* timeout_ms) { |
| 59 *timeout_ms = static_cast<WorkSource*>(source)->timeout_ms; | 59 *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare(); |
| 60 // We always return FALSE, so that our timeout is honored. If we were | |
| 61 // to return TRUE, the timeout would be considered to be 0 and the poll | |
| 62 // would never block. Once the poll is finished, Check will be called. | |
| 60 return FALSE; | 63 return FALSE; |
| 61 } | 64 } |
| 62 | 65 |
| 63 gboolean WorkSourceCheck(GSource* source) { | 66 gboolean WorkSourceCheck(GSource* source) { |
| 64 return FALSE; | 67 // Always return TRUE, and Dispatch will be called. |
| 68 return TRUE; | |
| 65 } | 69 } |
| 66 | 70 |
| 67 gboolean WorkSourceDispatch(GSource* source, | 71 gboolean WorkSourceDispatch(GSource* source, |
| 68 GSourceFunc unused_func, | 72 GSourceFunc unused_func, |
| 69 gpointer unused_data) { | 73 gpointer unused_data) { |
| 70 NOTREACHED(); | 74 |
| 75 static_cast<WorkSource*>(source)->pump->HandleDispatch(); | |
| 76 // Always return TRUE so our source stays registered. | |
| 71 return TRUE; | 77 return TRUE; |
| 72 } | 78 } |
| 73 | 79 |
| 74 // I wish these could be const, but g_source_new wants non-const. | 80 // I wish these could be const, but g_source_new wants non-const. |
| 75 GSourceFuncs WorkSourceFuncs = { | 81 GSourceFuncs WorkSourceFuncs = { |
| 76 WorkSourcePrepare, | 82 WorkSourcePrepare, |
| 77 WorkSourceCheck, | 83 WorkSourceCheck, |
| 78 WorkSourceDispatch, | 84 WorkSourceDispatch, |
| 79 NULL | 85 NULL |
| 80 }; | 86 }; |
| 81 | 87 |
| 82 } // namespace | 88 } // namespace |
| 83 | 89 |
| 84 | 90 |
| 85 namespace base { | 91 namespace base { |
| 86 | 92 |
| 87 MessagePumpForUI::MessagePumpForUI() | 93 MessagePumpForUI::MessagePumpForUI() |
| 88 : state_(NULL), | 94 : state_(NULL), |
| 89 context_(g_main_context_default()) { | 95 context_(g_main_context_default()) { |
| 90 work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); | 96 work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); |
| 97 static_cast<WorkSource*>(work_source_)->pump = this; | |
| 98 // Use a low priority so that we let other events in the queue go first. | |
| 99 g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE); | |
| 91 // This is needed to allow Run calls inside Dispatch. | 100 // This is needed to allow Run calls inside Dispatch. |
| 92 g_source_set_can_recurse(work_source_, TRUE); | 101 g_source_set_can_recurse(work_source_, TRUE); |
| 93 g_source_attach(work_source_, context_); | 102 g_source_attach(work_source_, context_); |
| 94 } | 103 } |
| 95 | 104 |
| 96 MessagePumpForUI::~MessagePumpForUI() { | 105 MessagePumpForUI::~MessagePumpForUI() { |
| 97 g_source_destroy(work_source_); | 106 g_source_destroy(work_source_); |
| 98 g_source_unref(work_source_); | 107 g_source_unref(work_source_); |
| 99 } | 108 } |
| 100 | 109 |
| 101 void MessagePumpForUI::Run(Delegate* delegate) { | 110 void MessagePumpForUI::Run(Delegate* delegate) { |
| 102 #ifndef NDEBUG | 111 #ifndef NDEBUG |
| 103 // Make sure we only run this on one thread. GTK only has one message pump | 112 // Make sure we only run this on one thread. GTK only has one message pump |
| 104 // so we can only have one UI loop per process. | 113 // so we can only have one UI loop per process. |
| 105 static int thread_id = PlatformThread::CurrentId(); | 114 static int thread_id = PlatformThread::CurrentId(); |
| 106 DCHECK(thread_id == PlatformThread::CurrentId()) << | 115 DCHECK(thread_id == PlatformThread::CurrentId()) << |
| 107 "Running MessagePumpForUI on two different threads; " | 116 "Running MessagePumpForUI on two different threads; " |
| 108 "this is unsupported by GLib!"; | 117 "this is unsupported by GLib!"; |
| 109 #endif | 118 #endif |
| 110 | 119 |
| 111 RunState state; | 120 RunState state; |
| 112 state.delegate = delegate; | 121 state.delegate = delegate; |
| 113 state.should_quit = false; | 122 state.should_quit = false; |
| 114 state.run_depth = state_ ? state_->run_depth + 1 : 1; | 123 state.run_depth = state_ ? state_->run_depth + 1 : 1; |
| 115 | |
| 116 RunState* previous_state = state_; | |
| 117 state_ = &state; | |
| 118 | |
| 119 // We really only do a single task for each iteration of the loop. If we | 124 // We really only do a single task for each iteration of the loop. If we |
| 120 // have done something, assume there is likely something more to do. This | 125 // have done something, assume there is likely something more to do. This |
| 121 // will mean that we don't block on the message pump until there was nothing | 126 // will mean that we don't block on the message pump until there was nothing |
| 122 // more to do. We also set this to true to make sure not to block on the | 127 // more to do. We also set this to true to make sure not to block on the |
| 123 // first iteration of the loop, so RunAllPending() works correctly. | 128 // first iteration of the loop, so RunAllPending() works correctly. |
| 124 bool more_work_is_plausible = true; | 129 state.more_work_is_plausible = true; |
| 125 for (;;) { | |
| 126 // Set up our timeout for any delayed work. | |
| 127 static_cast<WorkSource*>(work_source_)->timeout_ms = | |
| 128 GetTimeIntervalMilliseconds(delayed_work_time_); | |
| 129 | 130 |
| 130 // Process a single iteration of the event loop. | 131 RunState* previous_state = state_; |
| 131 g_main_context_iteration(context_, !more_work_is_plausible); | 132 state_ = &state; |
| 132 if (state_->should_quit) | |
| 133 break; | |
| 134 | 133 |
| 135 more_work_is_plausible = false; | 134 // We run our own loop instead of using g_main_loop_quit in one of the |
| 136 | 135 // callbacks. This is so we only quit our own loops, and we don't quit |
| 137 if (state_->delegate->DoWork()) | 136 // nested loops run by others. TODO(deanm): Is this what we want? |
| 138 more_work_is_plausible = true; | 137 while (!state_->should_quit) |
| 139 | 138 g_main_context_iteration(context_, true); |
| 140 if (state_->should_quit) | |
| 141 break; | |
| 142 | |
| 143 if (state_->delegate->DoDelayedWork(&delayed_work_time_)) | |
| 144 more_work_is_plausible = true; | |
| 145 if (state_->should_quit) | |
| 146 break; | |
| 147 | |
| 148 // Don't do idle work if we think there are more important things | |
| 149 // that we could be doing. | |
| 150 if (more_work_is_plausible) | |
| 151 continue; | |
| 152 | |
| 153 if (state_->delegate->DoIdleWork()) | |
| 154 more_work_is_plausible = true; | |
| 155 if (state_->should_quit) | |
| 156 break; | |
| 157 } | |
| 158 | 139 |
| 159 state_ = previous_state; | 140 state_ = previous_state; |
| 160 } | 141 } |
| 161 | 142 |
| 143 // Return the timeout we want passed to poll. | |
| 144 int MessagePumpForUI::HandlePrepare() { | |
| 145 // If it's likely that we have more work, don't let the pump | |
| 146 // block so that we can do some processing. | |
| 147 if (state_->more_work_is_plausible) | |
| 148 return 0; | |
| 149 | |
| 150 // Work wasn't plausible, so we'll block. In the case where glib fires | |
| 151 // our Dispatch(), |more_work_is_plausible| will be reset to whatever it | |
| 152 // should be. However, so we don't get starved by more important work, | |
| 153 // we set |more_work_is_plausible| to true. This means if we come back | |
| 154 // here without having been through Dispatch(), will get a chance to be | |
|
dsh
2008/11/12 23:48:39
Should this have a "we" before "will"?
| |
| 155 // fired and properly do our work in Dispatch(). | |
| 156 state_->more_work_is_plausible = true; | |
| 157 | |
| 158 // We don't think we have work to do, but make sure not to block | |
| 159 // longer than the next time we need to run delayed work. | |
| 160 return GetTimeIntervalMilliseconds(delayed_work_time_); | |
| 161 } | |
| 162 | |
| 163 void MessagePumpForUI::HandleDispatch() { | |
| 164 if (state_->should_quit) | |
| 165 return; | |
| 166 | |
| 167 state_->more_work_is_plausible = false; | |
| 168 | |
| 169 if (state_->delegate->DoWork()) | |
| 170 state_->more_work_is_plausible = true; | |
| 171 | |
| 172 if (state_->should_quit) | |
| 173 return; | |
| 174 | |
| 175 if (state_->delegate->DoDelayedWork(&delayed_work_time_)) | |
| 176 state_->more_work_is_plausible = true; | |
| 177 if (state_->should_quit) | |
| 178 return; | |
| 179 | |
| 180 // Don't do idle work if we think there are more important things | |
| 181 // that we could be doing. | |
| 182 if (state_->more_work_is_plausible) | |
| 183 return; | |
| 184 | |
| 185 if (state_->delegate->DoIdleWork()) | |
| 186 state_->more_work_is_plausible = true; | |
| 187 if (state_->should_quit) | |
| 188 return; | |
| 189 } | |
| 190 | |
| 162 void MessagePumpForUI::Quit() { | 191 void MessagePumpForUI::Quit() { |
| 163 if (state_) { | 192 if (state_) { |
| 164 state_->should_quit = true; | 193 state_->should_quit = true; |
| 165 } else { | 194 } else { |
| 166 NOTREACHED() << "Quit called outside Run!"; | 195 NOTREACHED() << "Quit called outside Run!"; |
| 167 } | 196 } |
| 168 } | 197 } |
| 169 | 198 |
| 170 void MessagePumpForUI::ScheduleWork() { | 199 void MessagePumpForUI::ScheduleWork() { |
| 171 // This can be called on any thread, so we don't want to touch any state | 200 // This can be called on any thread, so we don't want to touch any state |
| 172 // variables as we would then need locks all over. This ensures that if | 201 // variables as we would then need locks all over. This ensures that if |
| 173 // we are sleeping in a poll that we will wake up. | 202 // we are sleeping in a poll that we will wake up. |
| 174 g_main_context_wakeup(context_); | 203 g_main_context_wakeup(context_); |
| 175 } | 204 } |
| 176 | 205 |
| 177 void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { | 206 void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { |
| 178 // We need to wake up the loop in case the poll timeout needs to be | 207 // We need to wake up the loop in case the poll timeout needs to be |
| 179 // adjusted. This will cause us to try to do work, but that's ok. | 208 // adjusted. This will cause us to try to do work, but that's ok. |
| 180 delayed_work_time_ = delayed_work_time; | 209 delayed_work_time_ = delayed_work_time; |
| 181 ScheduleWork(); | 210 ScheduleWork(); |
| 182 } | 211 } |
| 183 | 212 |
| 184 } // namespace base | 213 } // namespace base |
| OLD | NEW |