Note: We no longer publish the latest version of our code here. We primarily use a kumc-bmi github organization. The heron ETL repository, in particular, is not public. Peers in the informatics community should see MultiSiteDev for details on requesting access.

source: webrtc/talk/base/macsocketserver.cc @ 0:4bda6873e34c

pub_scrub_3792 tip
Last change on this file since 0:4bda6873e34c was 0:4bda6873e34c, checked in by Michael Prittie <mprittie@…>, 6 years ago

Scrubbed password for publication.

File size: 10.9 KB
Line 
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
11namespace talk_base {
12
13///////////////////////////////////////////////////////////////////////////////
14// MacBaseSocketServer
15///////////////////////////////////////////////////////////////////////////////
16
17MacBaseSocketServer::MacBaseSocketServer() {
18}
19
20MacBaseSocketServer::~MacBaseSocketServer() {
21}
22
23AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int type) {
24  return CreateAsyncSocket(AF_INET, type);
25}
26
27AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int family, int type) {
28  if (SOCK_STREAM != type)
29    return NULL;
30
31  MacAsyncSocket* socket = new MacAsyncSocket(this, family);
32  if (!socket->valid()) {
33    delete socket;
34    return NULL;
35  }
36  return socket;
37}
38
39void MacBaseSocketServer::RegisterSocket(MacAsyncSocket* s) {
40  sockets_.insert(s);
41}
42
43void MacBaseSocketServer::UnregisterSocket(MacAsyncSocket* s) {
44  VERIFY(1 == sockets_.erase(s));   // found 1
45}
46
47bool MacBaseSocketServer::SetPosixSignalHandler(int signum,
48                                                void (*handler)(int)) {
49  Dispatcher* dispatcher = signal_dispatcher();
50  if (!PhysicalSocketServer::SetPosixSignalHandler(signum, handler)) {
51    return false;
52  }
53
54  // Only register the FD once, when the first custom handler is installed.
55  if (!dispatcher && (dispatcher = signal_dispatcher())) {
56    CFFileDescriptorContext ctx = { 0 };
57    ctx.info = this;
58
59    CFFileDescriptorRef desc = CFFileDescriptorCreate(
60        kCFAllocatorDefault,
61        dispatcher->GetDescriptor(),
62        false,
63        &MacBaseSocketServer::FileDescriptorCallback,
64        &ctx);
65    if (!desc) {
66      return false;
67    }
68
69    CFFileDescriptorEnableCallBacks(desc, kCFFileDescriptorReadCallBack);
70    CFRunLoopSourceRef ref =
71        CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, desc, 0);
72
73    if (!ref) {
74      CFRelease(desc);
75      return false;
76    }
77
78    CFRunLoopAddSource(CFRunLoopGetCurrent(), ref, kCFRunLoopCommonModes);
79    CFRelease(desc);
80    CFRelease(ref);
81  }
82
83  return true;
84}
85
86// Used to disable socket events from waking our message queue when
87// process_io is false.  Does not disable signal event handling though.
88void MacBaseSocketServer::EnableSocketCallbacks(bool enable) {
89  for (std::set<MacAsyncSocket*>::iterator it = sockets().begin();
90       it != sockets().end(); ++it) {
91    if (enable) {
92      (*it)->EnableCallbacks();
93    } else {
94      (*it)->DisableCallbacks();
95    }
96  }
97}
98
99void MacBaseSocketServer::FileDescriptorCallback(CFFileDescriptorRef fd,
100                                                 CFOptionFlags flags,
101                                                 void* context) {
102  MacBaseSocketServer* this_ss =
103      reinterpret_cast<MacBaseSocketServer*>(context);
104  ASSERT(this_ss);
105  Dispatcher* signal_dispatcher = this_ss->signal_dispatcher();
106  ASSERT(signal_dispatcher);
107
108  signal_dispatcher->OnPreEvent(DE_READ);
109  signal_dispatcher->OnEvent(DE_READ, 0);
110  CFFileDescriptorEnableCallBacks(fd, kCFFileDescriptorReadCallBack);
111}
112
113
114///////////////////////////////////////////////////////////////////////////////
115// MacCFSocketServer
116///////////////////////////////////////////////////////////////////////////////
117
118void WakeUpCallback(void* info) {
119  MacCFSocketServer* server = static_cast<MacCFSocketServer*>(info);
120  ASSERT(NULL != server);
121  server->OnWakeUpCallback();
122}
123
124MacCFSocketServer::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
138MacCFSocketServer::~MacCFSocketServer() {
139  if (wake_up_) {
140    CFRunLoopSourceInvalidate(wake_up_);
141    CFRelease(wake_up_);
142  }
143}
144
145bool 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
187void MacCFSocketServer::WakeUp() {
188  if (wake_up_) {
189    CFRunLoopSourceSignal(wake_up_);
190    CFRunLoopWakeUp(run_loop_);
191  }
192}
193
194void MacCFSocketServer::OnWakeUpCallback() {
195  ASSERT(run_loop_ == CFRunLoopGetCurrent());
196  CFRunLoopStop(run_loop_);
197}
198
199///////////////////////////////////////////////////////////////////////////////
200// MacCarbonSocketServer
201///////////////////////////////////////////////////////////////////////////////
202#ifndef CARBON_DEPRECATED
203
204const UInt32 kEventClassSocketServer = 'MCSS';
205const UInt32 kEventWakeUp = 'WAKE';
206const EventTypeSpec kEventWakeUpSpec[] = {
207  { kEventClassSocketServer, kEventWakeUp }
208};
209
210std::string DecodeEvent(EventRef event) {
211  std::string str;
212  DecodeFourChar(::GetEventClass(event), &str);
213  str.push_back(':');
214  DecodeFourChar(::GetEventKind(event), &str);
215  return str;
216}
217
218MacCarbonSocketServer::MacCarbonSocketServer()
219    : event_queue_(GetCurrentEventQueue()), wake_up_(NULL) {
220  VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
221                              kEventAttributeUserEvent, &wake_up_));
222}
223
224MacCarbonSocketServer::~MacCarbonSocketServer() {
225  if (wake_up_) {
226    ReleaseEvent(wake_up_);
227  }
228}
229
230bool MacCarbonSocketServer::Wait(int cms, bool process_io) {
231  ASSERT(GetCurrentEventQueue() == event_queue_);
232
233  // Listen to all events if we're processing I/O.
234  // Only listen for our wakeup event if we're not.
235  UInt32 num_types = 0;
236  const EventTypeSpec* events = NULL;
237  if (!process_io) {
238    num_types = GetEventTypeCount(kEventWakeUpSpec);
239    events = kEventWakeUpSpec;
240  }
241
242  EventTargetRef target = GetEventDispatcherTarget();
243  EventTimeout timeout =
244      (kForever == cms) ? kEventDurationForever : cms / 1000.0;
245  EventTimeout end_time = GetCurrentEventTime() + timeout;
246
247  bool done = false;
248  while (!done) {
249    EventRef event;
250    OSStatus result = ReceiveNextEvent(num_types, events, timeout, true,
251                                       &event);
252    if (noErr == result) {
253      if (wake_up_ != event) {
254        LOG_F(LS_VERBOSE) << "Dispatching event: " << DecodeEvent(event);
255        result = SendEventToEventTarget(event, target);
256        if ((noErr != result) && (eventNotHandledErr != result)) {
257          LOG_E(LS_ERROR, OS, result) << "SendEventToEventTarget";
258        }
259      } else {
260        done = true;
261      }
262      ReleaseEvent(event);
263    } else if (eventLoopTimedOutErr == result) {
264      ASSERT(cms != kForever);
265      done = true;
266    } else if (eventLoopQuitErr == result) {
267      // Ignore this... we get spurious quits for a variety of reasons.
268      LOG_E(LS_VERBOSE, OS, result) << "ReceiveNextEvent";
269    } else {
270      // Some strange error occurred. Log it.
271      LOG_E(LS_WARNING, OS, result) << "ReceiveNextEvent";
272      return false;
273    }
274    if (kForever != cms) {
275      timeout = end_time - GetCurrentEventTime();
276    }
277  }
278  return true;
279}
280
281void MacCarbonSocketServer::WakeUp() {
282  if (!IsEventInQueue(event_queue_, wake_up_)) {
283    RetainEvent(wake_up_);
284    OSStatus result = PostEventToQueue(event_queue_, wake_up_,
285                                       kEventPriorityStandard);
286    if (noErr != result) {
287      LOG_E(LS_ERROR, OS, result) << "PostEventToQueue";
288    }
289  }
290}
291
292///////////////////////////////////////////////////////////////////////////////
293// MacCarbonAppSocketServer
294///////////////////////////////////////////////////////////////////////////////
295
296MacCarbonAppSocketServer::MacCarbonAppSocketServer()
297    : event_queue_(GetCurrentEventQueue()) {
298  // Install event handler
299  VERIFY(noErr == InstallApplicationEventHandler(
300      NewEventHandlerUPP(WakeUpEventHandler), 1, kEventWakeUpSpec, this,
301      &event_handler_));
302
303  // Install a timer and set it idle to begin with.
304  VERIFY(noErr == InstallEventLoopTimer(GetMainEventLoop(),
305                                        kEventDurationForever,
306                                        kEventDurationForever,
307                                        NewEventLoopTimerUPP(TimerHandler),
308                                        this,
309                                        &timer_));
310}
311
312MacCarbonAppSocketServer::~MacCarbonAppSocketServer() {
313  RemoveEventLoopTimer(timer_);
314  RemoveEventHandler(event_handler_);
315}
316
317OSStatus MacCarbonAppSocketServer::WakeUpEventHandler(
318    EventHandlerCallRef next, EventRef event, void *data) {
319  QuitApplicationEventLoop();
320  return noErr;
321}
322
323void MacCarbonAppSocketServer::TimerHandler(
324    EventLoopTimerRef timer, void *data) {
325  QuitApplicationEventLoop();
326}
327
328bool MacCarbonAppSocketServer::Wait(int cms, bool process_io) {
329  if (!process_io && cms == 0) {
330    // No op.
331    return true;
332  }
333  if (kForever != cms) {
334    // Start a timer.
335    OSStatus error =
336        SetEventLoopTimerNextFireTime(timer_, cms / 1000.0);
337    if (error != noErr) {
338      LOG(LS_ERROR) << "Failed setting next fire time.";
339    }
340  }
341  if (!process_io) {
342    // No way to listen to common modes and not get socket events, unless
343    // we disable each one's callbacks.
344    EnableSocketCallbacks(false);
345  }
346  RunApplicationEventLoop();
347  if (!process_io) {
348    // Reenable them.  Hopefully this won't cause spurious callbacks or
349    // missing ones while they were disabled.
350    EnableSocketCallbacks(true);
351  }
352  return true;
353}
354
355void MacCarbonAppSocketServer::WakeUp() {
356  // TODO: No-op if there's already a WakeUp in flight.
357  EventRef wake_up;
358  VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
359                              kEventAttributeUserEvent, &wake_up));
360  OSStatus result = PostEventToQueue(event_queue_, wake_up,
361                                       kEventPriorityStandard);
362  if (noErr != result) {
363    LOG_E(LS_ERROR, OS, result) << "PostEventToQueue";
364  }
365  ReleaseEvent(wake_up);
366}
367
368#endif
369} // namespace talk_base
Note: See TracBrowser for help on using the repository browser.