1 /*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "Connection.h"
28
29 #include "ArgumentEncoder.h"
30 #include "WorkItem.h"
31 #include <wtf/RandomNumber.h>
32 #include <wtf/text/WTFString.h>
33
34 using namespace std;
35 // We explicitly don't use the WebCore namespace here because CoreIPC should only use WTF types and
36 // WTF::String is really in WTF.
37 using WTF::String;
38
39 namespace CoreIPC {
40
41 // FIXME: Rename this or use a different constant on windows.
42 static const size_t inlineMessageMaxSize = 4096;
43
createServerAndClientIdentifiers(HANDLE & serverIdentifier,HANDLE & clientIdentifier)44 bool Connection::createServerAndClientIdentifiers(HANDLE& serverIdentifier, HANDLE& clientIdentifier)
45 {
46 String pipeName;
47
48 while (true) {
49 unsigned uniqueID = randomNumber() * std::numeric_limits<unsigned>::max();
50 pipeName = String::format("\\\\.\\pipe\\com.apple.WebKit.%x", uniqueID);
51
52 serverIdentifier = ::CreateNamedPipe(pipeName.charactersWithNullTermination(),
53 PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
54 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, inlineMessageMaxSize, inlineMessageMaxSize,
55 0, 0);
56 if (!serverIdentifier && ::GetLastError() == ERROR_PIPE_BUSY) {
57 // There was already a pipe with this name, try again.
58 continue;
59 }
60
61 break;
62 }
63
64 if (!serverIdentifier)
65 return false;
66
67 clientIdentifier = ::CreateFileW(pipeName.charactersWithNullTermination(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
68 if (!clientIdentifier) {
69 ::CloseHandle(serverIdentifier);
70 return false;
71 }
72
73 DWORD mode = PIPE_READMODE_MESSAGE;
74 if (!::SetNamedPipeHandleState(clientIdentifier, &mode, 0, 0)) {
75 ::CloseHandle(serverIdentifier);
76 ::CloseHandle(clientIdentifier);
77 return false;
78 }
79
80 return true;
81 }
82
platformInitialize(Identifier identifier)83 void Connection::platformInitialize(Identifier identifier)
84 {
85 memset(&m_readState, 0, sizeof(m_readState));
86 m_readState.hEvent = ::CreateEventW(0, FALSE, FALSE, 0);
87
88 memset(&m_writeState, 0, sizeof(m_writeState));
89 m_writeState.hEvent = ::CreateEventW(0, FALSE, FALSE, 0);
90
91 m_connectionPipe = identifier;
92 }
93
platformInvalidate()94 void Connection::platformInvalidate()
95 {
96 if (m_connectionPipe == INVALID_HANDLE_VALUE)
97 return;
98
99 m_isConnected = false;
100
101 m_connectionQueue.unregisterAndCloseHandle(m_readState.hEvent);
102 m_readState.hEvent = 0;
103
104 m_connectionQueue.unregisterAndCloseHandle(m_writeState.hEvent);
105 m_writeState.hEvent = 0;
106
107 ::CloseHandle(m_connectionPipe);
108 m_connectionPipe = INVALID_HANDLE_VALUE;
109 }
110
readEventHandler()111 void Connection::readEventHandler()
112 {
113 if (m_connectionPipe == INVALID_HANDLE_VALUE)
114 return;
115
116 while (true) {
117 // Check if we got some data.
118 DWORD numberOfBytesRead = 0;
119 if (!::GetOverlappedResult(m_connectionPipe, &m_readState, &numberOfBytesRead, FALSE)) {
120 DWORD error = ::GetLastError();
121
122 switch (error) {
123 case ERROR_BROKEN_PIPE:
124 connectionDidClose();
125 return;
126 case ERROR_MORE_DATA: {
127 // Read the rest of the message out of the pipe.
128
129 DWORD bytesToRead = 0;
130 if (!::PeekNamedPipe(m_connectionPipe, 0, 0, 0, 0, &bytesToRead)) {
131 DWORD error = ::GetLastError();
132 if (error == ERROR_BROKEN_PIPE) {
133 connectionDidClose();
134 return;
135 }
136 ASSERT_NOT_REACHED();
137 return;
138 }
139
140 // ::GetOverlappedResult told us there's more data. ::PeekNamedPipe shouldn't
141 // contradict it!
142 ASSERT(bytesToRead);
143 if (!bytesToRead)
144 break;
145
146 m_readBuffer.grow(m_readBuffer.size() + bytesToRead);
147 if (!::ReadFile(m_connectionPipe, m_readBuffer.data() + numberOfBytesRead, bytesToRead, 0, &m_readState)) {
148 DWORD error = ::GetLastError();
149 ASSERT_NOT_REACHED();
150 return;
151 }
152 continue;
153 }
154
155 // FIXME: We should figure out why we're getting this error.
156 case ERROR_IO_INCOMPLETE:
157 return;
158 default:
159 ASSERT_NOT_REACHED();
160 }
161 }
162
163 if (!m_readBuffer.isEmpty()) {
164 // We have a message, let's dispatch it.
165
166 // The messageID is encoded at the end of the buffer.
167 // Note that we assume here that the message is the same size as m_readBuffer. We can
168 // assume this because we always size m_readBuffer to exactly match the size of the message,
169 // either when receiving ERROR_MORE_DATA from ::GetOverlappedResult above or when
170 // ::PeekNamedPipe tells us the size below. We never set m_readBuffer to a size larger
171 // than the message.
172 ASSERT(m_readBuffer.size() >= sizeof(MessageID));
173 size_t realBufferSize = m_readBuffer.size() - sizeof(MessageID);
174
175 unsigned messageID = *reinterpret_cast<unsigned*>(m_readBuffer.data() + realBufferSize);
176
177 processIncomingMessage(MessageID::fromInt(messageID), adoptPtr(new ArgumentDecoder(m_readBuffer.data(), realBufferSize)));
178 }
179
180 // Find out the size of the next message in the pipe (if there is one) so that we can read
181 // it all in one operation. (This is just an optimization to avoid an extra pass through the
182 // loop (if we chose a buffer size that was too small) or allocating extra memory (if we
183 // chose a buffer size that was too large).)
184 DWORD bytesToRead = 0;
185 if (!::PeekNamedPipe(m_connectionPipe, 0, 0, 0, 0, &bytesToRead)) {
186 DWORD error = ::GetLastError();
187 if (error == ERROR_BROKEN_PIPE) {
188 connectionDidClose();
189 return;
190 }
191 ASSERT_NOT_REACHED();
192 }
193 if (!bytesToRead) {
194 // There's no message waiting in the pipe. Schedule a read of the first byte of the
195 // next message. We'll find out the message's actual size when it arrives. (If we
196 // change this to read more than a single byte for performance reasons, we'll have to
197 // deal with m_readBuffer potentially being larger than the message we read after
198 // calling ::GetOverlappedResult above.)
199 bytesToRead = 1;
200 }
201
202 m_readBuffer.resize(bytesToRead);
203
204 // Either read the next available message (which should occur synchronously), or start an
205 // asynchronous read of the next message that becomes available.
206 BOOL result = ::ReadFile(m_connectionPipe, m_readBuffer.data(), m_readBuffer.size(), 0, &m_readState);
207 if (result) {
208 // There was already a message waiting in the pipe, and we read it synchronously.
209 // Process it.
210 continue;
211 }
212
213 DWORD error = ::GetLastError();
214
215 if (error == ERROR_IO_PENDING) {
216 // There are no messages in the pipe currently. readEventHandler will be called again once there is a message.
217 return;
218 }
219
220 if (error == ERROR_MORE_DATA) {
221 // Either a message is available when we didn't think one was, or the message is larger
222 // than ::PeekNamedPipe told us. The former seems far more likely. Probably the message
223 // became available between our calls to ::PeekNamedPipe and ::ReadFile above. Go back
224 // to the top of the loop to use ::GetOverlappedResult to retrieve the available data.
225 continue;
226 }
227
228 // FIXME: We need to handle other errors here.
229 ASSERT_NOT_REACHED();
230 }
231 }
232
writeEventHandler()233 void Connection::writeEventHandler()
234 {
235 if (m_connectionPipe == INVALID_HANDLE_VALUE)
236 return;
237
238 DWORD numberOfBytesWritten = 0;
239 if (!::GetOverlappedResult(m_connectionPipe, &m_writeState, &numberOfBytesWritten, FALSE)) {
240 DWORD error = ::GetLastError();
241 if (error == ERROR_IO_INCOMPLETE) {
242 // FIXME: We should figure out why we're getting this error.
243 return;
244 }
245 if (error == ERROR_BROKEN_PIPE) {
246 connectionDidClose();
247 return;
248 }
249 ASSERT_NOT_REACHED();
250 }
251
252 // The pending write has finished, so we are now done with its arguments. Clearing this member
253 // will allow us to send messages again.
254 m_pendingWriteArguments = 0;
255
256 // Now that the pending write has finished, we can try to send a new message.
257 sendOutgoingMessages();
258 }
259
open()260 bool Connection::open()
261 {
262 // We connected the two ends of the pipe in createServerAndClientIdentifiers.
263 m_isConnected = true;
264
265 // Start listening for read and write state events.
266 m_connectionQueue.registerHandle(m_readState.hEvent, WorkItem::create(this, &Connection::readEventHandler));
267 m_connectionQueue.registerHandle(m_writeState.hEvent, WorkItem::create(this, &Connection::writeEventHandler));
268
269 // Schedule a read.
270 m_connectionQueue.scheduleWork(WorkItem::create(this, &Connection::readEventHandler));
271
272 return true;
273 }
274
platformCanSendOutgoingMessages() const275 bool Connection::platformCanSendOutgoingMessages() const
276 {
277 // We only allow sending one asynchronous message at a time. If we wanted to send more than one
278 // at once, we'd have to use multiple OVERLAPPED structures and hold onto multiple pending
279 // ArgumentEncoders (one of each for each simultaneous asynchronous message).
280 return !m_pendingWriteArguments;
281 }
282
sendOutgoingMessage(MessageID messageID,PassOwnPtr<ArgumentEncoder> arguments)283 bool Connection::sendOutgoingMessage(MessageID messageID, PassOwnPtr<ArgumentEncoder> arguments)
284 {
285 ASSERT(!m_pendingWriteArguments);
286
287 // Just bail if the handle has been closed.
288 if (m_connectionPipe == INVALID_HANDLE_VALUE)
289 return false;
290
291 // We put the message ID last.
292 arguments->encodeUInt32(messageID.toInt());
293
294 // Write the outgoing message.
295
296 if (::WriteFile(m_connectionPipe, arguments->buffer(), arguments->bufferSize(), 0, &m_writeState)) {
297 // We successfully sent this message.
298 return true;
299 }
300
301 DWORD error = ::GetLastError();
302
303 if (error == ERROR_NO_DATA) {
304 // The pipe is being closed.
305 connectionDidClose();
306 return false;
307 }
308
309 if (error != ERROR_IO_PENDING) {
310 ASSERT_NOT_REACHED();
311 return false;
312 }
313
314 // The message will be sent soon. Hold onto the arguments so that they won't be destroyed
315 // before the write completes.
316 m_pendingWriteArguments = arguments;
317
318 // We can only send one asynchronous message at a time (see comment in platformCanSendOutgoingMessages).
319 return false;
320 }
321
322 } // namespace CoreIPC
323