In NetworkCallbackController::Unschedule*Stream(), don't proceed if the streams are...
[macgdbp.git] / Source / NetworkCallbackController.mm
1 /*
2 * MacGDBp
3 * Copyright (c) 2007 - 2011, Blue Static <http://www.bluestatic.org>
4 *
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17 #import "NetworkCallbackController.h"
18
19 #import <sys/socket.h>
20 #import <netinet/in.h>
21
22 #import "NetworkConnection.h"
23 #import "NetworkConnectionPrivate.h"
24
25 NetworkCallbackController::NetworkCallbackController(NetworkConnection* connection)
26 : connection_(connection),
27 runLoop_(CFRunLoopGetCurrent())
28 {
29 }
30
31 void NetworkCallbackController::OpenConnection(NSUInteger port)
32 {
33 // Pass ourselves to the callback so we don't have to use ugly globals.
34 CFSocketContext context = { 0 };
35 context.info = this;
36
37 // Create the address structure.
38 struct sockaddr_in address;
39 memset(&address, 0, sizeof(address));
40 address.sin_len = sizeof(address);
41 address.sin_family = AF_INET;
42 address.sin_port = htons(port);
43 address.sin_addr.s_addr = htonl(INADDR_ANY);
44
45 // Create the socket signature.
46 CFSocketSignature signature;
47 signature.protocolFamily = PF_INET;
48 signature.socketType = SOCK_STREAM;
49 signature.protocol = IPPROTO_TCP;
50 signature.address = (CFDataRef)[NSData dataWithBytes:&address length:sizeof(address)];
51
52 do {
53 socket_ = CFSocketCreateWithSocketSignature(kCFAllocatorDefault,
54 &signature, // Socket signature.
55 kCFSocketAcceptCallBack, // Callback types.
56 &NetworkCallbackController::SocketAcceptCallback, // Callout function pointer.
57 &context); // Context to pass to callout.
58 if (!socket_) {
59 [connection_ errorEncountered:@"Could not open socket."];
60 sleep(1);
61 }
62 } while (!socket_);
63
64 // Allow old, yet-to-be recycled sockets to be reused.
65 BOOL yes = YES;
66 setsockopt(CFSocketGetNative(socket_), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(BOOL));
67 setsockopt(CFSocketGetNative(socket_), SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(BOOL));
68
69 // Schedule the socket on the run loop.
70 CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 0);
71 CFRunLoopAddSource(runLoop_, source, kCFRunLoopCommonModes);
72 CFRelease(source);
73 }
74
75 void NetworkCallbackController::CloseConnection()
76 {
77 if (socket_) {
78 CFSocketInvalidate(socket_);
79 CFRelease(socket_);
80 socket_ = NULL;
81 }
82 UnscheduleReadStream();
83 UnscheduleWriteStream();
84 }
85
86 // Static Methods //////////////////////////////////////////////////////////////
87
88 void NetworkCallbackController::SocketAcceptCallback(CFSocketRef socket,
89 CFSocketCallBackType callbackType,
90 CFDataRef address,
91 const void* data,
92 void* self)
93 {
94 assert(callbackType == kCFSocketAcceptCallBack);
95 static_cast<NetworkCallbackController*>(self)->OnSocketAccept(socket, address, data);
96 }
97
98 void NetworkCallbackController::ReadStreamCallback(CFReadStreamRef stream,
99 CFStreamEventType eventType,
100 void* self)
101 {
102 static_cast<NetworkCallbackController*>(self)->OnReadStreamEvent(stream, eventType);
103 }
104
105 void NetworkCallbackController::WriteStreamCallback(CFWriteStreamRef stream,
106 CFStreamEventType eventType,
107 void* self)
108 {
109 static_cast<NetworkCallbackController*>(self)->OnWriteStreamEvent(stream, eventType);
110 }
111
112
113 // Private Instance Methods ////////////////////////////////////////////////////
114
115 void NetworkCallbackController::OnSocketAccept(CFSocketRef socket,
116 CFDataRef address,
117 const void* data)
118 {
119 CFReadStreamRef readStream;
120 CFWriteStreamRef writeStream;
121
122 // Create the streams on the socket.
123 CFStreamCreatePairWithSocket(kCFAllocatorDefault,
124 *(CFSocketNativeHandle*)data, // Socket handle.
125 &readStream, // Read stream in-pointer.
126 &writeStream); // Write stream in-pointer.
127
128 // Create struct to register callbacks for the stream.
129 CFStreamClientContext context = { 0 };
130 context.info = this;
131
132 // Set the client of the read stream.
133 CFOptionFlags readFlags = kCFStreamEventOpenCompleted |
134 kCFStreamEventHasBytesAvailable |
135 kCFStreamEventErrorOccurred |
136 kCFStreamEventEndEncountered;
137 if (CFReadStreamSetClient(readStream, readFlags, &NetworkCallbackController::ReadStreamCallback, &context))
138 // Schedule in run loop to do asynchronous communication with the engine.
139 CFReadStreamScheduleWithRunLoop(readStream, runLoop_, kCFRunLoopCommonModes);
140 else
141 return;
142
143 // Open the stream now that it's scheduled on the run loop.
144 if (!CFReadStreamOpen(readStream)) {
145 ReportError(CFReadStreamCopyError(readStream));
146 return;
147 }
148
149 // Set the client of the write stream.
150 CFOptionFlags writeFlags = kCFStreamEventOpenCompleted |
151 kCFStreamEventCanAcceptBytes |
152 kCFStreamEventErrorOccurred |
153 kCFStreamEventEndEncountered;
154 if (CFWriteStreamSetClient(writeStream, writeFlags, &NetworkCallbackController::WriteStreamCallback, &context))
155 // Schedule it in the run loop to receive error information.
156 CFWriteStreamScheduleWithRunLoop(writeStream, runLoop_, kCFRunLoopCommonModes);
157 else
158 return;
159
160 // Open the write stream.
161 if (!CFWriteStreamOpen(writeStream)) {
162 ReportError(CFWriteStreamCopyError(writeStream));
163 return;
164 }
165
166 connection_.readStream = readStream;
167 connection_.writeStream = writeStream;
168 [connection_ socketDidAccept];
169 }
170
171 void NetworkCallbackController::OnReadStreamEvent(CFReadStreamRef stream,
172 CFStreamEventType eventType)
173 {
174 switch (eventType)
175 {
176 case kCFStreamEventHasBytesAvailable:
177 if (connection_.readStream)
178 [connection_ readStreamHasData];
179 break;
180
181 case kCFStreamEventErrorOccurred:
182 ReportError(CFReadStreamCopyError(stream));
183 UnscheduleReadStream();
184 break;
185
186 case kCFStreamEventEndEncountered:
187 UnscheduleReadStream();
188 [connection_ socketDisconnected];
189 break;
190 };
191 }
192
193 void NetworkCallbackController::OnWriteStreamEvent(CFWriteStreamRef stream,
194 CFStreamEventType eventType)
195 {
196 switch (eventType)
197 {
198 case kCFStreamEventCanAcceptBytes:
199 [connection_ sendQueuedWrites];
200 break;
201
202 case kCFStreamEventErrorOccurred:
203 ReportError(CFWriteStreamCopyError(stream));
204 UnscheduleWriteStream();
205 break;
206
207 case kCFStreamEventEndEncountered:
208 UnscheduleReadStream();
209 [connection_ socketDisconnected];
210 break;
211 }
212 }
213
214 void NetworkCallbackController::UnscheduleReadStream()
215 {
216 if (!connection_.readStream)
217 return;
218 CFReadStreamUnscheduleFromRunLoop(connection_.readStream, runLoop_, kCFRunLoopCommonModes);
219 CFReadStreamClose(connection_.readStream);
220 CFRelease(connection_.readStream);
221 connection_.readStream = NULL;
222 }
223
224 void NetworkCallbackController::UnscheduleWriteStream()
225 {
226 if (!connection_.writeStream)
227 return;
228 CFWriteStreamUnscheduleFromRunLoop(connection_.writeStream, runLoop_, kCFRunLoopCommonModes);
229 CFWriteStreamClose(connection_.writeStream);
230 CFRelease(connection_.writeStream);
231 connection_.writeStream = NULL;
232 }
233
234 void NetworkCallbackController::ReportError(CFErrorRef error)
235 {
236 [connection_ errorEncountered:[(NSError*)error description]];
237 CFRelease(error);
238 }