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