blob: 123ffdc5255a6bfa7279f950232f58c569ec90ac [file] [log] [blame]
henrike@webrtc.orgf7795df2014-05-13 18:00:26 +00001/*
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
60namespace rtc {
61
62MacCocoaSocketServer::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
71MacCocoaSocketServer::~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!
80bool 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.
124void 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