1 /*
2 * Copyright 2007 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11
12 #include "webrtc/base/macsocketserver.h"
13
14 #include "webrtc/base/common.h"
15 #include "webrtc/base/logging.h"
16 #include "webrtc/base/macasyncsocket.h"
17 #include "webrtc/base/macutils.h"
18 #include "webrtc/base/thread.h"
19
20 namespace rtc {
21
22 ///////////////////////////////////////////////////////////////////////////////
23 // MacBaseSocketServer
24 ///////////////////////////////////////////////////////////////////////////////
25
MacBaseSocketServer()26 MacBaseSocketServer::MacBaseSocketServer() {
27 }
28
~MacBaseSocketServer()29 MacBaseSocketServer::~MacBaseSocketServer() {
30 }
31
CreateAsyncSocket(int type)32 AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int type) {
33 return CreateAsyncSocket(AF_INET, type);
34 }
35
CreateAsyncSocket(int family,int type)36 AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int family, int type) {
37 if (SOCK_STREAM != type)
38 return NULL;
39
40 MacAsyncSocket* socket = new MacAsyncSocket(this, family);
41 if (!socket->valid()) {
42 delete socket;
43 return NULL;
44 }
45 return socket;
46 }
47
RegisterSocket(MacAsyncSocket * s)48 void MacBaseSocketServer::RegisterSocket(MacAsyncSocket* s) {
49 sockets_.insert(s);
50 }
51
UnregisterSocket(MacAsyncSocket * s)52 void MacBaseSocketServer::UnregisterSocket(MacAsyncSocket* s) {
53 VERIFY(1 == sockets_.erase(s)); // found 1
54 }
55
SetPosixSignalHandler(int signum,void (* handler)(int))56 bool MacBaseSocketServer::SetPosixSignalHandler(int signum,
57 void (*handler)(int)) {
58 Dispatcher* dispatcher = signal_dispatcher();
59 if (!PhysicalSocketServer::SetPosixSignalHandler(signum, handler)) {
60 return false;
61 }
62
63 // Only register the FD once, when the first custom handler is installed.
64 if (!dispatcher && (dispatcher = signal_dispatcher())) {
65 CFFileDescriptorContext ctx = { 0 };
66 ctx.info = this;
67
68 CFFileDescriptorRef desc = CFFileDescriptorCreate(
69 kCFAllocatorDefault,
70 dispatcher->GetDescriptor(),
71 false,
72 &MacBaseSocketServer::FileDescriptorCallback,
73 &ctx);
74 if (!desc) {
75 return false;
76 }
77
78 CFFileDescriptorEnableCallBacks(desc, kCFFileDescriptorReadCallBack);
79 CFRunLoopSourceRef ref =
80 CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, desc, 0);
81
82 if (!ref) {
83 CFRelease(desc);
84 return false;
85 }
86
87 CFRunLoopAddSource(CFRunLoopGetCurrent(), ref, kCFRunLoopCommonModes);
88 CFRelease(desc);
89 CFRelease(ref);
90 }
91
92 return true;
93 }
94
95 // Used to disable socket events from waking our message queue when
96 // process_io is false. Does not disable signal event handling though.
EnableSocketCallbacks(bool enable)97 void MacBaseSocketServer::EnableSocketCallbacks(bool enable) {
98 for (std::set<MacAsyncSocket*>::iterator it = sockets().begin();
99 it != sockets().end(); ++it) {
100 if (enable) {
101 (*it)->EnableCallbacks();
102 } else {
103 (*it)->DisableCallbacks();
104 }
105 }
106 }
107
FileDescriptorCallback(CFFileDescriptorRef fd,CFOptionFlags flags,void * context)108 void MacBaseSocketServer::FileDescriptorCallback(CFFileDescriptorRef fd,
109 CFOptionFlags flags,
110 void* context) {
111 MacBaseSocketServer* this_ss =
112 reinterpret_cast<MacBaseSocketServer*>(context);
113 ASSERT(this_ss);
114 Dispatcher* signal_dispatcher = this_ss->signal_dispatcher();
115 ASSERT(signal_dispatcher);
116
117 signal_dispatcher->OnPreEvent(DE_READ);
118 signal_dispatcher->OnEvent(DE_READ, 0);
119 CFFileDescriptorEnableCallBacks(fd, kCFFileDescriptorReadCallBack);
120 }
121
122
123 ///////////////////////////////////////////////////////////////////////////////
124 // MacCFSocketServer
125 ///////////////////////////////////////////////////////////////////////////////
126
WakeUpCallback(void * info)127 void WakeUpCallback(void* info) {
128 MacCFSocketServer* server = static_cast<MacCFSocketServer*>(info);
129 ASSERT(NULL != server);
130 server->OnWakeUpCallback();
131 }
132
MacCFSocketServer()133 MacCFSocketServer::MacCFSocketServer()
134 : run_loop_(CFRunLoopGetCurrent()),
135 wake_up_(NULL) {
136 CFRunLoopSourceContext ctx;
137 memset(&ctx, 0, sizeof(ctx));
138 ctx.info = this;
139 ctx.perform = &WakeUpCallback;
140 wake_up_ = CFRunLoopSourceCreate(NULL, 0, &ctx);
141 ASSERT(NULL != wake_up_);
142 if (wake_up_) {
143 CFRunLoopAddSource(run_loop_, wake_up_, kCFRunLoopCommonModes);
144 }
145 }
146
~MacCFSocketServer()147 MacCFSocketServer::~MacCFSocketServer() {
148 if (wake_up_) {
149 CFRunLoopSourceInvalidate(wake_up_);
150 CFRelease(wake_up_);
151 }
152 }
153
Wait(int cms,bool process_io)154 bool MacCFSocketServer::Wait(int cms, bool process_io) {
155 ASSERT(CFRunLoopGetCurrent() == run_loop_);
156
157 if (!process_io && cms == 0) {
158 // No op.
159 return true;
160 }
161
162 if (!process_io) {
163 // No way to listen to common modes and not get socket events, unless
164 // we disable each one's callbacks.
165 EnableSocketCallbacks(false);
166 }
167
168 SInt32 result;
169 if (kForever == cms) {
170 do {
171 // Would prefer to run in a custom mode that only listens to wake_up,
172 // but we have qtkit sending work to the main thread which is effectively
173 // blocked here, causing deadlock. Thus listen to the common modes.
174 // TODO: If QTKit becomes thread safe, do the above.
175 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10000000, false);
176 } while (result != kCFRunLoopRunFinished && result != kCFRunLoopRunStopped);
177 } else {
178 // TODO: In the case of 0ms wait, this will only process one event, so we
179 // may want to loop until it returns TimedOut.
180 CFTimeInterval seconds = cms / 1000.0;
181 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, false);
182 }
183
184 if (!process_io) {
185 // Reenable them. Hopefully this won't cause spurious callbacks or
186 // missing ones while they were disabled.
187 EnableSocketCallbacks(true);
188 }
189
190 if (kCFRunLoopRunFinished == result) {
191 return false;
192 }
193 return true;
194 }
195
WakeUp()196 void MacCFSocketServer::WakeUp() {
197 if (wake_up_) {
198 CFRunLoopSourceSignal(wake_up_);
199 CFRunLoopWakeUp(run_loop_);
200 }
201 }
202
OnWakeUpCallback()203 void MacCFSocketServer::OnWakeUpCallback() {
204 ASSERT(run_loop_ == CFRunLoopGetCurrent());
205 CFRunLoopStop(run_loop_);
206 }
207
208 ///////////////////////////////////////////////////////////////////////////////
209 // MacCarbonSocketServer
210 ///////////////////////////////////////////////////////////////////////////////
211 #ifndef CARBON_DEPRECATED
212
213 const UInt32 kEventClassSocketServer = 'MCSS';
214 const UInt32 kEventWakeUp = 'WAKE';
215 const EventTypeSpec kEventWakeUpSpec[] = {
216 { kEventClassSocketServer, kEventWakeUp }
217 };
218
DecodeEvent(EventRef event)219 std::string DecodeEvent(EventRef event) {
220 std::string str;
221 DecodeFourChar(::GetEventClass(event), &str);
222 str.push_back(':');
223 DecodeFourChar(::GetEventKind(event), &str);
224 return str;
225 }
226
MacCarbonSocketServer()227 MacCarbonSocketServer::MacCarbonSocketServer()
228 : event_queue_(GetCurrentEventQueue()), wake_up_(NULL) {
229 VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
230 kEventAttributeUserEvent, &wake_up_));
231 }
232
~MacCarbonSocketServer()233 MacCarbonSocketServer::~MacCarbonSocketServer() {
234 if (wake_up_) {
235 ReleaseEvent(wake_up_);
236 }
237 }
238
Wait(int cms,bool process_io)239 bool MacCarbonSocketServer::Wait(int cms, bool process_io) {
240 ASSERT(GetCurrentEventQueue() == event_queue_);
241
242 // Listen to all events if we're processing I/O.
243 // Only listen for our wakeup event if we're not.
244 UInt32 num_types = 0;
245 const EventTypeSpec* events = NULL;
246 if (!process_io) {
247 num_types = GetEventTypeCount(kEventWakeUpSpec);
248 events = kEventWakeUpSpec;
249 }
250
251 EventTargetRef target = GetEventDispatcherTarget();
252 EventTimeout timeout =
253 (kForever == cms) ? kEventDurationForever : cms / 1000.0;
254 EventTimeout end_time = GetCurrentEventTime() + timeout;
255
256 bool done = false;
257 while (!done) {
258 EventRef event;
259 OSStatus result = ReceiveNextEvent(num_types, events, timeout, true,
260 &event);
261 if (noErr == result) {
262 if (wake_up_ != event) {
263 LOG_F(LS_VERBOSE) << "Dispatching event: " << DecodeEvent(event);
264 result = SendEventToEventTarget(event, target);
265 if ((noErr != result) && (eventNotHandledErr != result)) {
266 LOG_E(LS_ERROR, OS, result) << "SendEventToEventTarget";
267 }
268 } else {
269 done = true;
270 }
271 ReleaseEvent(event);
272 } else if (eventLoopTimedOutErr == result) {
273 ASSERT(cms != kForever);
274 done = true;
275 } else if (eventLoopQuitErr == result) {
276 // Ignore this... we get spurious quits for a variety of reasons.
277 LOG_E(LS_VERBOSE, OS, result) << "ReceiveNextEvent";
278 } else {
279 // Some strange error occurred. Log it.
280 LOG_E(LS_WARNING, OS, result) << "ReceiveNextEvent";
281 return false;
282 }
283 if (kForever != cms) {
284 timeout = end_time - GetCurrentEventTime();
285 }
286 }
287 return true;
288 }
289
WakeUp()290 void MacCarbonSocketServer::WakeUp() {
291 if (!IsEventInQueue(event_queue_, wake_up_)) {
292 RetainEvent(wake_up_);
293 OSStatus result = PostEventToQueue(event_queue_, wake_up_,
294 kEventPriorityStandard);
295 if (noErr != result) {
296 LOG_E(LS_ERROR, OS, result) << "PostEventToQueue";
297 }
298 }
299 }
300
301 ///////////////////////////////////////////////////////////////////////////////
302 // MacCarbonAppSocketServer
303 ///////////////////////////////////////////////////////////////////////////////
304
MacCarbonAppSocketServer()305 MacCarbonAppSocketServer::MacCarbonAppSocketServer()
306 : event_queue_(GetCurrentEventQueue()) {
307 // Install event handler
308 VERIFY(noErr == InstallApplicationEventHandler(
309 NewEventHandlerUPP(WakeUpEventHandler), 1, kEventWakeUpSpec, this,
310 &event_handler_));
311
312 // Install a timer and set it idle to begin with.
313 VERIFY(noErr == InstallEventLoopTimer(GetMainEventLoop(),
314 kEventDurationForever,
315 kEventDurationForever,
316 NewEventLoopTimerUPP(TimerHandler),
317 this,
318 &timer_));
319 }
320
~MacCarbonAppSocketServer()321 MacCarbonAppSocketServer::~MacCarbonAppSocketServer() {
322 RemoveEventLoopTimer(timer_);
323 RemoveEventHandler(event_handler_);
324 }
325
WakeUpEventHandler(EventHandlerCallRef next,EventRef event,void * data)326 OSStatus MacCarbonAppSocketServer::WakeUpEventHandler(
327 EventHandlerCallRef next, EventRef event, void *data) {
328 QuitApplicationEventLoop();
329 return noErr;
330 }
331
TimerHandler(EventLoopTimerRef timer,void * data)332 void MacCarbonAppSocketServer::TimerHandler(
333 EventLoopTimerRef timer, void *data) {
334 QuitApplicationEventLoop();
335 }
336
Wait(int cms,bool process_io)337 bool MacCarbonAppSocketServer::Wait(int cms, bool process_io) {
338 if (!process_io && cms == 0) {
339 // No op.
340 return true;
341 }
342 if (kForever != cms) {
343 // Start a timer.
344 OSStatus error =
345 SetEventLoopTimerNextFireTime(timer_, cms / 1000.0);
346 if (error != noErr) {
347 LOG(LS_ERROR) << "Failed setting next fire time.";
348 }
349 }
350 if (!process_io) {
351 // No way to listen to common modes and not get socket events, unless
352 // we disable each one's callbacks.
353 EnableSocketCallbacks(false);
354 }
355 RunApplicationEventLoop();
356 if (!process_io) {
357 // Reenable them. Hopefully this won't cause spurious callbacks or
358 // missing ones while they were disabled.
359 EnableSocketCallbacks(true);
360 }
361 return true;
362 }
363
WakeUp()364 void MacCarbonAppSocketServer::WakeUp() {
365 // TODO: No-op if there's already a WakeUp in flight.
366 EventRef wake_up;
367 VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
368 kEventAttributeUserEvent, &wake_up));
369 OSStatus result = PostEventToQueue(event_queue_, wake_up,
370 kEventPriorityStandard);
371 if (noErr != result) {
372 LOG_E(LS_ERROR, OS, result) << "PostEventToQueue";
373 }
374 ReleaseEvent(wake_up);
375 }
376
377 #endif
378 } // namespace rtc
379