henrike@webrtc.org | f7795df | 2014-05-13 18:00:26 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2012 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 | #import "webrtc/base/maccocoasocketserver.h" |
| 11 | |
| 12 | #import <Foundation/Foundation.h> |
| 13 | #import <AppKit/AppKit.h> |
| 14 | #include <assert.h> |
| 15 | |
| 16 | #include "webrtc/base/scoped_autorelease_pool.h" |
| 17 | |
| 18 | // MacCocoaSocketServerHelperRtc serves as a delegate to NSMachPort or a target for |
| 19 | // a timeout. |
| 20 | @interface MacCocoaSocketServerHelperRtc : NSObject { |
| 21 | // This is a weak reference. This works fine since the |
| 22 | // rtc::MacCocoaSocketServer owns this object. |
| 23 | rtc::MacCocoaSocketServer* socketServer_; // Weak. |
| 24 | } |
| 25 | @end |
| 26 | |
| 27 | @implementation MacCocoaSocketServerHelperRtc |
| 28 | - (id)initWithSocketServer:(rtc::MacCocoaSocketServer*)ss { |
| 29 | self = [super init]; |
| 30 | if (self) { |
| 31 | socketServer_ = ss; |
| 32 | } |
| 33 | return self; |
| 34 | } |
| 35 | |
| 36 | - (void)timerFired:(NSTimer*)timer { |
| 37 | socketServer_->WakeUp(); |
| 38 | } |
| 39 | |
| 40 | - (void)breakMainloop { |
| 41 | [NSApp stop:self]; |
| 42 | // NSApp stop only exits after finishing processing of the |
| 43 | // current event. Since we're potentially in a timer callback |
| 44 | // and not an NSEvent handler, we need to trigger a dummy one |
| 45 | // and turn the loop over. We may be able to skip this if we're |
| 46 | // on the ss' thread and not inside the app loop already. |
| 47 | NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined |
| 48 | location:NSMakePoint(0,0) |
| 49 | modifierFlags:0 |
| 50 | timestamp:0 |
| 51 | windowNumber:0 |
| 52 | context:nil |
| 53 | subtype:0 |
| 54 | data1:0 |
| 55 | data2:0]; |
| 56 | [NSApp postEvent:event atStart:NO]; |
| 57 | } |
| 58 | @end |
| 59 | |
| 60 | namespace rtc { |
| 61 | |
| 62 | MacCocoaSocketServer::MacCocoaSocketServer() { |
| 63 | helper_ = [[MacCocoaSocketServerHelperRtc alloc] initWithSocketServer:this]; |
| 64 | timer_ = nil; |
| 65 | run_count_ = 0; |
| 66 | |
| 67 | // Initialize the shared NSApplication |
| 68 | [NSApplication sharedApplication]; |
| 69 | } |
| 70 | |
| 71 | MacCocoaSocketServer::~MacCocoaSocketServer() { |
| 72 | [timer_ invalidate]; |
| 73 | [timer_ release]; |
| 74 | [helper_ release]; |
| 75 | } |
| 76 | |
| 77 | // ::Wait is reentrant, for example when blocking on another thread while |
| 78 | // responding to I/O. Calls to [NSApp] MUST be made from the main thread |
| 79 | // only! |
| 80 | bool MacCocoaSocketServer::Wait(int cms, bool process_io) { |
| 81 | rtc::ScopedAutoreleasePool pool; |
| 82 | if (!process_io && cms == 0) { |
| 83 | // No op. |
| 84 | return true; |
| 85 | } |
| 86 | if ([NSApp isRunning]) { |
| 87 | // Only allow reentrant waiting if we're in a blocking send. |
| 88 | ASSERT(!process_io && cms == kForever); |
| 89 | } |
| 90 | |
| 91 | if (!process_io) { |
| 92 | // No way to listen to common modes and not get socket events, unless |
| 93 | // we disable each one's callbacks. |
| 94 | EnableSocketCallbacks(false); |
| 95 | } |
| 96 | |
| 97 | if (kForever != cms) { |
| 98 | // Install a timer that fires wakeup after cms has elapsed. |
| 99 | timer_ = |
| 100 | [NSTimer scheduledTimerWithTimeInterval:cms / 1000.0 |
| 101 | target:helper_ |
| 102 | selector:@selector(timerFired:) |
| 103 | userInfo:nil |
| 104 | repeats:NO]; |
| 105 | [timer_ retain]; |
| 106 | } |
| 107 | |
| 108 | // Run until WakeUp is called, which will call stop and exit this loop. |
| 109 | run_count_++; |
| 110 | [NSApp run]; |
| 111 | run_count_--; |
| 112 | |
| 113 | if (!process_io) { |
| 114 | // Reenable them. Hopefully this won't cause spurious callbacks or |
| 115 | // missing ones while they were disabled. |
| 116 | EnableSocketCallbacks(true); |
| 117 | } |
| 118 | |
| 119 | return true; |
| 120 | } |
| 121 | |
| 122 | // Can be called from any thread. Post a message back to the main thread to |
| 123 | // break out of the NSApp loop. |
| 124 | void MacCocoaSocketServer::WakeUp() { |
| 125 | if (timer_ != nil) { |
| 126 | [timer_ invalidate]; |
| 127 | [timer_ release]; |
| 128 | timer_ = nil; |
| 129 | } |
| 130 | |
| 131 | // [NSApp isRunning] returns unexpected results when called from another |
| 132 | // thread. Maintain our own count of how many times to break the main loop. |
| 133 | if (run_count_ > 0) { |
| 134 | [helper_ performSelectorOnMainThread:@selector(breakMainloop) |
| 135 | withObject:nil |
| 136 | waitUntilDone:false]; |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | } // namespace rtc |