blob: 895b0bf1b96bce17fe01ec00ffbf8c6b5adfe72f [file] [log] [blame]
henrike@webrtc.org0e118e72013-07-10 00:45:36 +00001
2
3#include "talk/base/macsocketserver.h"
4
5#include "talk/base/common.h"
6#include "talk/base/logging.h"
7#include "talk/base/macasyncsocket.h"
8#include "talk/base/macutils.h"
9#include "talk/base/thread.h"
10
11namespace talk_base {
12
13///////////////////////////////////////////////////////////////////////////////
14// MacBaseSocketServer
15///////////////////////////////////////////////////////////////////////////////
16
17MacBaseSocketServer::MacBaseSocketServer() {
18}
19
20MacBaseSocketServer::~MacBaseSocketServer() {
21}
22
23AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int type) {
24 return CreateAsyncSocket(AF_INET, type);
25}
26
27AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int family, int type) {
28 if (SOCK_STREAM != type)
29 return NULL;
30
31 MacAsyncSocket* socket = new MacAsyncSocket(this, family);
32 if (!socket->valid()) {
33 delete socket;
34 return NULL;
35 }
36 return socket;
37}
38
39void MacBaseSocketServer::RegisterSocket(MacAsyncSocket* s) {
40 sockets_.insert(s);
41}
42
43void MacBaseSocketServer::UnregisterSocket(MacAsyncSocket* s) {
44 VERIFY(1 == sockets_.erase(s)); // found 1
45}
46
47bool MacBaseSocketServer::SetPosixSignalHandler(int signum,
48 void (*handler)(int)) {
49 Dispatcher* dispatcher = signal_dispatcher();
50 if (!PhysicalSocketServer::SetPosixSignalHandler(signum, handler)) {
51 return false;
52 }
53
54 // Only register the FD once, when the first custom handler is installed.
55 if (!dispatcher && (dispatcher = signal_dispatcher())) {
56 CFFileDescriptorContext ctx = { 0 };
57 ctx.info = this;
58
59 CFFileDescriptorRef desc = CFFileDescriptorCreate(
60 kCFAllocatorDefault,
61 dispatcher->GetDescriptor(),
62 false,
63 &MacBaseSocketServer::FileDescriptorCallback,
64 &ctx);
65 if (!desc) {
66 return false;
67 }
68
69 CFFileDescriptorEnableCallBacks(desc, kCFFileDescriptorReadCallBack);
70 CFRunLoopSourceRef ref =
71 CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, desc, 0);
72
73 if (!ref) {
74 CFRelease(desc);
75 return false;
76 }
77
78 CFRunLoopAddSource(CFRunLoopGetCurrent(), ref, kCFRunLoopCommonModes);
79 CFRelease(desc);
80 CFRelease(ref);
81 }
82
83 return true;
84}
85
86// Used to disable socket events from waking our message queue when
87// process_io is false. Does not disable signal event handling though.
88void MacBaseSocketServer::EnableSocketCallbacks(bool enable) {
89 for (std::set<MacAsyncSocket*>::iterator it = sockets().begin();
90 it != sockets().end(); ++it) {
91 if (enable) {
92 (*it)->EnableCallbacks();
93 } else {
94 (*it)->DisableCallbacks();
95 }
96 }
97}
98
99void MacBaseSocketServer::FileDescriptorCallback(CFFileDescriptorRef fd,
100 CFOptionFlags flags,
101 void* context) {
102 MacBaseSocketServer* this_ss =
103 reinterpret_cast<MacBaseSocketServer*>(context);
104 ASSERT(this_ss);
105 Dispatcher* signal_dispatcher = this_ss->signal_dispatcher();
106 ASSERT(signal_dispatcher);
107
108 signal_dispatcher->OnPreEvent(DE_READ);
109 signal_dispatcher->OnEvent(DE_READ, 0);
110 CFFileDescriptorEnableCallBacks(fd, kCFFileDescriptorReadCallBack);
111}
112
113
114///////////////////////////////////////////////////////////////////////////////
115// MacCFSocketServer
116///////////////////////////////////////////////////////////////////////////////
117
118void WakeUpCallback(void* info) {
119 MacCFSocketServer* server = static_cast<MacCFSocketServer*>(info);
120 ASSERT(NULL != server);
121 server->OnWakeUpCallback();
122}
123
124MacCFSocketServer::MacCFSocketServer()
125 : run_loop_(CFRunLoopGetCurrent()),
126 wake_up_(NULL) {
127 CFRunLoopSourceContext ctx;
128 memset(&ctx, 0, sizeof(ctx));
129 ctx.info = this;
130 ctx.perform = &WakeUpCallback;
131 wake_up_ = CFRunLoopSourceCreate(NULL, 0, &ctx);
132 ASSERT(NULL != wake_up_);
133 if (wake_up_) {
134 CFRunLoopAddSource(run_loop_, wake_up_, kCFRunLoopCommonModes);
135 }
136}
137
138MacCFSocketServer::~MacCFSocketServer() {
139 if (wake_up_) {
140 CFRunLoopSourceInvalidate(wake_up_);
141 CFRelease(wake_up_);
142 }
143}
144
145bool MacCFSocketServer::Wait(int cms, bool process_io) {
146 ASSERT(CFRunLoopGetCurrent() == run_loop_);
147
148 if (!process_io && cms == 0) {
149 // No op.
150 return true;
151 }
152
153 if (!process_io) {
154 // No way to listen to common modes and not get socket events, unless
155 // we disable each one's callbacks.
156 EnableSocketCallbacks(false);
157 }
158
159 SInt32 result;
160 if (kForever == cms) {
161 do {
162 // Would prefer to run in a custom mode that only listens to wake_up,
163 // but we have qtkit sending work to the main thread which is effectively
164 // blocked here, causing deadlock. Thus listen to the common modes.
165 // TODO: If QTKit becomes thread safe, do the above.
166 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10000000, false);
167 } while (result != kCFRunLoopRunFinished && result != kCFRunLoopRunStopped);
168 } else {
169 // TODO: In the case of 0ms wait, this will only process one event, so we
170 // may want to loop until it returns TimedOut.
171 CFTimeInterval seconds = cms / 1000.0;
172 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, false);
173 }
174
175 if (!process_io) {
176 // Reenable them. Hopefully this won't cause spurious callbacks or
177 // missing ones while they were disabled.
178 EnableSocketCallbacks(true);
179 }
180
181 if (kCFRunLoopRunFinished == result) {
182 return false;
183 }
184 return true;
185}
186
187void MacCFSocketServer::WakeUp() {
188 if (wake_up_) {
189 CFRunLoopSourceSignal(wake_up_);
190 CFRunLoopWakeUp(run_loop_);
191 }
192}
193
194void MacCFSocketServer::OnWakeUpCallback() {
195 ASSERT(run_loop_ == CFRunLoopGetCurrent());
196 CFRunLoopStop(run_loop_);
197}
198
199///////////////////////////////////////////////////////////////////////////////
200// MacCarbonSocketServer
201///////////////////////////////////////////////////////////////////////////////
202#ifndef CARBON_DEPRECATED
203
204const UInt32 kEventClassSocketServer = 'MCSS';
205const UInt32 kEventWakeUp = 'WAKE';
206const EventTypeSpec kEventWakeUpSpec[] = {
207 { kEventClassSocketServer, kEventWakeUp }
208};
209
210std::string DecodeEvent(EventRef event) {
211 std::string str;
212 DecodeFourChar(::GetEventClass(event), &str);
213 str.push_back(':');
214 DecodeFourChar(::GetEventKind(event), &str);
215 return str;
216}
217
218MacCarbonSocketServer::MacCarbonSocketServer()
219 : event_queue_(GetCurrentEventQueue()), wake_up_(NULL) {
220 VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
221 kEventAttributeUserEvent, &wake_up_));
222}
223
224MacCarbonSocketServer::~MacCarbonSocketServer() {
225 if (wake_up_) {
226 ReleaseEvent(wake_up_);
227 }
228}
229
230bool MacCarbonSocketServer::Wait(int cms, bool process_io) {
231 ASSERT(GetCurrentEventQueue() == event_queue_);
232
233 // Listen to all events if we're processing I/O.
234 // Only listen for our wakeup event if we're not.
235 UInt32 num_types = 0;
236 const EventTypeSpec* events = NULL;
237 if (!process_io) {
238 num_types = GetEventTypeCount(kEventWakeUpSpec);
239 events = kEventWakeUpSpec;
240 }
241
242 EventTargetRef target = GetEventDispatcherTarget();
243 EventTimeout timeout =
244 (kForever == cms) ? kEventDurationForever : cms / 1000.0;
245 EventTimeout end_time = GetCurrentEventTime() + timeout;
246
247 bool done = false;
248 while (!done) {
249 EventRef event;
250 OSStatus result = ReceiveNextEvent(num_types, events, timeout, true,
251 &event);
252 if (noErr == result) {
253 if (wake_up_ != event) {
254 LOG_F(LS_VERBOSE) << "Dispatching event: " << DecodeEvent(event);
255 result = SendEventToEventTarget(event, target);
256 if ((noErr != result) && (eventNotHandledErr != result)) {
257 LOG_E(LS_ERROR, OS, result) << "SendEventToEventTarget";
258 }
259 } else {
260 done = true;
261 }
262 ReleaseEvent(event);
263 } else if (eventLoopTimedOutErr == result) {
264 ASSERT(cms != kForever);
265 done = true;
266 } else if (eventLoopQuitErr == result) {
267 // Ignore this... we get spurious quits for a variety of reasons.
268 LOG_E(LS_VERBOSE, OS, result) << "ReceiveNextEvent";
269 } else {
270 // Some strange error occurred. Log it.
271 LOG_E(LS_WARNING, OS, result) << "ReceiveNextEvent";
272 return false;
273 }
274 if (kForever != cms) {
275 timeout = end_time - GetCurrentEventTime();
276 }
277 }
278 return true;
279}
280
281void MacCarbonSocketServer::WakeUp() {
282 if (!IsEventInQueue(event_queue_, wake_up_)) {
283 RetainEvent(wake_up_);
284 OSStatus result = PostEventToQueue(event_queue_, wake_up_,
285 kEventPriorityStandard);
286 if (noErr != result) {
287 LOG_E(LS_ERROR, OS, result) << "PostEventToQueue";
288 }
289 }
290}
291
292///////////////////////////////////////////////////////////////////////////////
293// MacCarbonAppSocketServer
294///////////////////////////////////////////////////////////////////////////////
295
296MacCarbonAppSocketServer::MacCarbonAppSocketServer()
297 : event_queue_(GetCurrentEventQueue()) {
298 // Install event handler
299 VERIFY(noErr == InstallApplicationEventHandler(
300 NewEventHandlerUPP(WakeUpEventHandler), 1, kEventWakeUpSpec, this,
301 &event_handler_));
302
303 // Install a timer and set it idle to begin with.
304 VERIFY(noErr == InstallEventLoopTimer(GetMainEventLoop(),
305 kEventDurationForever,
306 kEventDurationForever,
307 NewEventLoopTimerUPP(TimerHandler),
308 this,
309 &timer_));
310}
311
312MacCarbonAppSocketServer::~MacCarbonAppSocketServer() {
313 RemoveEventLoopTimer(timer_);
314 RemoveEventHandler(event_handler_);
315}
316
317OSStatus MacCarbonAppSocketServer::WakeUpEventHandler(
318 EventHandlerCallRef next, EventRef event, void *data) {
319 QuitApplicationEventLoop();
320 return noErr;
321}
322
323void MacCarbonAppSocketServer::TimerHandler(
324 EventLoopTimerRef timer, void *data) {
325 QuitApplicationEventLoop();
326}
327
328bool MacCarbonAppSocketServer::Wait(int cms, bool process_io) {
329 if (!process_io && cms == 0) {
330 // No op.
331 return true;
332 }
333 if (kForever != cms) {
334 // Start a timer.
335 OSStatus error =
336 SetEventLoopTimerNextFireTime(timer_, cms / 1000.0);
337 if (error != noErr) {
338 LOG(LS_ERROR) << "Failed setting next fire time.";
339 }
340 }
341 if (!process_io) {
342 // No way to listen to common modes and not get socket events, unless
343 // we disable each one's callbacks.
344 EnableSocketCallbacks(false);
345 }
346 RunApplicationEventLoop();
347 if (!process_io) {
348 // Reenable them. Hopefully this won't cause spurious callbacks or
349 // missing ones while they were disabled.
350 EnableSocketCallbacks(true);
351 }
352 return true;
353}
354
355void MacCarbonAppSocketServer::WakeUp() {
356 // TODO: No-op if there's already a WakeUp in flight.
357 EventRef wake_up;
358 VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
359 kEventAttributeUserEvent, &wake_up));
360 OSStatus result = PostEventToQueue(event_queue_, wake_up,
361 kEventPriorityStandard);
362 if (noErr != result) {
363 LOG_E(LS_ERROR, OS, result) << "PostEventToQueue";
364 }
365 ReleaseEvent(wake_up);
366}
367
368#endif
369} // namespace talk_base