• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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