Bug 1804798 - Explicitly set auto page name (and corresponding debug flag) when inser...
[gecko.git] / netwerk / base / PollableEvent.cpp
blobaf6e052bedf10e44c37a026ae71cc0ba56af230b
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"
12 #include "prerror.h"
13 #include "prio.h"
14 #include "private/pprio.h"
15 #include "prnetdb.h"
17 #ifdef XP_WIN
18 # include "ShutdownLayer.h"
19 #else
20 # include <fcntl.h>
21 # define USEPIPE 1
22 #endif
24 namespace mozilla {
25 namespace net {
27 #ifndef USEPIPE
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) {
35 return;
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);
67 if (!listener) {
68 goto failed;
71 if (aSetRecvBuff) {
72 PR_SetSocketOption(listener, &recvBufferOpt);
74 PR_SetSocketOption(listener, &nodelayOpt);
76 PRNetAddr listenAddr;
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)) {
83 goto failed;
86 writer = PR_OpenTCPSocket(PR_AF_INET);
87 if (!writer) {
88 goto failed;
90 if (aSetRecvBuff) {
91 PR_SetSocketOption(writer, &recvBufferOpt);
93 PR_SetSocketOption(writer, &nodelayOpt);
94 PR_SetSocketOption(writer, &noblockOpt);
95 PRNetAddr writerAddr;
96 if (PR_InitializeNetAddr(PR_IpAddrLoopback, ntohs(listenAddr.inet.port),
97 &writerAddr) == PR_FAILURE) {
98 goto failed;
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)) {
104 goto failed;
107 PR_SetFDInheritable(writer, false);
109 reader = PR_Accept(listener, &listenAddr, PR_MillisecondsToInterval(200));
110 if (!reader) {
111 goto failed;
113 PR_SetFDInheritable(reader, false);
114 if (aSetRecvBuff) {
115 PR_SetSocketOption(reader, &recvBufferOpt);
117 PR_SetSocketOption(reader, &nodelayOpt);
118 PR_SetSocketOption(reader, &noblockOpt);
119 PR_Close(listener);
121 fd[0] = reader;
122 fd[1] = writer;
123 return true;
125 failed:
126 if (listener) {
127 PR_Close(listener);
129 if (reader) {
130 PR_Close(reader);
132 if (writer) {
133 PR_Close(writer);
135 return false;
138 #endif
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
147 // unix use a pipe.
148 #ifdef USEPIPE
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);
159 } else {
160 mReadFD = nullptr;
161 mWriteFD = nullptr;
162 SOCKET_LOG(("PollableEvent() pipe failed\n"));
164 #else
165 SOCKET_LOG(("PollableEvent() using socket pair\n"));
166 PRFileDesc* fd[2];
167 LazyInitSocket();
169 // Try with a increased recv buffer first (bug 1248358).
170 if (NewTCPSocketPair(fd, true)) {
171 mReadFD = fd[0];
172 mWriteFD = fd[1];
173 // If the previous fails try without recv buffer increase (bug 1305436).
174 } else if (NewTCPSocketPair(fd, false)) {
175 mReadFD = fd[0];
176 mWriteFD = fd[1];
177 // If both fail, try the old version.
178 } else if (PR_NewTCPSocketPair(fd) == PR_SUCCESS) {
179 mReadFD = fd[0];
180 mWriteFD = fd[1];
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
199 // nop.
200 PRFileDesc* topLayer = PR_CreateIOLayerStub(sPollableEventLayerIdentity,
201 sPollableEventLayerMethodsPtr);
202 if (topLayer) {
203 if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, topLayer) == PR_FAILURE) {
204 topLayer->dtor(topLayer);
205 } else {
206 SOCKET_LOG(("PollableEvent() nspr layer ok\n"));
207 mReadFD = topLayer;
211 } else {
212 SOCKET_LOG(("PollableEvent() socketpair failed\n"));
214 #endif
216 if (mReadFD && mWriteFD) {
217 // prime the system to deal with races invovled in [dc]tor cycle
218 SOCKET_LOG(("PollableEvent() ctor ok\n"));
219 mSignaled = true;
220 MarkFirstSignalTimestamp();
221 PR_Write(mWriteFD, "I", 1);
225 PollableEvent::~PollableEvent() {
226 MOZ_COUNT_DTOR(PollableEvent);
227 if (mWriteFD) {
228 #if defined(XP_WIN)
229 AttachShutdownLayer(mWriteFD);
230 #endif
231 PR_Close(mWriteFD);
233 if (mReadFD) {
234 #if defined(XP_WIN)
235 AttachShutdownLayer(mReadFD);
236 #endif
237 PR_Close(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"));
249 if (!mWriteFD) {
250 SOCKET_LOG(("PollableEvent::Signal Failed on no FD\n"));
251 return false;
253 #ifndef XP_WIN
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"));
261 return true;
263 #endif
265 #ifndef XP_WIN
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.
269 if (mSignaled) {
270 return true;
272 #endif
274 if (!mSignaled) {
275 mSignaled = true;
276 MarkFirstSignalTimestamp();
279 int32_t status = PR_Write(mWriteFD, "M", 1);
280 SOCKET_LOG(("PollableEvent::Signal PR_Write %d\n", status));
281 if (status != 1) {
282 NS_WARNING("PollableEvent::Signal Failed\n");
283 SOCKET_LOG(("PollableEvent::Signal Failed\n"));
284 mSignaled = false;
285 mWriteFailed = true;
286 } else {
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)
301 .ToMilliseconds()));
304 mFirstSignalAfterClear = TimeStamp();
305 mSignalTimestampAdjusted = false;
306 mSignaled = false;
308 if (!mReadFD) {
309 SOCKET_LOG(("PollableEvent::Clear mReadFD is null\n"));
310 return false;
313 char buf[2048];
314 int32_t status;
315 #ifdef XP_WIN
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.
318 while (true) {
319 status = PR_Read(mReadFD, buf, 2048);
320 SOCKET_LOG(("PollableEvent::Clear PR_Read %d\n", status));
321 if (status == 0) {
322 SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
323 return false;
325 if (status < 0) {
326 PRErrorCode code = PR_GetError();
327 if (code == PR_WOULD_BLOCK_ERROR) {
328 return true;
329 } else {
330 SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
331 return false;
335 #else
336 status = PR_Read(mReadFD, buf, 2048);
337 SOCKET_LOG(("PollableEvent::Clear PR_Read %d\n", status));
339 if (status == 1) {
340 return true;
342 if (status == 0) {
343 SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
344 return false;
346 if (status > 1) {
347 MOZ_ASSERT(false);
348 SOCKET_LOG(("PollableEvent::Clear Unexpected events\n"));
349 Clear();
350 return true;
352 PRErrorCode code = PR_GetError();
353 if (code == PR_WOULD_BLOCK_ERROR) {
354 return true;
356 SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
357 return false;
358 #endif // XP_WIN
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) {
377 if (mWriteFailed) {
378 return false;
381 #ifdef DEBUG
382 // The timeout would be just a disturbance in a debug build.
383 return true;
384 #else
385 if (!mSignaled || mFirstSignalAfterClear.IsNull() ||
386 timeout == TimeDuration()) {
387 return true;
390 TimeDuration delay = (TimeStamp::NowLoRes() - mFirstSignalAfterClear);
391 bool timedOut = delay > timeout;
393 return !timedOut;
394 #endif // DEBUG
397 } // namespace net
398 } // namespace mozilla