PollableEvent.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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/. */
  6. #include "nsSocketTransportService2.h"
  7. #include "PollableEvent.h"
  8. #include "mozilla/Assertions.h"
  9. #include "mozilla/DebugOnly.h"
  10. #include "mozilla/Logging.h"
  11. #include "mozilla/net/DNS.h"
  12. #include "prerror.h"
  13. #include "prio.h"
  14. #include "private/pprio.h"
  15. #include "prnetdb.h"
  16. #ifdef XP_WIN
  17. #include "ShutdownLayer.h"
  18. #else
  19. #include <fcntl.h>
  20. #define USEPIPE 1
  21. #endif
  22. using namespace mozilla::net;
  23. namespace mozilla {
  24. namespace net {
  25. #ifndef USEPIPE
  26. static PRDescIdentity sPollableEventLayerIdentity;
  27. static PRIOMethods sPollableEventLayerMethods;
  28. static PRIOMethods *sPollableEventLayerMethodsPtr = nullptr;
  29. static void LazyInitSocket()
  30. {
  31. MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  32. if (sPollableEventLayerMethodsPtr) {
  33. return;
  34. }
  35. sPollableEventLayerIdentity = PR_GetUniqueIdentity("PollableEvent Layer");
  36. sPollableEventLayerMethods = *PR_GetDefaultIOMethods();
  37. sPollableEventLayerMethodsPtr = &sPollableEventLayerMethods;
  38. }
  39. static bool NewTCPSocketPair(PRFileDesc *fd[], bool aSetRecvBuff)
  40. {
  41. // this is a replacement for PR_NewTCPSocketPair that manually
  42. // sets the recv buffer to 64K. A windows bug (1248358)
  43. // can result in using an incompatible rwin and window
  44. // scale option on localhost pipes if not set before connect.
  45. SOCKET_LOG(("NewTCPSocketPair %s a recv buffer tuning\n", aSetRecvBuff ? "with" : "without"));
  46. PRFileDesc *listener = nullptr;
  47. PRFileDesc *writer = nullptr;
  48. PRFileDesc *reader = nullptr;
  49. PRSocketOptionData recvBufferOpt;
  50. recvBufferOpt.option = PR_SockOpt_RecvBufferSize;
  51. recvBufferOpt.value.recv_buffer_size = 65535;
  52. PRSocketOptionData nodelayOpt;
  53. nodelayOpt.option = PR_SockOpt_NoDelay;
  54. nodelayOpt.value.no_delay = true;
  55. PRSocketOptionData noblockOpt;
  56. noblockOpt.option = PR_SockOpt_Nonblocking;
  57. noblockOpt.value.non_blocking = true;
  58. listener = PR_OpenTCPSocket(PR_AF_INET);
  59. if (!listener) {
  60. goto failed;
  61. }
  62. if (aSetRecvBuff) {
  63. PR_SetSocketOption(listener, &recvBufferOpt);
  64. }
  65. PR_SetSocketOption(listener, &nodelayOpt);
  66. PRNetAddr listenAddr;
  67. memset(&listenAddr, 0, sizeof(listenAddr));
  68. if ((PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &listenAddr) == PR_FAILURE) ||
  69. (PR_Bind(listener, &listenAddr) == PR_FAILURE) ||
  70. (PR_GetSockName(listener, &listenAddr) == PR_FAILURE) || // learn the dynamic port
  71. (PR_Listen(listener, 5) == PR_FAILURE)) {
  72. goto failed;
  73. }
  74. writer = PR_OpenTCPSocket(PR_AF_INET);
  75. if (!writer) {
  76. goto failed;
  77. }
  78. if (aSetRecvBuff) {
  79. PR_SetSocketOption(writer, &recvBufferOpt);
  80. }
  81. PR_SetSocketOption(writer, &nodelayOpt);
  82. PR_SetSocketOption(writer, &noblockOpt);
  83. PRNetAddr writerAddr;
  84. if (PR_InitializeNetAddr(PR_IpAddrLoopback, ntohs(listenAddr.inet.port), &writerAddr) == PR_FAILURE) {
  85. goto failed;
  86. }
  87. if (PR_Connect(writer, &writerAddr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
  88. if ((PR_GetError() != PR_IN_PROGRESS_ERROR) ||
  89. (PR_ConnectContinue(writer, PR_POLL_WRITE) == PR_FAILURE)) {
  90. goto failed;
  91. }
  92. }
  93. reader = PR_Accept(listener, &listenAddr, PR_MillisecondsToInterval(200));
  94. if (!reader) {
  95. goto failed;
  96. }
  97. if (aSetRecvBuff) {
  98. PR_SetSocketOption(reader, &recvBufferOpt);
  99. }
  100. PR_SetSocketOption(reader, &nodelayOpt);
  101. PR_SetSocketOption(reader, &noblockOpt);
  102. PR_Close(listener);
  103. fd[0] = reader;
  104. fd[1] = writer;
  105. return true;
  106. failed:
  107. if (listener) {
  108. PR_Close(listener);
  109. }
  110. if (reader) {
  111. PR_Close(reader);
  112. }
  113. if (writer) {
  114. PR_Close(writer);
  115. }
  116. return false;
  117. }
  118. #endif
  119. PollableEvent::PollableEvent()
  120. : mWriteFD(nullptr)
  121. , mReadFD(nullptr)
  122. , mSignaled(false)
  123. {
  124. MOZ_COUNT_CTOR(PollableEvent);
  125. MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  126. // create pair of prfiledesc that can be used as a poll()ble
  127. // signal. on windows use a localhost socket pair, and on
  128. // unix use a pipe.
  129. #ifdef USEPIPE
  130. SOCKET_LOG(("PollableEvent() using pipe\n"));
  131. if (PR_CreatePipe(&mReadFD, &mWriteFD) == PR_SUCCESS) {
  132. // make the pipe non blocking. NSPR asserts at
  133. // trying to use SockOpt here
  134. PROsfd fd = PR_FileDesc2NativeHandle(mReadFD);
  135. int flags = fcntl(fd, F_GETFL, 0);
  136. (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  137. fd = PR_FileDesc2NativeHandle(mWriteFD);
  138. flags = fcntl(fd, F_GETFL, 0);
  139. (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  140. } else {
  141. mReadFD = nullptr;
  142. mWriteFD = nullptr;
  143. SOCKET_LOG(("PollableEvent() pipe failed\n"));
  144. }
  145. #else
  146. SOCKET_LOG(("PollableEvent() using socket pair\n"));
  147. PRFileDesc *fd[2];
  148. LazyInitSocket();
  149. // Try with a increased recv buffer first (bug 1248358).
  150. if (NewTCPSocketPair(fd, true)) {
  151. mReadFD = fd[0];
  152. mWriteFD = fd[1];
  153. // If the previous fails try without recv buffer increase (bug 1305436).
  154. } else if (NewTCPSocketPair(fd, false)) {
  155. mReadFD = fd[0];
  156. mWriteFD = fd[1];
  157. // If both fail, try the old version.
  158. } else if (PR_NewTCPSocketPair(fd) == PR_SUCCESS) {
  159. mReadFD = fd[0];
  160. mWriteFD = fd[1];
  161. PRSocketOptionData socket_opt;
  162. DebugOnly<PRStatus> status;
  163. socket_opt.option = PR_SockOpt_NoDelay;
  164. socket_opt.value.no_delay = true;
  165. PR_SetSocketOption(mWriteFD, &socket_opt);
  166. PR_SetSocketOption(mReadFD, &socket_opt);
  167. socket_opt.option = PR_SockOpt_Nonblocking;
  168. socket_opt.value.non_blocking = true;
  169. status = PR_SetSocketOption(mWriteFD, &socket_opt);
  170. MOZ_ASSERT(status == PR_SUCCESS);
  171. status = PR_SetSocketOption(mReadFD, &socket_opt);
  172. MOZ_ASSERT(status == PR_SUCCESS);
  173. }
  174. if (mReadFD && mWriteFD) {
  175. // compatibility with LSPs such as McAfee that assume a NSPR
  176. // layer for read ala the nspr Pollable Event - Bug 698882. This layer is a nop.
  177. PRFileDesc *topLayer =
  178. PR_CreateIOLayerStub(sPollableEventLayerIdentity,
  179. sPollableEventLayerMethodsPtr);
  180. if (topLayer) {
  181. if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, topLayer) == PR_FAILURE) {
  182. topLayer->dtor(topLayer);
  183. } else {
  184. SOCKET_LOG(("PollableEvent() nspr layer ok\n"));
  185. mReadFD = topLayer;
  186. }
  187. }
  188. } else {
  189. SOCKET_LOG(("PollableEvent() socketpair failed\n"));
  190. }
  191. #endif
  192. if (mReadFD && mWriteFD) {
  193. // prime the system to deal with races invovled in [dc]tor cycle
  194. SOCKET_LOG(("PollableEvent() ctor ok\n"));
  195. mSignaled = true;
  196. PR_Write(mWriteFD, "I", 1);
  197. }
  198. }
  199. PollableEvent::~PollableEvent()
  200. {
  201. MOZ_COUNT_DTOR(PollableEvent);
  202. if (mWriteFD) {
  203. #if defined(XP_WIN)
  204. AttachShutdownLayer(mWriteFD);
  205. #endif
  206. PR_Close(mWriteFD);
  207. }
  208. if (mReadFD) {
  209. #if defined(XP_WIN)
  210. AttachShutdownLayer(mReadFD);
  211. #endif
  212. PR_Close(mReadFD);
  213. }
  214. }
  215. // we do not record signals on the socket thread
  216. // because the socket thread can reliably look at its
  217. // own runnable queue before selecting a poll time
  218. // this is the "service the network without blocking" comment in
  219. // nsSocketTransportService2.cpp
  220. bool
  221. PollableEvent::Signal()
  222. {
  223. SOCKET_LOG(("PollableEvent::Signal\n"));
  224. if (!mWriteFD) {
  225. SOCKET_LOG(("PollableEvent::Signal Failed on no FD\n"));
  226. return false;
  227. }
  228. #ifndef XP_WIN
  229. // On windows poll can hang and this became worse when we introduced the
  230. // patch for bug 698882 (see also bug 1292181), therefore we reverted the
  231. // behavior on windows to be as before bug 698882, e.g. write to the socket
  232. // also if an event dispatch is on the socket thread and writing to the
  233. // socket for each event. See bug 1292181.
  234. if (PR_GetCurrentThread() == gSocketThread) {
  235. SOCKET_LOG(("PollableEvent::Signal OnSocketThread nop\n"));
  236. return true;
  237. }
  238. #endif
  239. #ifndef XP_WIN
  240. // To wake up the poll writing once is enough, but for Windows that can cause
  241. // hangs so we will write for every event.
  242. // For non-Windows systems it is enough to write just once.
  243. if (mSignaled) {
  244. return true;
  245. }
  246. #endif
  247. mSignaled = true;
  248. int32_t status = PR_Write(mWriteFD, "M", 1);
  249. SOCKET_LOG(("PollableEvent::Signal PR_Write %d\n", status));
  250. if (status != 1) {
  251. NS_WARNING("PollableEvent::Signal Failed\n");
  252. SOCKET_LOG(("PollableEvent::Signal Failed\n"));
  253. mSignaled = false;
  254. }
  255. return (status == 1);
  256. }
  257. bool
  258. PollableEvent::Clear()
  259. {
  260. // necessary because of the "dont signal on socket thread" optimization
  261. MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  262. SOCKET_LOG(("PollableEvent::Clear\n"));
  263. mSignaled = false;
  264. if (!mReadFD) {
  265. SOCKET_LOG(("PollableEvent::Clear mReadFD is null\n"));
  266. return false;
  267. }
  268. char buf[2048];
  269. int32_t status;
  270. #ifdef XP_WIN
  271. // On Windows we are writing to the socket for each event, to be sure that we
  272. // do not have any deadlock read from the socket as much as we can.
  273. while (true) {
  274. status = PR_Read(mReadFD, buf, 2048);
  275. SOCKET_LOG(("PollableEvent::Signal PR_Read %d\n", status));
  276. if (status == 0) {
  277. SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
  278. return false;
  279. }
  280. if (status < 0) {
  281. PRErrorCode code = PR_GetError();
  282. if (code == PR_WOULD_BLOCK_ERROR) {
  283. return true;
  284. } else {
  285. SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
  286. return false;
  287. }
  288. }
  289. }
  290. #else
  291. status = PR_Read(mReadFD, buf, 2048);
  292. SOCKET_LOG(("PollableEvent::Signal PR_Read %d\n", status));
  293. if (status == 1) {
  294. return true;
  295. }
  296. if (status == 0) {
  297. SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
  298. return false;
  299. }
  300. if (status > 1) {
  301. MOZ_ASSERT(false);
  302. SOCKET_LOG(("PollableEvent::Clear Unexpected events\n"));
  303. Clear();
  304. return true;
  305. }
  306. PRErrorCode code = PR_GetError();
  307. if (code == PR_WOULD_BLOCK_ERROR) {
  308. return true;
  309. }
  310. SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
  311. return false;
  312. #endif //XP_WIN
  313. }
  314. } // namespace net
  315. } // namespace mozilla