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