1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "RunLoop.h"
28
29#import "WorkItem.h"
30
31void RunLoop::performWork(void* context)
32{
33    // Wrap main thread in an Autorelease pool.  Sending messages can call
34    // into objc code and accumulate memory.
35    if (current() == main()) {
36        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
37        static_cast<RunLoop*>(context)->performWork();
38        [pool drain];
39    } else
40        static_cast<RunLoop*>(context)->performWork();
41}
42
43RunLoop::RunLoop()
44{
45    m_runLoop = CFRunLoopGetCurrent();
46
47    CFRunLoopSourceContext context = { 0, this, 0, 0, 0, 0, 0, 0, 0, performWork };
48    m_runLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
49    CFRunLoopAddSource(m_runLoop, m_runLoopSource, kCFRunLoopCommonModes);
50}
51
52RunLoop::~RunLoop()
53{
54    // FIXME: Tear down the work item queue here.
55    CFRunLoopSourceInvalidate(m_runLoopSource);
56    CFRelease(m_runLoopSource);
57}
58
59void RunLoop::run()
60{
61    if (current() == main()) {
62        // Use -[NSApplication run] for the main run loop.
63        [NSApp run];
64    } else {
65        // Otherwise, use NSRunLoop. We do this because it sets up an autorelease pool for us.
66        [[NSRunLoop currentRunLoop] run];
67    }
68}
69
70void RunLoop::stop()
71{
72    ASSERT(m_runLoop == CFRunLoopGetCurrent());
73
74    if (m_runLoop == main()->m_runLoop) {
75        [NSApp stop:nil];
76        NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
77                                            location:NSMakePoint(0, 0)
78                                       modifierFlags:0
79                                           timestamp:0.0
80                                         windowNumber:0
81                                             context:nil
82                                             subtype: 0
83                                               data1:0
84                                               data2:0];
85        [NSApp postEvent:event atStart:true];
86    } else
87        CFRunLoopStop(m_runLoop);
88}
89
90void RunLoop::wakeUp()
91{
92    CFRunLoopSourceSignal(m_runLoopSource);
93    CFRunLoopWakeUp(m_runLoop);
94}
95
96// RunLoop::Timer
97
98void RunLoop::TimerBase::timerFired(CFRunLoopTimerRef, void* context)
99{
100    TimerBase* timer = static_cast<TimerBase*>(context);
101
102    // Wrap main thread in an Autorelease pool.  The timer can call
103    // into objc code and accumulate memory outside of the main event loop.
104    if (current() == main()) {
105        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
106        timer->fired();
107        [pool drain];
108    } else
109        timer->fired();
110}
111
112RunLoop::TimerBase::TimerBase(RunLoop* runLoop)
113    : m_runLoop(runLoop)
114    , m_timer(0)
115{
116}
117
118RunLoop::TimerBase::~TimerBase()
119{
120    stop();
121}
122
123void RunLoop::TimerBase::start(double nextFireInterval, bool repeat)
124{
125    if (m_timer)
126        stop();
127
128    CFRunLoopTimerContext context = { 0, this, 0, 0, 0 };
129    CFTimeInterval repeatInterval = repeat ? nextFireInterval : 0;
130    m_timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + nextFireInterval, repeatInterval, 0, 0, timerFired, &context);
131    CFRunLoopAddTimer(m_runLoop->m_runLoop, m_timer, kCFRunLoopCommonModes);
132}
133
134void RunLoop::TimerBase::stop()
135{
136    if (!m_timer)
137        return;
138
139    CFRunLoopTimerInvalidate(m_timer);
140    CFRelease(m_timer);
141    m_timer = 0;
142}
143
144bool RunLoop::TimerBase::isActive() const
145{
146    return m_timer && CFRunLoopTimerIsValid(m_timer);
147}
148