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
20namespace rtc {
21
22///////////////////////////////////////////////////////////////////////////////
23// MacBaseSocketServer
24///////////////////////////////////////////////////////////////////////////////
25
26MacBaseSocketServer::MacBaseSocketServer() {
27}
28
29MacBaseSocketServer::~MacBaseSocketServer() {
30}
31
32AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int type) {
33  return CreateAsyncSocket(AF_INET, type);
34}
35
36AsyncSocket* 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
48void MacBaseSocketServer::RegisterSocket(MacAsyncSocket* s) {
49  sockets_.insert(s);
50}
51
52void MacBaseSocketServer::UnregisterSocket(MacAsyncSocket* s) {
53  VERIFY(1 == sockets_.erase(s));   // found 1
54}
55
56bool 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.
97void 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
108void 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
127void WakeUpCallback(void* info) {
128  MacCFSocketServer* server = static_cast<MacCFSocketServer*>(info);
129  ASSERT(NULL != server);
130  server->OnWakeUpCallback();
131}
132
133MacCFSocketServer::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
147MacCFSocketServer::~MacCFSocketServer() {
148  if (wake_up_) {
149    CFRunLoopSourceInvalidate(wake_up_);
150    CFRelease(wake_up_);
151  }
152}
153
154bool 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
196void MacCFSocketServer::WakeUp() {
197  if (wake_up_) {
198    CFRunLoopSourceSignal(wake_up_);
199    CFRunLoopWakeUp(run_loop_);
200  }
201}
202
203void MacCFSocketServer::OnWakeUpCallback() {
204  ASSERT(run_loop_ == CFRunLoopGetCurrent());
205  CFRunLoopStop(run_loop_);
206}
207
208///////////////////////////////////////////////////////////////////////////////
209// MacCarbonSocketServer
210///////////////////////////////////////////////////////////////////////////////
211#ifndef CARBON_DEPRECATED
212
213const UInt32 kEventClassSocketServer = 'MCSS';
214const UInt32 kEventWakeUp = 'WAKE';
215const EventTypeSpec kEventWakeUpSpec[] = {
216  { kEventClassSocketServer, kEventWakeUp }
217};
218
219std::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
227MacCarbonSocketServer::MacCarbonSocketServer()
228    : event_queue_(GetCurrentEventQueue()), wake_up_(NULL) {
229  VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
230                              kEventAttributeUserEvent, &wake_up_));
231}
232
233MacCarbonSocketServer::~MacCarbonSocketServer() {
234  if (wake_up_) {
235    ReleaseEvent(wake_up_);
236  }
237}
238
239bool 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
290void 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
305MacCarbonAppSocketServer::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
321MacCarbonAppSocketServer::~MacCarbonAppSocketServer() {
322  RemoveEventLoopTimer(timer_);
323  RemoveEventHandler(event_handler_);
324}
325
326OSStatus MacCarbonAppSocketServer::WakeUpEventHandler(
327    EventHandlerCallRef next, EventRef event, void *data) {
328  QuitApplicationEventLoop();
329  return noErr;
330}
331
332void MacCarbonAppSocketServer::TimerHandler(
333    EventLoopTimerRef timer, void *data) {
334  QuitApplicationEventLoop();
335}
336
337bool 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
364void 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