Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2006, 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 COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #import "config.h"
     27 #import "SharedTimer.h"
     28 
     29 #import <IOKit/IOMessage.h>
     30 #import <IOKit/pwr_mgt/IOPMLib.h>
     31 #import <wtf/Assertions.h>
     32 #import <wtf/Noncopyable.h>
     33 #import <wtf/PassOwnPtr.h>
     34 #import <wtf/UnusedParam.h>
     35 
     36 #include <stdio.h>
     37 
     38 // On Snow Leopard and newer we'll ask IOKit to deliver notifications on a queue.
     39 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
     40 #define IOKIT_WITHOUT_LIBDISPATCH 1
     41 #endif
     42 
     43 namespace WebCore {
     44 
     45 static CFRunLoopTimerRef sharedTimer;
     46 static void (*sharedTimerFiredFunction)();
     47 static void timerFired(CFRunLoopTimerRef, void*);
     48 
     49 #if !defined(IOKIT_WITHOUT_LIBDISPATCH) && defined(BUILDING_ON_SNOW_LEOPARD)
     50 extern "C" void IONotificationPortSetDispatchQueue(IONotificationPortRef notify, dispatch_queue_t queue);
     51 #endif
     52 
     53 class PowerObserver {
     54     WTF_MAKE_NONCOPYABLE(PowerObserver);
     55 
     56 public:
     57     static PassOwnPtr<PowerObserver> create()
     58     {
     59         return adoptPtr(new PowerObserver);
     60     }
     61     ~PowerObserver();
     62 
     63 private:
     64     PowerObserver();
     65 
     66     static void didReceiveSystemPowerNotification(void* context, io_service_t, uint32_t messageType, void* messageArgument);
     67     void didReceiveSystemPowerNotification(io_service_t, uint32_t messageType, void* messageArgument);
     68 
     69     void restartSharedTimer();
     70 
     71     io_connect_t m_powerConnection;
     72     IONotificationPortRef m_notificationPort;
     73     io_object_t m_notifierReference;
     74 #ifdef IOKIT_WITHOUT_LIBDISPATCH
     75     CFRunLoopSourceRef m_runLoopSource;
     76 #else
     77     dispatch_queue_t m_dispatchQueue;
     78 #endif
     79 };
     80 
     81 PowerObserver::PowerObserver()
     82     : m_powerConnection(0)
     83     , m_notificationPort(0)
     84     , m_notifierReference(0)
     85 #ifdef IOKIT_WITHOUT_LIBDISPATCH
     86     , m_runLoopSource(0)
     87 #else
     88     , m_dispatchQueue(dispatch_queue_create("com.apple.WebKit.PowerObserver", 0))
     89 #endif
     90 {
     91     m_powerConnection = IORegisterForSystemPower(this, &m_notificationPort, didReceiveSystemPowerNotification, &m_notifierReference);
     92     if (!m_powerConnection)
     93         return;
     94 
     95 #ifdef IOKIT_WITHOUT_LIBDISPATCH
     96     m_runLoopSource = IONotificationPortGetRunLoopSource(m_notificationPort);
     97     CFRunLoopAddSource(CFRunLoopGetMain(), m_runLoopSource, kCFRunLoopCommonModes);
     98 #else
     99     IONotificationPortSetDispatchQueue(m_notificationPort, m_dispatchQueue);
    100 #endif
    101 }
    102 
    103 PowerObserver::~PowerObserver()
    104 {
    105     if (!m_powerConnection)
    106         return;
    107 
    108 #ifdef IOKIT_WITHOUT_LIBDISPATCH
    109     CFRunLoopRemoveSource(CFRunLoopGetMain(), m_runLoopSource, kCFRunLoopCommonModes);
    110 #else
    111     dispatch_release(m_dispatchQueue);
    112 #endif
    113 
    114     IODeregisterForSystemPower(&m_notifierReference);
    115     IOServiceClose(m_powerConnection);
    116     IONotificationPortDestroy(m_notificationPort);
    117 }
    118 
    119 void PowerObserver::didReceiveSystemPowerNotification(void* context, io_service_t service, uint32_t messageType, void* messageArgument)
    120 {
    121     static_cast<PowerObserver*>(context)->didReceiveSystemPowerNotification(service, messageType, messageArgument);
    122 }
    123 
    124 void PowerObserver::didReceiveSystemPowerNotification(io_service_t, uint32_t messageType, void* messageArgument)
    125 {
    126     IOAllowPowerChange(m_powerConnection, reinterpret_cast<long>(messageArgument));
    127 
    128     // We only care about the "wake from sleep" message.
    129     if (messageType != kIOMessageSystemWillPowerOn)
    130         return;
    131 
    132 #ifdef IOKIT_WITHOUT_LIBDISPATCH
    133     restartSharedTimer();
    134 #else
    135     // We need to restart the timer on the main thread.
    136     CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^() {
    137         restartSharedTimer();
    138     });
    139 #endif
    140 }
    141 
    142 void PowerObserver::restartSharedTimer()
    143 {
    144     ASSERT(CFRunLoopGetCurrent() == CFRunLoopGetMain());
    145 
    146     if (!sharedTimer)
    147         return;
    148 
    149     stopSharedTimer();
    150     timerFired(0, 0);
    151 }
    152 
    153 static PowerObserver* PowerObserver;
    154 
    155 void setSharedTimerFiredFunction(void (*f)())
    156 {
    157     ASSERT(!sharedTimerFiredFunction || sharedTimerFiredFunction == f);
    158 
    159     sharedTimerFiredFunction = f;
    160 }
    161 
    162 static void timerFired(CFRunLoopTimerRef, void*)
    163 {
    164     // FIXME: We can remove this global catch-all if we fix <rdar://problem/5299018>.
    165     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    166     sharedTimerFiredFunction();
    167     [pool drain];
    168 }
    169 
    170 void setSharedTimerFireTime(double fireTime)
    171 {
    172     ASSERT(sharedTimerFiredFunction);
    173 
    174     if (sharedTimer) {
    175         CFRunLoopTimerInvalidate(sharedTimer);
    176         CFRelease(sharedTimer);
    177     }
    178 
    179     CFAbsoluteTime fireDate = fireTime - kCFAbsoluteTimeIntervalSince1970;
    180     sharedTimer = CFRunLoopTimerCreate(0, fireDate, 0, 0, 0, timerFired, 0);
    181     CFRunLoopAddTimer(CFRunLoopGetCurrent(), sharedTimer, kCFRunLoopCommonModes);
    182 
    183     if (!PowerObserver)
    184         PowerObserver = PowerObserver::create().leakPtr();
    185 }
    186 
    187 void stopSharedTimer()
    188 {
    189     if (sharedTimer) {
    190         CFRunLoopTimerInvalidate(sharedTimer);
    191         CFRelease(sharedTimer);
    192         sharedTimer = 0;
    193     }
    194 }
    195 
    196 } // namespace WebCore
    197