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