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 #include "config.h"
     27 #include "WorkQueue.h"
     28 
     29 #include <mach/mach_port.h>
     30 #include <wtf/PassOwnPtr.h>
     31 
     32 #if HAVE(DISPATCH_H)
     33 
     34 void WorkQueue::executeWorkItem(void* item)
     35 {
     36     WorkQueue* queue = static_cast<WorkQueue*>(dispatch_get_context(dispatch_get_current_queue()));
     37     OwnPtr<WorkItem> workItem(static_cast<WorkItem*>(item));
     38 
     39     {
     40         MutexLocker locker(queue->m_isValidMutex);
     41         if (!queue->m_isValid)
     42             return;
     43     }
     44 
     45     workItem->execute();
     46 }
     47 
     48 void WorkQueue::scheduleWork(PassOwnPtr<WorkItem> item)
     49 {
     50     dispatch_async_f(m_dispatchQueue, item.leakPtr(), executeWorkItem);
     51 }
     52 
     53 void WorkQueue::scheduleWorkAfterDelay(PassOwnPtr<WorkItem> item, double delay)
     54 {
     55     dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC);
     56 
     57     dispatch_after_f(delayTime, m_dispatchQueue, item.leakPtr(), executeWorkItem);
     58 }
     59 
     60 class WorkQueue::EventSource {
     61 public:
     62     EventSource(MachPortEventType eventType, dispatch_source_t dispatchSource, PassOwnPtr<WorkItem> workItem)
     63         : m_eventType(eventType)
     64         , m_dispatchSource(dispatchSource)
     65         , m_workItem(workItem)
     66     {
     67     }
     68 
     69     dispatch_source_t dispatchSource() const { return m_dispatchSource; }
     70 
     71     static void eventHandler(void* source)
     72     {
     73         EventSource* eventSource = static_cast<EventSource*>(source);
     74 
     75         eventSource->m_workItem->execute();
     76     }
     77 
     78     static void cancelHandler(void* source)
     79     {
     80         EventSource* eventSource = static_cast<EventSource*>(source);
     81 
     82         mach_port_t machPort = dispatch_source_get_handle(eventSource->m_dispatchSource);
     83 
     84         switch (eventSource->m_eventType) {
     85         case MachPortDataAvailable:
     86             // Release our receive right.
     87             mach_port_mod_refs(mach_task_self(), machPort, MACH_PORT_RIGHT_RECEIVE, -1);
     88             break;
     89         case MachPortDeadNameNotification:
     90             // Release our send right.
     91             mach_port_deallocate(mach_task_self(), machPort);
     92             break;
     93         }
     94     }
     95 
     96     static void finalizeHandler(void* source)
     97     {
     98         EventSource* eventSource = static_cast<EventSource*>(source);
     99 
    100         delete eventSource;
    101     }
    102 
    103 private:
    104     MachPortEventType m_eventType;
    105 
    106     // This is a weak reference, since m_dispatchSource references the event source.
    107     dispatch_source_t m_dispatchSource;
    108 
    109     OwnPtr<WorkItem> m_workItem;
    110 };
    111 
    112 void WorkQueue::registerMachPortEventHandler(mach_port_t machPort, MachPortEventType eventType, PassOwnPtr<WorkItem> workItem)
    113 {
    114     dispatch_source_type_t sourceType = 0;
    115     switch (eventType) {
    116     case MachPortDataAvailable:
    117         sourceType = DISPATCH_SOURCE_TYPE_MACH_RECV;
    118         break;
    119     case MachPortDeadNameNotification:
    120         sourceType = DISPATCH_SOURCE_TYPE_MACH_SEND;
    121         break;
    122     }
    123 
    124     dispatch_source_t dispatchSource = dispatch_source_create(sourceType, machPort, 0, m_dispatchQueue);
    125 
    126     EventSource* eventSource = new EventSource(eventType, dispatchSource, workItem);
    127     dispatch_set_context(dispatchSource, eventSource);
    128 
    129     dispatch_source_set_event_handler_f(dispatchSource, &EventSource::eventHandler);
    130     dispatch_source_set_cancel_handler_f(dispatchSource, &EventSource::cancelHandler);
    131     dispatch_set_finalizer_f(dispatchSource, &EventSource::finalizeHandler);
    132 
    133     // Add the source to our set of sources.
    134     {
    135         MutexLocker locker(m_eventSourcesMutex);
    136 
    137         ASSERT(!m_eventSources.contains(machPort));
    138 
    139         m_eventSources.set(machPort, eventSource);
    140 
    141         // And start it!
    142         dispatch_resume(dispatchSource);
    143     }
    144 }
    145 
    146 void WorkQueue::unregisterMachPortEventHandler(mach_port_t machPort)
    147 {
    148     ASSERT(machPort);
    149 
    150     MutexLocker locker(m_eventSourcesMutex);
    151 
    152     HashMap<mach_port_t, EventSource*>::iterator it = m_eventSources.find(machPort);
    153     ASSERT(it != m_eventSources.end());
    154 
    155     ASSERT(m_eventSources.contains(machPort));
    156 
    157     EventSource* eventSource = it->second;
    158     // Cancel and release the source. It will be deleted in its finalize handler.
    159     dispatch_source_cancel(eventSource->dispatchSource());
    160     dispatch_release(eventSource->dispatchSource());
    161 
    162     m_eventSources.remove(it);
    163 }
    164 
    165 void WorkQueue::platformInitialize(const char* name)
    166 {
    167     m_dispatchQueue = dispatch_queue_create(name, 0);
    168     dispatch_set_context(m_dispatchQueue, this);
    169 }
    170 
    171 void WorkQueue::platformInvalidate()
    172 {
    173 #if !ASSERT_DISABLED
    174     MutexLocker locker(m_eventSourcesMutex);
    175     ASSERT(m_eventSources.isEmpty());
    176 #endif
    177 
    178     dispatch_release(m_dispatchQueue);
    179 }
    180 
    181 #else /* !HAVE(DISPATCH_H) */
    182 
    183 void WorkQueue::scheduleWork(PassOwnPtr<WorkItem> item)
    184 {
    185 }
    186 
    187 void WorkQueue::registerMachPortEventHandler(mach_port_t, MachPortEventType, PassOwnPtr<WorkItem>)
    188 {
    189 }
    190 
    191 void WorkQueue::unregisterMachPortEventHandler(mach_port_t)
    192 {
    193 }
    194 
    195 void WorkQueue::platformInitialize(const char*)
    196 {
    197 }
    198 
    199 void WorkQueue::platformInvalidate()
    200 {
    201 }
    202 
    203 #endif
    204