Home | History | Annotate | Download | only in base
      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 
     11 namespace talk_base {
     12 
     13 ///////////////////////////////////////////////////////////////////////////////
     14 // MacBaseSocketServer
     15 ///////////////////////////////////////////////////////////////////////////////
     16 
     17 MacBaseSocketServer::MacBaseSocketServer() {
     18 }
     19 
     20 MacBaseSocketServer::~MacBaseSocketServer() {
     21 }
     22 
     23 AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int type) {
     24   return CreateAsyncSocket(AF_INET, type);
     25 }
     26 
     27 AsyncSocket* 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 
     39 void MacBaseSocketServer::RegisterSocket(MacAsyncSocket* s) {
     40   sockets_.insert(s);
     41 }
     42 
     43 void MacBaseSocketServer::UnregisterSocket(MacAsyncSocket* s) {
     44   VERIFY(1 == sockets_.erase(s));   // found 1
     45 }
     46 
     47 bool 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.
     88 void 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 
     99 void 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 
    118 void WakeUpCallback(void* info) {
    119   MacCFSocketServer* server = static_cast<MacCFSocketServer*>(info);
    120   ASSERT(NULL != server);
    121   server->OnWakeUpCallback();
    122 }
    123 
    124 MacCFSocketServer::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 
    138 MacCFSocketServer::~MacCFSocketServer() {
    139   if (wake_up_) {
    140     CFRunLoopSourceInvalidate(wake_up_);
    141     CFRelease(wake_up_);
    142   }
    143 }
    144 
    145 bool 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 
    187 void MacCFSocketServer::WakeUp() {
    188   if (wake_up_) {
    189     CFRunLoopSourceSignal(wake_up_);
    190     CFRunLoopWakeUp(run_loop_);
    191   }
    192 }
    193 
    194 void MacCFSocketServer::OnWakeUpCallback() {
    195   ASSERT(run_loop_ == CFRunLoopGetCurrent());
    196   CFRunLoopStop(run_loop_);
    197 }
    198 
    199 ///////////////////////////////////////////////////////////////////////////////
    200 // MacCarbonSocketServer
    201 ///////////////////////////////////////////////////////////////////////////////
    202 #ifndef CARBON_DEPRECATED
    203 
    204 const UInt32 kEventClassSocketServer = 'MCSS';
    205 const UInt32 kEventWakeUp = 'WAKE';
    206 const EventTypeSpec kEventWakeUpSpec[] = {
    207   { kEventClassSocketServer, kEventWakeUp }
    208 };
    209 
    210 std::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 
    218 MacCarbonSocketServer::MacCarbonSocketServer()
    219     : event_queue_(GetCurrentEventQueue()), wake_up_(NULL) {
    220   VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
    221                               kEventAttributeUserEvent, &wake_up_));
    222 }
    223 
    224 MacCarbonSocketServer::~MacCarbonSocketServer() {
    225   if (wake_up_) {
    226     ReleaseEvent(wake_up_);
    227   }
    228 }
    229 
    230 bool 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 
    281 void 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 
    296 MacCarbonAppSocketServer::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 
    312 MacCarbonAppSocketServer::~MacCarbonAppSocketServer() {
    313   RemoveEventLoopTimer(timer_);
    314   RemoveEventHandler(event_handler_);
    315 }
    316 
    317 OSStatus MacCarbonAppSocketServer::WakeUpEventHandler(
    318     EventHandlerCallRef next, EventRef event, void *data) {
    319   QuitApplicationEventLoop();
    320   return noErr;
    321 }
    322 
    323 void MacCarbonAppSocketServer::TimerHandler(
    324     EventLoopTimerRef timer, void *data) {
    325   QuitApplicationEventLoop();
    326 }
    327 
    328 bool 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 
    355 void 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
    370