/* * Copyright 2012 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #import "webrtc/base/maccocoasocketserver.h" #import #import #include #include "webrtc/base/scoped_autorelease_pool.h" // MacCocoaSocketServerHelperRtc serves as a delegate to NSMachPort or a target for // a timeout. @interface MacCocoaSocketServerHelperRtc : NSObject { // This is a weak reference. This works fine since the // rtc::MacCocoaSocketServer owns this object. rtc::MacCocoaSocketServer* socketServer_; // Weak. } @end @implementation MacCocoaSocketServerHelperRtc - (id)initWithSocketServer:(rtc::MacCocoaSocketServer*)ss { self = [super init]; if (self) { socketServer_ = ss; } return self; } - (void)timerFired:(NSTimer*)timer { socketServer_->WakeUp(); } - (void)breakMainloop { [NSApp stop:self]; // NSApp stop only exits after finishing processing of the // current event. Since we're potentially in a timer callback // and not an NSEvent handler, we need to trigger a dummy one // and turn the loop over. We may be able to skip this if we're // on the ss' thread and not inside the app loop already. NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:0 data1:0 data2:0]; [NSApp postEvent:event atStart:NO]; } @end namespace rtc { MacCocoaSocketServer::MacCocoaSocketServer() { helper_ = [[MacCocoaSocketServerHelperRtc alloc] initWithSocketServer:this]; timer_ = nil; run_count_ = 0; // Initialize the shared NSApplication [NSApplication sharedApplication]; } MacCocoaSocketServer::~MacCocoaSocketServer() { [timer_ invalidate]; [timer_ release]; [helper_ release]; } // ::Wait is reentrant, for example when blocking on another thread while // responding to I/O. Calls to [NSApp] MUST be made from the main thread // only! bool MacCocoaSocketServer::Wait(int cms, bool process_io) { rtc::ScopedAutoreleasePool pool; if (!process_io && cms == 0) { // No op. return true; } if ([NSApp isRunning]) { // Only allow reentrant waiting if we're in a blocking send. ASSERT(!process_io && cms == kForever); } if (!process_io) { // No way to listen to common modes and not get socket events, unless // we disable each one's callbacks. EnableSocketCallbacks(false); } if (kForever != cms) { // Install a timer that fires wakeup after cms has elapsed. timer_ = [NSTimer scheduledTimerWithTimeInterval:cms / 1000.0 target:helper_ selector:@selector(timerFired:) userInfo:nil repeats:NO]; [timer_ retain]; } // Run until WakeUp is called, which will call stop and exit this loop. run_count_++; [NSApp run]; run_count_--; if (!process_io) { // Reenable them. Hopefully this won't cause spurious callbacks or // missing ones while they were disabled. EnableSocketCallbacks(true); } return true; } // Can be called from any thread. Post a message back to the main thread to // break out of the NSApp loop. void MacCocoaSocketServer::WakeUp() { if (timer_ != nil) { [timer_ invalidate]; [timer_ release]; timer_ = nil; } // [NSApp isRunning] returns unexpected results when called from another // thread. Maintain our own count of how many times to break the main loop. if (run_count_ > 0) { [helper_ performSelectorOnMainThread:@selector(breakMainloop) withObject:nil waitUntilDone:false]; } } } // namespace rtc