Home | History | Annotate | Download | only in mac
      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 
     31 void 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 
     43 RunLoop::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 
     52 RunLoop::~RunLoop()
     53 {
     54     // FIXME: Tear down the work item queue here.
     55     CFRunLoopSourceInvalidate(m_runLoopSource);
     56     CFRelease(m_runLoopSource);
     57 }
     58 
     59 void 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 
     70 void 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 
     90 void RunLoop::wakeUp()
     91 {
     92     CFRunLoopSourceSignal(m_runLoopSource);
     93     CFRunLoopWakeUp(m_runLoop);
     94 }
     95 
     96 // RunLoop::Timer
     97 
     98 void 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 
    112 RunLoop::TimerBase::TimerBase(RunLoop* runLoop)
    113     : m_runLoop(runLoop)
    114     , m_timer(0)
    115 {
    116 }
    117 
    118 RunLoop::TimerBase::~TimerBase()
    119 {
    120     stop();
    121 }
    122 
    123 void 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 
    134 void 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 
    144 bool RunLoop::TimerBase::isActive() const
    145 {
    146     return m_timer && CFRunLoopTimerIsValid(m_timer);
    147 }
    148