• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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