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