Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / base / maccocoasocketserver.mm
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 }
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 }
75 @end
76
77 namespace talk_base {
78
79 MacCocoaSocketServer::MacCocoaSocketServer() {
80   helper_ = [[MacCocoaSocketServerHelper alloc] initWithSocketServer:this];
81   timer_ = nil;
82   run_count_ = 0;
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
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!
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   }
103   if ([NSApp isRunning]) {
104     // Only allow reentrant waiting if we're in a blocking send.
105     ASSERT(!process_io && cms == kForever);
106   }
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.
126   run_count_++;
127   [NSApp run];
128   run_count_--;
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
139 // Can be called from any thread.  Post a message back to the main thread to
140 // break out of the NSApp loop.
141 void MacCocoaSocketServer::WakeUp() {
142   if (timer_ != nil) {
143     [timer_ invalidate];
144     [timer_ release];
145     timer_ = nil;
146   }
147
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   }
155 }
156
157 }  // namespace talk_base