w32poll.c (8536B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 * This file implements _PR_MD_PR_POLL for Win32. 8 */ 9 10 /* The default value of FD_SETSIZE is 64. */ 11 #define FD_SETSIZE 1024 12 13 #include "primpl.h" 14 15 #if !defined(_PR_GLOBAL_THREADS_ONLY) 16 17 struct select_data_s { 18 PRInt32 status; 19 PRInt32 error; 20 fd_set *rd, *wt, *ex; 21 const struct timeval* tv; 22 }; 23 24 static void _PR_MD_select_thread(void* cdata) { 25 struct select_data_s* cd = (struct select_data_s*)cdata; 26 27 cd->status = select(0, cd->rd, cd->wt, cd->ex, cd->tv); 28 29 if (cd->status == SOCKET_ERROR) { 30 cd->error = WSAGetLastError(); 31 } 32 } 33 34 int _PR_NTFiberSafeSelect(int nfds, fd_set* readfds, fd_set* writefds, 35 fd_set* exceptfds, const struct timeval* timeout) { 36 PRThread* me = _PR_MD_CURRENT_THREAD(); 37 int ready; 38 39 if (_PR_IS_NATIVE_THREAD(me)) { 40 ready = _MD_SELECT(nfds, readfds, writefds, exceptfds, timeout); 41 } else { 42 /* 43 ** Creating a new thread on each call!! 44 ** I guess web server doesn't use non-block I/O. 45 */ 46 PRThread* selectThread; 47 struct select_data_s data; 48 data.status = 0; 49 data.error = 0; 50 data.rd = readfds; 51 data.wt = writefds; 52 data.ex = exceptfds; 53 data.tv = timeout; 54 55 selectThread = PR_CreateThread(PR_USER_THREAD, _PR_MD_select_thread, &data, 56 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, 57 PR_JOINABLE_THREAD, 0); 58 if (selectThread == NULL) { 59 return -1; 60 } 61 62 PR_JoinThread(selectThread); 63 ready = data.status; 64 if (ready == SOCKET_ERROR) { 65 WSASetLastError(data.error); 66 } 67 } 68 return ready; 69 } 70 71 #endif /* !defined(_PR_GLOBAL_THREADS_ONLY) */ 72 73 PRInt32 _PR_MD_PR_POLL(PRPollDesc* pds, PRIntn npds, PRIntervalTime timeout) { 74 int ready, err; 75 fd_set rd, wt, ex; 76 fd_set *rdp, *wtp, *exp; 77 int nrd, nwt, nex; 78 PRFileDesc* bottom; 79 PRPollDesc *pd, *epd; 80 PRThread* me = _PR_MD_CURRENT_THREAD(); 81 82 struct timeval tv, *tvp = NULL; 83 84 if (_PR_PENDING_INTERRUPT(me)) { 85 me->flags &= ~_PR_INTERRUPT; 86 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); 87 return -1; 88 } 89 90 /* 91 ** Is it an empty set? If so, just sleep for the timeout and return 92 */ 93 if (0 == npds) { 94 PR_Sleep(timeout); 95 return 0; 96 } 97 98 nrd = nwt = nex = 0; 99 FD_ZERO(&rd); 100 FD_ZERO(&wt); 101 FD_ZERO(&ex); 102 103 ready = 0; 104 for (pd = pds, epd = pd + npds; pd < epd; pd++) { 105 SOCKET osfd; 106 PRInt16 in_flags_read = 0, in_flags_write = 0; 107 PRInt16 out_flags_read = 0, out_flags_write = 0; 108 109 if ((NULL != pd->fd) && (0 != pd->in_flags)) { 110 if (pd->in_flags & PR_POLL_READ) { 111 in_flags_read = (pd->fd->methods->poll)( 112 pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_WRITE), &out_flags_read); 113 } 114 if (pd->in_flags & PR_POLL_WRITE) { 115 in_flags_write = (pd->fd->methods->poll)( 116 pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_READ), &out_flags_write); 117 } 118 if ((0 != (in_flags_read & out_flags_read)) || 119 (0 != (in_flags_write & out_flags_write))) { 120 /* this one's ready right now (buffered input) */ 121 if (0 == ready) { 122 /* 123 * We will have to return without calling the 124 * system poll/select function. So zero the 125 * out_flags fields of all the poll descriptors 126 * before this one. 127 */ 128 PRPollDesc* prev; 129 for (prev = pds; prev < pd; prev++) { 130 prev->out_flags = 0; 131 } 132 } 133 ready += 1; 134 pd->out_flags = out_flags_read | out_flags_write; 135 } else { 136 pd->out_flags = 0; /* pre-condition */ 137 /* make sure this is an NSPR supported stack */ 138 bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); 139 /* ignore a socket without PR_NSPR_IO_LAYER available */ 140 141 if ((NULL != bottom) && (_PR_FILEDESC_OPEN == bottom->secret->state)) { 142 if (0 == ready) { 143 osfd = (SOCKET)bottom->secret->md.osfd; 144 if (in_flags_read & PR_POLL_READ) { 145 pd->out_flags |= _PR_POLL_READ_SYS_READ; 146 FD_SET(osfd, &rd); 147 nrd++; 148 } 149 if (in_flags_read & PR_POLL_WRITE) { 150 pd->out_flags |= _PR_POLL_READ_SYS_WRITE; 151 FD_SET(osfd, &wt); 152 nwt++; 153 } 154 if (in_flags_write & PR_POLL_READ) { 155 pd->out_flags |= _PR_POLL_WRITE_SYS_READ; 156 FD_SET(osfd, &rd); 157 nrd++; 158 } 159 if (in_flags_write & PR_POLL_WRITE) { 160 pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE; 161 FD_SET(osfd, &wt); 162 nwt++; 163 } 164 if (pd->in_flags & PR_POLL_EXCEPT) { 165 FD_SET(osfd, &ex); 166 nex++; 167 } 168 } 169 } else { 170 if (0 == ready) { 171 PRPollDesc* prev; 172 for (prev = pds; prev < pd; prev++) { 173 prev->out_flags = 0; 174 } 175 } 176 ready += 1; /* this will cause an abrupt return */ 177 pd->out_flags = PR_POLL_NVAL; /* bogii */ 178 } 179 } 180 } else { 181 pd->out_flags = 0; 182 } 183 } 184 185 if (0 != ready) { 186 return ready; /* no need to block */ 187 } 188 189 /* 190 * FD_SET does nothing if the fd_set's internal fd_array is full. If 191 * nrd, nwt, or nex is greater than FD_SETSIZE, we know FD_SET must 192 * have failed to insert an osfd into the corresponding fd_set, and 193 * therefore we should fail. 194 */ 195 if ((nrd > FD_SETSIZE) || (nwt > FD_SETSIZE) || (nex > FD_SETSIZE)) { 196 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 197 return -1; 198 } 199 200 rdp = (0 == nrd) ? NULL : &rd; 201 wtp = (0 == nwt) ? NULL : &wt; 202 exp = (0 == nex) ? NULL : &ex; 203 204 if ((NULL == rdp) && (NULL == wtp) && (NULL == exp)) { 205 PR_Sleep(timeout); 206 return 0; 207 } 208 209 if (timeout != PR_INTERVAL_NO_TIMEOUT) { 210 PRInt32 ticksPerSecond = PR_TicksPerSecond(); 211 tv.tv_sec = timeout / ticksPerSecond; 212 tv.tv_usec = PR_IntervalToMicroseconds(timeout % ticksPerSecond); 213 tvp = &tv; 214 } 215 216 #if defined(_PR_GLOBAL_THREADS_ONLY) 217 ready = _MD_SELECT(0, rdp, wtp, exp, tvp); 218 #else 219 ready = _PR_NTFiberSafeSelect(0, rdp, wtp, exp, tvp); 220 #endif 221 222 /* 223 ** Now to unravel the select sets back into the client's poll 224 ** descriptor list. Is this possibly an area for pissing away 225 ** a few cycles or what? 226 */ 227 if (ready > 0) { 228 ready = 0; 229 for (pd = pds, epd = pd + npds; pd < epd; pd++) { 230 PRInt16 out_flags = 0; 231 if ((NULL != pd->fd) && (0 != pd->in_flags)) { 232 SOCKET osfd; 233 bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); 234 PR_ASSERT(NULL != bottom); 235 236 osfd = (SOCKET)bottom->secret->md.osfd; 237 238 if (FD_ISSET(osfd, &rd)) { 239 if (pd->out_flags & _PR_POLL_READ_SYS_READ) { 240 out_flags |= PR_POLL_READ; 241 } 242 if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) { 243 out_flags |= PR_POLL_WRITE; 244 } 245 } 246 if (FD_ISSET(osfd, &wt)) { 247 if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) { 248 out_flags |= PR_POLL_READ; 249 } 250 if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) { 251 out_flags |= PR_POLL_WRITE; 252 } 253 } 254 if (FD_ISSET(osfd, &ex)) { 255 out_flags |= PR_POLL_EXCEPT; 256 } 257 } 258 pd->out_flags = out_flags; 259 if (out_flags) { 260 ready++; 261 } 262 } 263 PR_ASSERT(ready > 0); 264 } else if (ready == SOCKET_ERROR) { 265 err = WSAGetLastError(); 266 if (err == WSAENOTSOCK) { 267 /* Find the bad fds */ 268 int optval; 269 int optlen = sizeof(optval); 270 ready = 0; 271 for (pd = pds, epd = pd + npds; pd < epd; pd++) { 272 pd->out_flags = 0; 273 if ((NULL != pd->fd) && (0 != pd->in_flags)) { 274 bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER); 275 if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET, SO_TYPE, 276 (char*)&optval, &optlen) == -1) { 277 PR_ASSERT(WSAGetLastError() == WSAENOTSOCK); 278 if (WSAGetLastError() == WSAENOTSOCK) { 279 pd->out_flags = PR_POLL_NVAL; 280 ready++; 281 } 282 } 283 } 284 } 285 PR_ASSERT(ready > 0); 286 } else { 287 _PR_MD_MAP_SELECT_ERROR(err); 288 } 289 } 290 291 return ready; 292 }