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