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
34void 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
48void WorkQueue::scheduleWork(PassOwnPtr<WorkItem> item)
49{
50    dispatch_async_f(m_dispatchQueue, item.leakPtr(), executeWorkItem);
51}
52
53void 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
60class WorkQueue::EventSource {
61public:
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
103private:
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
112void 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
146void 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
165void WorkQueue::platformInitialize(const char* name)
166{
167    m_dispatchQueue = dispatch_queue_create(name, 0);
168    dispatch_set_context(m_dispatchQueue, this);
169}
170
171void 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
183void WorkQueue::scheduleWork(PassOwnPtr<WorkItem> item)
184{
185}
186
187void WorkQueue::registerMachPortEventHandler(mach_port_t, MachPortEventType, PassOwnPtr<WorkItem>)
188{
189}
190
191void WorkQueue::unregisterMachPortEventHandler(mach_port_t)
192{
193}
194
195void WorkQueue::platformInitialize(const char*)
196{
197}
198
199void WorkQueue::platformInvalidate()
200{
201}
202
203#endif
204