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 |