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