| /* | 
 |  *  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 <Foundation/Foundation.h> | 
 | #import <AppKit/AppKit.h> | 
 | #include <assert.h> | 
 |  | 
 | #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 |