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
executeWorkItem(void * item)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
scheduleWork(PassOwnPtr<WorkItem> item)48 void WorkQueue::scheduleWork(PassOwnPtr<WorkItem> item)
49 {
50 dispatch_async_f(m_dispatchQueue, item.leakPtr(), executeWorkItem);
51 }
52
scheduleWorkAfterDelay(PassOwnPtr<WorkItem> item,double delay)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:
EventSource(MachPortEventType eventType,dispatch_source_t dispatchSource,PassOwnPtr<WorkItem> workItem)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
dispatchSource() const69 dispatch_source_t dispatchSource() const { return m_dispatchSource; }
70
eventHandler(void * source)71 static void eventHandler(void* source)
72 {
73 EventSource* eventSource = static_cast<EventSource*>(source);
74
75 eventSource->m_workItem->execute();
76 }
77
cancelHandler(void * source)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
finalizeHandler(void * source)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
registerMachPortEventHandler(mach_port_t machPort,MachPortEventType eventType,PassOwnPtr<WorkItem> workItem)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
unregisterMachPortEventHandler(mach_port_t machPort)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
platformInitialize(const char * name)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
platformInvalidate()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
scheduleWork(PassOwnPtr<WorkItem> item)183 void WorkQueue::scheduleWork(PassOwnPtr<WorkItem> item)
184 {
185 }
186
registerMachPortEventHandler(mach_port_t,MachPortEventType,PassOwnPtr<WorkItem>)187 void WorkQueue::registerMachPortEventHandler(mach_port_t, MachPortEventType, PassOwnPtr<WorkItem>)
188 {
189 }
190
unregisterMachPortEventHandler(mach_port_t)191 void WorkQueue::unregisterMachPortEventHandler(mach_port_t)
192 {
193 }
194
platformInitialize(const char *)195 void WorkQueue::platformInitialize(const char*)
196 {
197 }
198
platformInvalidate()199 void WorkQueue::platformInvalidate()
200 {
201 }
202
203 #endif
204