| /* |
| * 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 "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 |