henrike@webrtc.org | 0e118e7 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 1 | /* |
| 2 | * libjingle |
| 3 | * Copyright 2012, Google Inc |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions are met: |
| 7 | * |
| 8 | * 1. Redistributions of source code must retain the above copyright notice, |
| 9 | * this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
| 11 | * this list of conditions and the following disclaimer in the documentation |
| 12 | * and/or other materials provided with the distribution. |
| 13 | * 3. The name of the author may not be used to endorse or promote products |
| 14 | * derived from this software without specific prior written permission. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| 19 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | #import "talk/base/maccocoasocketserver.h" |
| 28 | |
| 29 | #import <Foundation/Foundation.h> |
| 30 | #import <AppKit/AppKit.h> |
| 31 | #include <assert.h> |
| 32 | |
| 33 | #include "talk/base/scoped_autorelease_pool.h" |
| 34 | |
| 35 | // MacCocoaSocketServerHelper serves as a delegate to NSMachPort or a target for |
| 36 | // a timeout. |
| 37 | @interface MacCocoaSocketServerHelper : NSObject { |
| 38 | // This is a weak reference. This works fine since the |
| 39 | // talk_base::MacCocoaSocketServer owns this object. |
| 40 | talk_base::MacCocoaSocketServer* socketServer_; // Weak. |
| 41 | } |
| 42 | @end |
| 43 | |
| 44 | @implementation MacCocoaSocketServerHelper |
| 45 | - (id)initWithSocketServer:(talk_base::MacCocoaSocketServer*)ss { |
| 46 | self = [super init]; |
| 47 | if (self) { |
| 48 | socketServer_ = ss; |
| 49 | } |
| 50 | return self; |
| 51 | } |
| 52 | |
| 53 | - (void)timerFired:(NSTimer*)timer { |
| 54 | socketServer_->WakeUp(); |
| 55 | } |
wu@webrtc.org | 8a77f5b | 2014-02-13 23:18:49 +0000 | [diff] [blame] | 56 | |
| 57 | - (void)breakMainloop { |
| 58 | [NSApp stop:self]; |
| 59 | // NSApp stop only exits after finishing processing of the |
| 60 | // current event. Since we're potentially in a timer callback |
| 61 | // and not an NSEvent handler, we need to trigger a dummy one |
| 62 | // and turn the loop over. We may be able to skip this if we're |
| 63 | // on the ss' thread and not inside the app loop already. |
| 64 | NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined |
| 65 | location:NSMakePoint(0,0) |
| 66 | modifierFlags:0 |
| 67 | timestamp:0 |
| 68 | windowNumber:0 |
| 69 | context:nil |
| 70 | subtype:0 |
| 71 | data1:0 |
| 72 | data2:0]; |
| 73 | [NSApp postEvent:event atStart:NO]; |
| 74 | } |
henrike@webrtc.org | 0e118e7 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 75 | @end |
| 76 | |
| 77 | namespace talk_base { |
| 78 | |
| 79 | MacCocoaSocketServer::MacCocoaSocketServer() { |
| 80 | helper_ = [[MacCocoaSocketServerHelper alloc] initWithSocketServer:this]; |
| 81 | timer_ = nil; |
wu@webrtc.org | 8a77f5b | 2014-02-13 23:18:49 +0000 | [diff] [blame] | 82 | run_count_ = 0; |
henrike@webrtc.org | 0e118e7 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 83 | |
| 84 | // Initialize the shared NSApplication |
| 85 | [NSApplication sharedApplication]; |
| 86 | } |
| 87 | |
| 88 | MacCocoaSocketServer::~MacCocoaSocketServer() { |
| 89 | [timer_ invalidate]; |
| 90 | [timer_ release]; |
| 91 | [helper_ release]; |
| 92 | } |
| 93 | |
wu@webrtc.org | 8a77f5b | 2014-02-13 23:18:49 +0000 | [diff] [blame] | 94 | // ::Wait is reentrant, for example when blocking on another thread while |
| 95 | // responding to I/O. Calls to [NSApp] MUST be made from the main thread |
| 96 | // only! |
henrike@webrtc.org | 0e118e7 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 97 | bool MacCocoaSocketServer::Wait(int cms, bool process_io) { |
| 98 | talk_base::ScopedAutoreleasePool pool; |
| 99 | if (!process_io && cms == 0) { |
| 100 | // No op. |
| 101 | return true; |
| 102 | } |
wu@webrtc.org | 8a77f5b | 2014-02-13 23:18:49 +0000 | [diff] [blame] | 103 | if ([NSApp isRunning]) { |
| 104 | // Only allow reentrant waiting if we're in a blocking send. |
| 105 | ASSERT(!process_io && cms == kForever); |
| 106 | } |
henrike@webrtc.org | 0e118e7 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 107 | |
| 108 | if (!process_io) { |
| 109 | // No way to listen to common modes and not get socket events, unless |
| 110 | // we disable each one's callbacks. |
| 111 | EnableSocketCallbacks(false); |
| 112 | } |
| 113 | |
| 114 | if (kForever != cms) { |
| 115 | // Install a timer that fires wakeup after cms has elapsed. |
| 116 | timer_ = |
| 117 | [NSTimer scheduledTimerWithTimeInterval:cms / 1000.0 |
| 118 | target:helper_ |
| 119 | selector:@selector(timerFired:) |
| 120 | userInfo:nil |
| 121 | repeats:NO]; |
| 122 | [timer_ retain]; |
| 123 | } |
| 124 | |
| 125 | // Run until WakeUp is called, which will call stop and exit this loop. |
wu@webrtc.org | 8a77f5b | 2014-02-13 23:18:49 +0000 | [diff] [blame] | 126 | run_count_++; |
henrike@webrtc.org | 0e118e7 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 127 | [NSApp run]; |
wu@webrtc.org | 8a77f5b | 2014-02-13 23:18:49 +0000 | [diff] [blame] | 128 | run_count_--; |
henrike@webrtc.org | 0e118e7 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 129 | |
| 130 | if (!process_io) { |
| 131 | // Reenable them. Hopefully this won't cause spurious callbacks or |
| 132 | // missing ones while they were disabled. |
| 133 | EnableSocketCallbacks(true); |
| 134 | } |
| 135 | |
| 136 | return true; |
| 137 | } |
| 138 | |
wu@webrtc.org | 8a77f5b | 2014-02-13 23:18:49 +0000 | [diff] [blame] | 139 | // Can be called from any thread. Post a message back to the main thread to |
| 140 | // break out of the NSApp loop. |
henrike@webrtc.org | 0e118e7 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 141 | void MacCocoaSocketServer::WakeUp() { |
wu@webrtc.org | 8a77f5b | 2014-02-13 23:18:49 +0000 | [diff] [blame] | 142 | if (timer_ != nil) { |
| 143 | [timer_ invalidate]; |
| 144 | [timer_ release]; |
| 145 | timer_ = nil; |
| 146 | } |
henrike@webrtc.org | 0e118e7 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 147 | |
wu@webrtc.org | 8a77f5b | 2014-02-13 23:18:49 +0000 | [diff] [blame] | 148 | // [NSApp isRunning] returns unexpected results when called from another |
| 149 | // thread. Maintain our own count of how many times to break the main loop. |
| 150 | if (run_count_ > 0) { |
| 151 | [helper_ performSelectorOnMainThread:@selector(breakMainloop) |
| 152 | withObject:nil |
| 153 | waitUntilDone:false]; |
| 154 | } |
henrike@webrtc.org | 0e118e7 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 155 | } |
| 156 | |
| 157 | } // namespace talk_base |