Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1720)

Side by Side Diff: base/message_pump_mac.mm

Issue 444: Mac-specific CFRunLoop-based MessagePump implementation (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/message_pump_mac.h ('k') | net/net.xcodeproj/project.pbxproj » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "base/message_pump_mac.h"
6
7 #import <AppKit/AppKit.h>
8 #import <Foundation/Foundation.h>
9 #include <float.h>
10
11 namespace {
12
13 void NoOp(void* info) {
14 }
15
16 } // namespace
17
18 namespace base {
19
20 // Must be called on the run loop thread.
21 MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
22 : delegate_(NULL) {
23 run_loop_ = CFRunLoopGetCurrent();
24 CFRetain(run_loop_);
25
26 // Set a repeating timer with a preposterous firing time and interval. The
27 // timer will effectively never fire as-is. The firing time will be adjusted
28 // as needed when ScheduleDelayedWork is called.
29 CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
30 timer_context.info = this;
31 delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator
32 DBL_MAX, // fire time
33 DBL_MAX, // interval
34 0, // flags (ignored)
35 0, // priority (ignored)
36 RunDelayedWorkTimer,
37 &timer_context);
38 CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
39
40 CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
41 source_context.info = this;
42 source_context.perform = RunWork;
43 work_source_ = CFRunLoopSourceCreate(NULL, // allocator
44 0, // priority
45 &source_context);
46 CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes);
47
48 source_context.perform = RunDelayedWork;
49 delayed_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
50 1, // priority
51 &source_context);
52 CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes);
53
54 CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
55 observer_context.info = this;
56 idle_work_observer_ = CFRunLoopObserverCreate(NULL, // allocator
57 kCFRunLoopBeforeWaiting,
58 true, // repeat
59 0, // priority
60 RunIdleWork,
61 &observer_context);
62 CFRunLoopAddObserver(run_loop_, idle_work_observer_, kCFRunLoopCommonModes);
63 }
64
65 // Ideally called on the run loop thread.
66 MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
67 CFRunLoopRemoveObserver(run_loop_, idle_work_observer_,
68 kCFRunLoopCommonModes);
69 CFRelease(idle_work_observer_);
70
71 CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes);
72 CFRelease(delayed_work_source_);
73
74 CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes);
75 CFRelease(work_source_);
76
77 CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
78 CFRelease(delayed_work_timer_);
79
80 CFRelease(run_loop_);
81 }
82
83 // Must be called on the run loop thread.
84 void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
85 Delegate* last_delegate = delegate_;
86 delegate_ = delegate;
87
88 DoRun(delegate);
89
90 delegate_ = last_delegate;
91 }
92
93 // May be called on any thread.
94 void MessagePumpCFRunLoopBase::ScheduleWork() {
95 CFRunLoopSourceSignal(work_source_);
96 CFRunLoopWakeUp(run_loop_);
97 }
98
99 // Must be called on the run loop thread.
100 void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
101 const Time& delayed_work_time) {
102 Time::Exploded exploded;
103 delayed_work_time.UTCExplode(&exploded);
104 double seconds = exploded.second +
105 (static_cast<double>((delayed_work_time.ToInternalValue()) %
106 Time::kMicrosecondsPerSecond) /
107 Time::kMicrosecondsPerSecond);
108 CFGregorianDate gregorian = {
109 exploded.year,
110 exploded.month,
111 exploded.day_of_month,
112 exploded.hour,
113 exploded.minute,
114 seconds
115 };
116 CFAbsoluteTime fire_time = CFGregorianDateGetAbsoluteTime(gregorian, NULL);
117
118 CFRunLoopTimerSetNextFireDate(delayed_work_timer_, fire_time);
119 }
120
121 // Called from the run loop.
122 // static
123 void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
124 void* info) {
125 MessagePumpCFRunLoop* self = static_cast<MessagePumpCFRunLoop*>(info);
126
127 // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
128 // In order to establish the proper priority where delegate_->DoDelayedWork
129 // can only be called if delegate_->DoWork returns false, the timer used
130 // to schedule delayed work must signal a CFRunLoopSource set at a lower
131 // priority than the one used for delegate_->DoWork.
132 CFRunLoopSourceSignal(self->delayed_work_source_);
133 }
134
135 // Called from the run loop.
136 // static
137 void MessagePumpCFRunLoopBase::RunWork(void* info) {
138 MessagePumpCFRunLoop* self = static_cast<MessagePumpCFRunLoop*>(info);
139
140 // Call DoWork once, and if something was done, arrange to come back here
141 // again as long as the loop is still running.
142 if (self->delegate_->DoWork()) {
143 CFRunLoopSourceSignal(self->work_source_);
144 }
145 }
146
147 // Called from the run loop.
148 // static
149 void MessagePumpCFRunLoopBase::RunDelayedWork(void* info) {
150 MessagePumpCFRunLoop* self = static_cast<MessagePumpCFRunLoop*>(info);
151
152 Time next_time;
153 self->delegate_->DoDelayedWork(&next_time);
154 if (!next_time.is_null()) {
155 TimeDelta delay = next_time - Time::Now();
156 if (delay > TimeDelta()) {
157 // There's more delayed work to be done in the future.
158 self->ScheduleDelayedWork(next_time);
159 } else {
160 // There's more delayed work to be done, and its time is in the past.
161 // Arrange to come back here directly as long as the loop is still
162 // running.
163 CFRunLoopSourceSignal(self->delayed_work_source_);
164 }
165 }
166 }
167
168 // Called from the run loop.
169 // static
170 void MessagePumpCFRunLoopBase::RunIdleWork(CFRunLoopObserverRef observer,
171 CFRunLoopActivity activity,
172 void* info) {
173 MessagePumpCFRunLoop* self = static_cast<MessagePumpCFRunLoop*>(info);
174
175 if (self->delegate_->DoIdleWork()) {
176 // If idle work was done, don't let the loop go to sleep. More idle work
177 // might be waiting.
178 CFRunLoopWakeUp(self->run_loop_);
179 }
180 }
181
182 // Must be called on the run loop thread.
183 MessagePumpCFRunLoop::MessagePumpCFRunLoop()
184 : nesting_level_(0),
185 innermost_quittable_(0),
186 quit_pending_(false) {
187 CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
188 observer_context.info = this;
189 enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator
190 kCFRunLoopEntry |
191 kCFRunLoopExit,
192 true, // repeat
193 0, // priority
194 EnterExitRunLoop,
195 &observer_context);
196 CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes);
197 }
198
199 // Ideally called on the run loop thread. If other CFRunLoopRun loops were
200 // running lower on the run loop thread's stack when this object was created,
201 // the same number of CFRunLoopRun loops must be running when this object is
202 // destroyed.
203 MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {
204 CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_,
205 kCFRunLoopCommonModes);
206 CFRelease(enter_exit_observer_);
207 }
208
209 // Called by CFRunLoopBase::DoRun. If other CFRunLoopRun loops were running
210 // lower on the run loop thread's stack when this object was created, the same
211 // number of CFRunLoopRun loops must be running for the outermost call to Run.
212 // Run/DoRun are reentrant after that point.
213 void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
214 // nesting_level_ will be incremented in EnterExitRunLoop, so set
215 // innermost_quittable_ accordingly.
216 int last_innermost_quittable = innermost_quittable_;
217 innermost_quittable_ = nesting_level_ + 1;
218
219 CFRunLoopRun();
220
221 // Restore the previous state of the object.
222 innermost_quittable_ = last_innermost_quittable;
223 }
224
225 // Must be called on the run loop thread.
226 void MessagePumpCFRunLoop::Quit() {
227 // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
228 if (nesting_level_ == innermost_quittable_) {
229 // This object is running the innermost loop, just stop it.
230 CFRunLoopStop(run_loop_);
231 } else {
232 // There's another loop running inside the loop managed by this object.
233 // In other words, someone else called CFRunLoopRun on the same thread,
234 // higher on the stack than our highest Run call. Don't preempt other
235 // run loops, just mark the object to quit our innermost run loop as soon
236 // as the other inner loops we don't manage are done.
237 quit_pending_ = true;
238 }
239 }
240
241 // Called from the run loop.
242 // static
243 void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopObserverRef observer,
244 CFRunLoopActivity activity,
245 void* info) {
246 MessagePumpCFRunLoop* self = static_cast<MessagePumpCFRunLoop*>(info);
247
248 switch (activity) {
249 case kCFRunLoopEntry:
250 // If the run loop was entered by a call to Run, this will properly
251 // balance the decrement done in Run before entering the loop.
252 ++self->nesting_level_;
253 break;
254 case kCFRunLoopExit:
255 if (--self->nesting_level_ == self->innermost_quittable_ &&
256 self->quit_pending_) {
257 // Quit was called while loops other than those managed by this object
258 // were running further inside a run loop managed by this object. Now
259 // that all unmanaged inner run loops are gone, stop the loop running
260 // just inside Run.
261 CFRunLoopStop(self->run_loop_);
262 self->quit_pending_ = false;
263 }
264 break;
265 default:
266 break;
267 }
268 }
269
270 MessagePumpNSRunLoop::MessagePumpNSRunLoop()
271 : keep_running_(true) {
272 CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
273 source_context.perform = NoOp;
274 quit_source_ = CFRunLoopSourceCreate(NULL, // allocator
275 0, // priority
276 &source_context);
277 CFRunLoopAddSource(run_loop_, quit_source_, kCFRunLoopCommonModes);
278 }
279
280 MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
281 CFRunLoopRemoveSource(run_loop_, quit_source_, kCFRunLoopCommonModes);
282 CFRelease(quit_source_);
283 }
284
285 void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
286 while (keep_running_) {
287 // NSRunLoop manages autorelease pools itself.
288 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
289 beforeDate:[NSDate distantFuture]];
290 }
291
292 keep_running_ = true;
293 }
294
295 void MessagePumpNSRunLoop::Quit() {
296 keep_running_ = false;
297 CFRunLoopSourceSignal(quit_source_);
298 CFRunLoopWakeUp(run_loop_);
299 }
300
301 MessagePumpNSApplication::MessagePumpNSApplication()
302 : keep_running_(true),
303 running_own_loop_(false) {
304 }
305
306 void MessagePumpNSApplication::DoRun(Delegate* delegate) {
307 bool last_running_own_loop_ = running_own_loop_;
308
309 [NSApplication sharedApplication];
310
311 if (![NSApp isRunning]) {
312 running_own_loop_ = false;
313 // NSApplication manages autorelease pools itself when run this way.
314 [NSApp run];
315 } else {
316 running_own_loop_ = true;
317 while (keep_running_) {
318 NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init];
319 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
320 untilDate:[NSDate distantFuture]
321 inMode:NSDefaultRunLoopMode
322 dequeue:YES];
323 if (event) {
324 [NSApp sendEvent:event];
325 }
326 [autorelease_pool drain];
327 }
328 keep_running_ = true;
329 }
330
331 running_own_loop_ = last_running_own_loop_;
332 }
333
334 void MessagePumpNSApplication::Quit() {
335 if (!running_own_loop_) {
336 [NSApp stop:nil];
337 } else {
338 keep_running_ = false;
339 }
340
341 // Send a fake event to wake the loop up.
342 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
343 location:NSMakePoint(0,0)
344 modifierFlags:0
345 timestamp:0
346 windowNumber:0
347 context:NULL
348 subtype:0
349 data1:0
350 data2:0]
351 atStart:NO];
352 }
353
354 // static
355 MessagePump* MessagePumpMac::Create() {
356 if ([NSThread isMainThread]) {
357 return new MessagePumpNSApplication;
358 }
359
360 return new MessagePumpNSRunLoop;
361 }
362
363 } // namespace base
OLDNEW
« no previous file with comments | « base/message_pump_mac.h ('k') | net/net.xcodeproj/project.pbxproj » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698