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 AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int type) {
     33   return CreateAsyncSocket(AF_INET, type);
     34 }
     35 
     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 
     48 void MacBaseSocketServer::RegisterSocket(MacAsyncSocket* s) {
     49   sockets_.insert(s);
     50 }
     51 
     52 void MacBaseSocketServer::UnregisterSocket(MacAsyncSocket* s) {
     53   VERIFY(1 == sockets_.erase(s));   // found 1
     54 }
     55 
     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.
     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 
    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 
    127 void WakeUpCallback(void* info) {
    128   MacCFSocketServer* server = static_cast<MacCFSocketServer*>(info);
    129   ASSERT(NULL != server);
    130   server->OnWakeUpCallback();
    131 }
    132 
    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 
    147 MacCFSocketServer::~MacCFSocketServer() {
    148   if (wake_up_) {
    149     CFRunLoopSourceInvalidate(wake_up_);
    150     CFRelease(wake_up_);
    151   }
    152 }
    153 
    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 
    196 void MacCFSocketServer::WakeUp() {
    197   if (wake_up_) {
    198     CFRunLoopSourceSignal(wake_up_);
    199     CFRunLoopWakeUp(run_loop_);
    200   }
    201 }
    202 
    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 
    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 
    227 MacCarbonSocketServer::MacCarbonSocketServer()
    228     : event_queue_(GetCurrentEventQueue()), wake_up_(NULL) {
    229   VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
    230                               kEventAttributeUserEvent, &wake_up_));
    231 }
    232 
    233 MacCarbonSocketServer::~MacCarbonSocketServer() {
    234   if (wake_up_) {
    235     ReleaseEvent(wake_up_);
    236   }
    237 }
    238 
    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 
    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 
    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 
    321 MacCarbonAppSocketServer::~MacCarbonAppSocketServer() {
    322   RemoveEventLoopTimer(timer_);
    323   RemoveEventHandler(event_handler_);
    324 }
    325 
    326 OSStatus MacCarbonAppSocketServer::WakeUpEventHandler(
    327     EventHandlerCallRef next, EventRef event, void *data) {
    328   QuitApplicationEventLoop();
    329   return noErr;
    330 }
    331 
    332 void MacCarbonAppSocketServer::TimerHandler(
    333     EventLoopTimerRef timer, void *data) {
    334   QuitApplicationEventLoop();
    335 }
    336 
    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 
    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