1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsSocketTransportService2.h"
8 #include "PollableEvent.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/Logging.h"
14 #include "private/pprio.h"
18 # include "ShutdownLayer.h"
28 static PRDescIdentity sPollableEventLayerIdentity
;
29 static PRIOMethods sPollableEventLayerMethods
;
30 static PRIOMethods
* sPollableEventLayerMethodsPtr
= nullptr;
32 static void LazyInitSocket() {
33 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
34 if (sPollableEventLayerMethodsPtr
) {
37 sPollableEventLayerIdentity
= PR_GetUniqueIdentity("PollableEvent Layer");
38 sPollableEventLayerMethods
= *PR_GetDefaultIOMethods();
39 sPollableEventLayerMethodsPtr
= &sPollableEventLayerMethods
;
42 static bool NewTCPSocketPair(PRFileDesc
* fd
[], bool aSetRecvBuff
) {
43 // this is a replacement for PR_NewTCPSocketPair that manually
44 // sets the recv buffer to 64K. A windows bug (1248358)
45 // can result in using an incompatible rwin and window
46 // scale option on localhost pipes if not set before connect.
48 SOCKET_LOG(("NewTCPSocketPair %s a recv buffer tuning\n",
49 aSetRecvBuff
? "with" : "without"));
51 PRFileDesc
* listener
= nullptr;
52 PRFileDesc
* writer
= nullptr;
53 PRFileDesc
* reader
= nullptr;
54 PRSocketOptionData recvBufferOpt
;
55 recvBufferOpt
.option
= PR_SockOpt_RecvBufferSize
;
56 recvBufferOpt
.value
.recv_buffer_size
= 65535;
58 PRSocketOptionData nodelayOpt
;
59 nodelayOpt
.option
= PR_SockOpt_NoDelay
;
60 nodelayOpt
.value
.no_delay
= true;
62 PRSocketOptionData noblockOpt
;
63 noblockOpt
.option
= PR_SockOpt_Nonblocking
;
64 noblockOpt
.value
.non_blocking
= true;
66 listener
= PR_OpenTCPSocket(PR_AF_INET
);
72 PR_SetSocketOption(listener
, &recvBufferOpt
);
74 PR_SetSocketOption(listener
, &nodelayOpt
);
77 memset(&listenAddr
, 0, sizeof(listenAddr
));
78 if ((PR_InitializeNetAddr(PR_IpAddrLoopback
, 0, &listenAddr
) == PR_FAILURE
) ||
79 (PR_Bind(listener
, &listenAddr
) == PR_FAILURE
) ||
80 (PR_GetSockName(listener
, &listenAddr
) ==
81 PR_FAILURE
) || // learn the dynamic port
82 (PR_Listen(listener
, 5) == PR_FAILURE
)) {
86 writer
= PR_OpenTCPSocket(PR_AF_INET
);
91 PR_SetSocketOption(writer
, &recvBufferOpt
);
93 PR_SetSocketOption(writer
, &nodelayOpt
);
94 PR_SetSocketOption(writer
, &noblockOpt
);
96 if (PR_InitializeNetAddr(PR_IpAddrLoopback
, ntohs(listenAddr
.inet
.port
),
97 &writerAddr
) == PR_FAILURE
) {
101 if (PR_Connect(writer
, &writerAddr
, PR_INTERVAL_NO_TIMEOUT
) == PR_FAILURE
) {
102 if ((PR_GetError() != PR_IN_PROGRESS_ERROR
) ||
103 (PR_ConnectContinue(writer
, PR_POLL_WRITE
) == PR_FAILURE
)) {
107 PR_SetFDInheritable(writer
, false);
109 reader
= PR_Accept(listener
, &listenAddr
, PR_MillisecondsToInterval(200));
113 PR_SetFDInheritable(reader
, false);
115 PR_SetSocketOption(reader
, &recvBufferOpt
);
117 PR_SetSocketOption(reader
, &nodelayOpt
);
118 PR_SetSocketOption(reader
, &noblockOpt
);
140 PollableEvent::PollableEvent()
143 MOZ_COUNT_CTOR(PollableEvent
);
144 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
145 // create pair of prfiledesc that can be used as a poll()ble
146 // signal. on windows use a localhost socket pair, and on
149 SOCKET_LOG(("PollableEvent() using pipe\n"));
150 if (PR_CreatePipe(&mReadFD
, &mWriteFD
) == PR_SUCCESS
) {
151 // make the pipe non blocking. NSPR asserts at
152 // trying to use SockOpt here
153 PROsfd fd
= PR_FileDesc2NativeHandle(mReadFD
);
154 int flags
= fcntl(fd
, F_GETFL
, 0);
155 (void)fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
);
156 fd
= PR_FileDesc2NativeHandle(mWriteFD
);
157 flags
= fcntl(fd
, F_GETFL
, 0);
158 (void)fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
);
162 SOCKET_LOG(("PollableEvent() pipe failed\n"));
165 SOCKET_LOG(("PollableEvent() using socket pair\n"));
169 // Try with a increased recv buffer first (bug 1248358).
170 if (NewTCPSocketPair(fd
, true)) {
173 // If the previous fails try without recv buffer increase (bug 1305436).
174 } else if (NewTCPSocketPair(fd
, false)) {
177 // If both fail, try the old version.
178 } else if (PR_NewTCPSocketPair(fd
) == PR_SUCCESS
) {
182 PRSocketOptionData socket_opt
;
183 DebugOnly
<PRStatus
> status
;
184 socket_opt
.option
= PR_SockOpt_NoDelay
;
185 socket_opt
.value
.no_delay
= true;
186 PR_SetSocketOption(mWriteFD
, &socket_opt
);
187 PR_SetSocketOption(mReadFD
, &socket_opt
);
188 socket_opt
.option
= PR_SockOpt_Nonblocking
;
189 socket_opt
.value
.non_blocking
= true;
190 status
= PR_SetSocketOption(mWriteFD
, &socket_opt
);
191 MOZ_ASSERT(status
== PR_SUCCESS
);
192 status
= PR_SetSocketOption(mReadFD
, &socket_opt
);
193 MOZ_ASSERT(status
== PR_SUCCESS
);
196 if (mReadFD
&& mWriteFD
) {
197 // compatibility with LSPs such as McAfee that assume a NSPR
198 // layer for read ala the nspr Pollable Event - Bug 698882. This layer is a
200 PRFileDesc
* topLayer
= PR_CreateIOLayerStub(sPollableEventLayerIdentity
,
201 sPollableEventLayerMethodsPtr
);
203 if (PR_PushIOLayer(fd
[0], PR_TOP_IO_LAYER
, topLayer
) == PR_FAILURE
) {
204 topLayer
->dtor(topLayer
);
206 SOCKET_LOG(("PollableEvent() nspr layer ok\n"));
212 SOCKET_LOG(("PollableEvent() socketpair failed\n"));
216 if (mReadFD
&& mWriteFD
) {
217 // prime the system to deal with races invovled in [dc]tor cycle
218 SOCKET_LOG(("PollableEvent() ctor ok\n"));
220 MarkFirstSignalTimestamp();
221 PR_Write(mWriteFD
, "I", 1);
225 PollableEvent::~PollableEvent() {
226 MOZ_COUNT_DTOR(PollableEvent
);
229 AttachShutdownLayer(mWriteFD
);
235 AttachShutdownLayer(mReadFD
);
241 // we do not record signals on the socket thread
242 // because the socket thread can reliably look at its
243 // own runnable queue before selecting a poll time
244 // this is the "service the network without blocking" comment in
245 // nsSocketTransportService2.cpp
246 bool PollableEvent::Signal() {
247 SOCKET_LOG(("PollableEvent::Signal\n"));
250 SOCKET_LOG(("PollableEvent::Signal Failed on no FD\n"));
254 // On windows poll can hang and this became worse when we introduced the
255 // patch for bug 698882 (see also bug 1292181), therefore we reverted the
256 // behavior on windows to be as before bug 698882, e.g. write to the socket
257 // also if an event dispatch is on the socket thread and writing to the
258 // socket for each event. See bug 1292181.
259 if (OnSocketThread()) {
260 SOCKET_LOG(("PollableEvent::Signal OnSocketThread nop\n"));
266 // To wake up the poll writing once is enough, but for Windows that can cause
267 // hangs so we will write for every event.
268 // For non-Windows systems it is enough to write just once.
276 MarkFirstSignalTimestamp();
279 int32_t status
= PR_Write(mWriteFD
, "M", 1);
280 SOCKET_LOG(("PollableEvent::Signal PR_Write %d\n", status
));
282 NS_WARNING("PollableEvent::Signal Failed\n");
283 SOCKET_LOG(("PollableEvent::Signal Failed\n"));
287 mWriteFailed
= false;
289 return (status
== 1);
292 bool PollableEvent::Clear() {
293 // necessary because of the "dont signal on socket thread" optimization
294 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
296 SOCKET_LOG(("PollableEvent::Clear\n"));
298 if (!mFirstSignalAfterClear
.IsNull()) {
299 SOCKET_LOG(("PollableEvent::Clear time to signal %ums",
300 (uint32_t)(TimeStamp::NowLoRes() - mFirstSignalAfterClear
)
304 mFirstSignalAfterClear
= TimeStamp();
305 mSignalTimestampAdjusted
= false;
309 SOCKET_LOG(("PollableEvent::Clear mReadFD is null\n"));
316 // On Windows we are writing to the socket for each event, to be sure that we
317 // do not have any deadlock read from the socket as much as we can.
319 status
= PR_Read(mReadFD
, buf
, 2048);
320 SOCKET_LOG(("PollableEvent::Clear PR_Read %d\n", status
));
322 SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
326 PRErrorCode code
= PR_GetError();
327 if (code
== PR_WOULD_BLOCK_ERROR
) {
330 SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code
));
336 status
= PR_Read(mReadFD
, buf
, 2048);
337 SOCKET_LOG(("PollableEvent::Clear PR_Read %d\n", status
));
343 SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
348 SOCKET_LOG(("PollableEvent::Clear Unexpected events\n"));
352 PRErrorCode code
= PR_GetError();
353 if (code
== PR_WOULD_BLOCK_ERROR
) {
356 SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code
));
361 void PollableEvent::MarkFirstSignalTimestamp() {
362 if (mFirstSignalAfterClear
.IsNull()) {
363 SOCKET_LOG(("PollableEvent::MarkFirstSignalTimestamp"));
364 mFirstSignalAfterClear
= TimeStamp::NowLoRes();
368 void PollableEvent::AdjustFirstSignalTimestamp() {
369 if (!mSignalTimestampAdjusted
&& !mFirstSignalAfterClear
.IsNull()) {
370 SOCKET_LOG(("PollableEvent::AdjustFirstSignalTimestamp"));
371 mFirstSignalAfterClear
= TimeStamp::NowLoRes();
372 mSignalTimestampAdjusted
= true;
376 bool PollableEvent::IsSignallingAlive(TimeDuration
const& timeout
) {
382 // The timeout would be just a disturbance in a debug build.
385 if (!mSignaled
|| mFirstSignalAfterClear
.IsNull() ||
386 timeout
== TimeDuration()) {
390 TimeDuration delay
= (TimeStamp::NowLoRes() - mFirstSignalAfterClear
);
391 bool timedOut
= delay
> timeout
;
398 } // namespace mozilla