root/maint/gnulib/lib/select.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. IsConsoleHandle
  2. IsSocketHandle
  3. windows_poll_handle
  4. rpl_select
  5. rpl_select

   1 /* Emulation for select(2)
   2    Contributed by Paolo Bonzini.
   3 
   4    Copyright 2008-2021 Free Software Foundation, Inc.
   5 
   6    This file is part of gnulib.
   7 
   8    This file is free software: you can redistribute it and/or modify
   9    it under the terms of the GNU Lesser General Public License as
  10    published by the Free Software Foundation; either version 2.1 of the
  11    License, or (at your option) any later version.
  12 
  13    This file is distributed in the hope that it will be useful,
  14    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16    GNU Lesser General Public License for more details.
  17 
  18    You should have received a copy of the GNU Lesser General Public License
  19    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  20 
  21 #include <config.h>
  22 
  23 /* Specification.  */
  24 #include <sys/select.h>
  25 
  26 #if defined _WIN32 && ! defined __CYGWIN__
  27 /* Native Windows.  */
  28 
  29 #include <alloca.h>
  30 #include <assert.h>
  31 #include <sys/types.h>
  32 #include <errno.h>
  33 #include <limits.h>
  34 
  35 #include <winsock2.h>
  36 #include <windows.h>
  37 #include <io.h>
  38 #include <stdio.h>
  39 #include <conio.h>
  40 #include <time.h>
  41 
  42 /* Get the overridden 'struct timeval'.  */
  43 #include <sys/time.h>
  44 
  45 #if GNULIB_MSVC_NOTHROW
  46 # include "msvc-nothrow.h"
  47 #else
  48 # include <io.h>
  49 #endif
  50 
  51 #undef select
  52 
  53 /* Don't assume that UNICODE is not defined.  */
  54 #undef GetModuleHandle
  55 #define GetModuleHandle GetModuleHandleA
  56 #undef PeekConsoleInput
  57 #define PeekConsoleInput PeekConsoleInputA
  58 #undef CreateEvent
  59 #define CreateEvent CreateEventA
  60 #undef PeekMessage
  61 #define PeekMessage PeekMessageA
  62 #undef DispatchMessage
  63 #define DispatchMessage DispatchMessageA
  64 
  65 /* Avoid warnings from gcc -Wcast-function-type.  */
  66 #define GetProcAddress \
  67   (void *) GetProcAddress
  68 
  69 struct bitset {
  70   unsigned char in[FD_SETSIZE / CHAR_BIT];
  71   unsigned char out[FD_SETSIZE / CHAR_BIT];
  72 };
  73 
  74 /* Declare data structures for ntdll functions.  */
  75 typedef struct _FILE_PIPE_LOCAL_INFORMATION {
  76   ULONG NamedPipeType;
  77   ULONG NamedPipeConfiguration;
  78   ULONG MaximumInstances;
  79   ULONG CurrentInstances;
  80   ULONG InboundQuota;
  81   ULONG ReadDataAvailable;
  82   ULONG OutboundQuota;
  83   ULONG WriteQuotaAvailable;
  84   ULONG NamedPipeState;
  85   ULONG NamedPipeEnd;
  86 } FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
  87 
  88 typedef struct _IO_STATUS_BLOCK
  89 {
  90   union {
  91     DWORD Status;
  92     PVOID Pointer;
  93   } u;
  94   ULONG_PTR Information;
  95 } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
  96 
  97 typedef enum _FILE_INFORMATION_CLASS {
  98   FilePipeLocalInformation = 24
  99 } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
 100 
 101 typedef DWORD (WINAPI *PNtQueryInformationFile)
 102          (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
 103 
 104 #ifndef PIPE_BUF
 105 #define PIPE_BUF        512
 106 #endif
 107 
 108 static BOOL IsConsoleHandle (HANDLE h)
     /* [previous][next][first][last][top][bottom][index][help] */
 109 {
 110   DWORD mode;
 111   return GetConsoleMode (h, &mode) != 0;
 112 }
 113 
 114 static BOOL
 115 IsSocketHandle (HANDLE h)
     /* [previous][next][first][last][top][bottom][index][help] */
 116 {
 117   WSANETWORKEVENTS ev;
 118 
 119   if (IsConsoleHandle (h))
 120     return FALSE;
 121 
 122   /* Under Wine, it seems that getsockopt returns 0 for pipes too.
 123      WSAEnumNetworkEvents instead distinguishes the two correctly.  */
 124   ev.lNetworkEvents = 0xDEADBEEF;
 125   WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
 126   return ev.lNetworkEvents != 0xDEADBEEF;
 127 }
 128 
 129 /* Compute output fd_sets for libc descriptor FD (whose Windows handle is
 130    H).  */
 131 
 132 static int
 133 windows_poll_handle (HANDLE h, int fd,
     /* [previous][next][first][last][top][bottom][index][help] */
 134                      struct bitset *rbits,
 135                      struct bitset *wbits,
 136                      struct bitset *xbits)
 137 {
 138   BOOL read, write, except;
 139   int i, ret;
 140   INPUT_RECORD *irbuffer;
 141   DWORD avail, nbuffer;
 142   BOOL bRet;
 143   IO_STATUS_BLOCK iosb;
 144   FILE_PIPE_LOCAL_INFORMATION fpli;
 145   static PNtQueryInformationFile NtQueryInformationFile;
 146   static BOOL once_only;
 147 
 148   read = write = except = FALSE;
 149   switch (GetFileType (h))
 150     {
 151     case FILE_TYPE_DISK:
 152       read = TRUE;
 153       write = TRUE;
 154       break;
 155 
 156     case FILE_TYPE_PIPE:
 157       if (!once_only)
 158         {
 159           NtQueryInformationFile = (PNtQueryInformationFile)
 160             GetProcAddress (GetModuleHandle ("ntdll.dll"),
 161                             "NtQueryInformationFile");
 162           once_only = TRUE;
 163         }
 164 
 165       if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
 166         {
 167           if (avail)
 168             read = TRUE;
 169         }
 170       else if (GetLastError () == ERROR_BROKEN_PIPE)
 171         ;
 172 
 173       else
 174         {
 175           /* It was the write-end of the pipe.  Check if it is writable.
 176              If NtQueryInformationFile fails, optimistically assume the pipe is
 177              writable.  This could happen on Windows 9x, where
 178              NtQueryInformationFile is not available, or if we inherit a pipe
 179              that doesn't permit FILE_READ_ATTRIBUTES access on the write end
 180              (I think this should not happen since Windows XP SP2; WINE seems
 181              fine too).  Otherwise, ensure that enough space is available for
 182              atomic writes.  */
 183           memset (&iosb, 0, sizeof (iosb));
 184           memset (&fpli, 0, sizeof (fpli));
 185 
 186           if (!NtQueryInformationFile
 187               || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
 188                                          FilePipeLocalInformation)
 189               || fpli.WriteQuotaAvailable >= PIPE_BUF
 190               || (fpli.OutboundQuota < PIPE_BUF &&
 191                   fpli.WriteQuotaAvailable == fpli.OutboundQuota))
 192             write = TRUE;
 193         }
 194       break;
 195 
 196     case FILE_TYPE_CHAR:
 197       write = TRUE;
 198       if (!(rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
 199         break;
 200 
 201       ret = WaitForSingleObject (h, 0);
 202       if (ret == WAIT_OBJECT_0)
 203         {
 204           if (!IsConsoleHandle (h))
 205             {
 206               read = TRUE;
 207               break;
 208             }
 209 
 210           nbuffer = avail = 0;
 211           bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
 212 
 213           /* Screen buffers handles are filtered earlier.  */
 214           assert (bRet);
 215           if (nbuffer == 0)
 216             {
 217               except = TRUE;
 218               break;
 219             }
 220 
 221           irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
 222           bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
 223           if (!bRet || avail == 0)
 224             {
 225               except = TRUE;
 226               break;
 227             }
 228 
 229           for (i = 0; i < avail; i++)
 230             if (irbuffer[i].EventType == KEY_EVENT)
 231               read = TRUE;
 232         }
 233       break;
 234 
 235     default:
 236       ret = WaitForSingleObject (h, 0);
 237       write = TRUE;
 238       if (ret == WAIT_OBJECT_0)
 239         read = TRUE;
 240 
 241       break;
 242     }
 243 
 244   ret = 0;
 245   if (read && (rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
 246     {
 247       rbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
 248       ret++;
 249     }
 250 
 251   if (write && (wbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
 252     {
 253       wbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
 254       ret++;
 255     }
 256 
 257   if (except && (xbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
 258     {
 259       xbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
 260       ret++;
 261     }
 262 
 263   return ret;
 264 }
 265 
 266 int
 267 rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
     /* [previous][next][first][last][top][bottom][index][help] */
 268             struct timeval *timeout)
 269 #undef timeval
 270 {
 271   static struct timeval tv0;
 272   static HANDLE hEvent;
 273   HANDLE h, handle_array[FD_SETSIZE + 2];
 274   fd_set handle_rfds, handle_wfds, handle_xfds;
 275   struct bitset rbits, wbits, xbits;
 276   unsigned char anyfds_in[FD_SETSIZE / CHAR_BIT];
 277   DWORD ret, wait_timeout, nhandles, nsock, nbuffer;
 278   MSG msg;
 279   int i, fd, rc;
 280   clock_t tend;
 281 
 282   if (nfds > FD_SETSIZE)
 283     nfds = FD_SETSIZE;
 284 
 285   if (!timeout)
 286     wait_timeout = INFINITE;
 287   else
 288     {
 289       wait_timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
 290 
 291       /* select is also used as a portable usleep.  */
 292       if (!rfds && !wfds && !xfds)
 293         {
 294           Sleep (wait_timeout);
 295           return 0;
 296         }
 297     }
 298 
 299   if (!hEvent)
 300     hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
 301 
 302   handle_array[0] = hEvent;
 303   nhandles = 1;
 304   nsock = 0;
 305 
 306   /* Copy descriptors to bitsets.  At the same time, eliminate
 307      bits in the "wrong" direction for console input buffers
 308      and screen buffers, because screen buffers are waitable
 309      and they will block until a character is available.  */
 310   memset (&rbits, 0, sizeof (rbits));
 311   memset (&wbits, 0, sizeof (wbits));
 312   memset (&xbits, 0, sizeof (xbits));
 313   memset (anyfds_in, 0, sizeof (anyfds_in));
 314   if (rfds)
 315     for (i = 0; i < rfds->fd_count; i++)
 316       {
 317         fd = rfds->fd_array[i];
 318         h = (HANDLE) _get_osfhandle (fd);
 319         if (IsConsoleHandle (h)
 320             && !GetNumberOfConsoleInputEvents (h, &nbuffer))
 321           continue;
 322 
 323         rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
 324         anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
 325       }
 326   else
 327     rfds = (fd_set *) alloca (sizeof (fd_set));
 328 
 329   if (wfds)
 330     for (i = 0; i < wfds->fd_count; i++)
 331       {
 332         fd = wfds->fd_array[i];
 333         h = (HANDLE) _get_osfhandle (fd);
 334         if (IsConsoleHandle (h)
 335             && GetNumberOfConsoleInputEvents (h, &nbuffer))
 336           continue;
 337 
 338         wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
 339         anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
 340       }
 341   else
 342     wfds = (fd_set *) alloca (sizeof (fd_set));
 343 
 344   if (xfds)
 345     for (i = 0; i < xfds->fd_count; i++)
 346       {
 347         fd = xfds->fd_array[i];
 348         xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
 349         anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
 350       }
 351   else
 352     xfds = (fd_set *) alloca (sizeof (fd_set));
 353 
 354   /* Zero all the fd_sets, including the application's.  */
 355   FD_ZERO (rfds);
 356   FD_ZERO (wfds);
 357   FD_ZERO (xfds);
 358   FD_ZERO (&handle_rfds);
 359   FD_ZERO (&handle_wfds);
 360   FD_ZERO (&handle_xfds);
 361 
 362   /* Classify handles.  Create fd sets for sockets, poll the others. */
 363   for (i = 0; i < nfds; i++)
 364     {
 365       if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
 366         continue;
 367 
 368       h = (HANDLE) _get_osfhandle (i);
 369       if (!h)
 370         {
 371           errno = EBADF;
 372           return -1;
 373         }
 374 
 375       if (IsSocketHandle (h))
 376         {
 377           int requested = FD_CLOSE;
 378 
 379           /* See above; socket handles are mapped onto select, but we
 380              need to map descriptors to handles.  */
 381           if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
 382             {
 383               requested |= FD_READ | FD_ACCEPT;
 384               FD_SET ((SOCKET) h, rfds);
 385               FD_SET ((SOCKET) h, &handle_rfds);
 386             }
 387           if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
 388             {
 389               requested |= FD_WRITE | FD_CONNECT;
 390               FD_SET ((SOCKET) h, wfds);
 391               FD_SET ((SOCKET) h, &handle_wfds);
 392             }
 393           if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
 394             {
 395               requested |= FD_OOB;
 396               FD_SET ((SOCKET) h, xfds);
 397               FD_SET ((SOCKET) h, &handle_xfds);
 398             }
 399 
 400           WSAEventSelect ((SOCKET) h, hEvent, requested);
 401           nsock++;
 402         }
 403       else
 404         {
 405           handle_array[nhandles++] = h;
 406 
 407           /* Poll now.  If we get an event, do not wait below.  */
 408           if (wait_timeout != 0
 409               && windows_poll_handle (h, i, &rbits, &wbits, &xbits))
 410             wait_timeout = 0;
 411         }
 412     }
 413 
 414   /* Place a sentinel at the end of the array.  */
 415   handle_array[nhandles] = NULL;
 416 
 417   /* When will the waiting period expire?  */
 418   if (wait_timeout != INFINITE)
 419     tend = clock () + wait_timeout;
 420 
 421 restart:
 422   if (wait_timeout == 0 || nsock == 0)
 423     rc = 0;
 424   else
 425     {
 426       /* See if we need to wait in the loop below.  If any select is ready,
 427          do MsgWaitForMultipleObjects anyway to dispatch messages, but
 428          no need to call select again.  */
 429       rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
 430       if (rc == 0)
 431         {
 432           /* Restore the fd_sets for the other select we do below.  */
 433           memcpy (&handle_rfds, rfds, sizeof (fd_set));
 434           memcpy (&handle_wfds, wfds, sizeof (fd_set));
 435           memcpy (&handle_xfds, xfds, sizeof (fd_set));
 436         }
 437       else
 438         wait_timeout = 0;
 439     }
 440 
 441   /* How much is left to wait?  */
 442   if (wait_timeout != INFINITE)
 443     {
 444       clock_t tnow = clock ();
 445       if (tend >= tnow)
 446         wait_timeout = tend - tnow;
 447       else
 448         wait_timeout = 0;
 449     }
 450 
 451   for (;;)
 452     {
 453       ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
 454                                        wait_timeout, QS_ALLINPUT);
 455 
 456       if (ret == WAIT_OBJECT_0 + nhandles)
 457         {
 458           /* new input of some other kind */
 459           BOOL bRet;
 460           while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
 461             {
 462               TranslateMessage (&msg);
 463               DispatchMessage (&msg);
 464             }
 465         }
 466       else
 467         break;
 468     }
 469 
 470   /* If we haven't done it yet, check the status of the sockets.  */
 471   if (rc == 0 && nsock > 0)
 472     rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
 473 
 474   if (nhandles > 1)
 475     {
 476       /* Count results that are not counted in the return value of select.  */
 477       nhandles = 1;
 478       for (i = 0; i < nfds; i++)
 479         {
 480           if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
 481             continue;
 482 
 483           h = (HANDLE) _get_osfhandle (i);
 484           if (h == handle_array[nhandles])
 485             {
 486               /* Not a socket.  */
 487               nhandles++;
 488               windows_poll_handle (h, i, &rbits, &wbits, &xbits);
 489               if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))
 490                   || wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))
 491                   || xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
 492                 rc++;
 493             }
 494         }
 495 
 496       if (rc == 0
 497           && (wait_timeout == INFINITE
 498               /* If NHANDLES > 1, but no bits are set, it means we've
 499                  been told incorrectly that some handle was signaled.
 500                  This happens with anonymous pipes, which always cause
 501                  MsgWaitForMultipleObjects to exit immediately, but no
 502                  data is found ready to be read by windows_poll_handle.
 503                  To avoid a total failure (whereby we return zero and
 504                  don't wait at all), let's poll in a more busy loop.  */
 505               || (wait_timeout != 0 && nhandles > 1)))
 506         {
 507           /* Sleep 1 millisecond to avoid busy wait and retry with the
 508              original fd_sets.  */
 509           memcpy (&handle_rfds, rfds, sizeof (fd_set));
 510           memcpy (&handle_wfds, wfds, sizeof (fd_set));
 511           memcpy (&handle_xfds, xfds, sizeof (fd_set));
 512           SleepEx (1, TRUE);
 513           goto restart;
 514         }
 515       if (timeout && wait_timeout == 0 && rc == 0)
 516         timeout->tv_sec = timeout->tv_usec = 0;
 517     }
 518 
 519   /* Now fill in the results.  */
 520   FD_ZERO (rfds);
 521   FD_ZERO (wfds);
 522   FD_ZERO (xfds);
 523   nhandles = 1;
 524   for (i = 0; i < nfds; i++)
 525     {
 526       if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
 527         continue;
 528 
 529       h = (HANDLE) _get_osfhandle (i);
 530       if (h != handle_array[nhandles])
 531         {
 532           /* Perform handle->descriptor mapping.  */
 533           SOCKET s = (SOCKET) h;
 534           WSAEventSelect (s, NULL, 0);
 535           if (FD_ISSET (s, &handle_rfds))
 536             FD_SET (i, rfds);
 537           if (FD_ISSET (s, &handle_wfds))
 538             FD_SET (i, wfds);
 539           if (FD_ISSET (s, &handle_xfds))
 540             FD_SET (i, xfds);
 541         }
 542       else
 543         {
 544           /* Not a socket.  */
 545           nhandles++;
 546           if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
 547             FD_SET (i, rfds);
 548           if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
 549             FD_SET (i, wfds);
 550           if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
 551             FD_SET (i, xfds);
 552         }
 553     }
 554 
 555   return rc;
 556 }
 557 
 558 #else /* ! Native Windows.  */
 559 
 560 #include <stddef.h> /* NULL */
 561 #include <errno.h>
 562 #include <unistd.h>
 563 
 564 #undef select
 565 
 566 int
 567 rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
     /* [previous][next][first][last][top][bottom][index][help] */
 568             struct timeval *timeout)
 569 {
 570   int i;
 571 
 572   /* FreeBSD 8.2 has a bug: it does not always detect invalid fds.  */
 573   if (nfds < 0 || nfds > FD_SETSIZE)
 574     {
 575       errno = EINVAL;
 576       return -1;
 577     }
 578   for (i = 0; i < nfds; i++)
 579     {
 580       if (((rfds && FD_ISSET (i, rfds))
 581            || (wfds && FD_ISSET (i, wfds))
 582            || (xfds && FD_ISSET (i, xfds)))
 583           && dup2 (i, i) != i)
 584         return -1;
 585     }
 586 
 587   /* Interix 3.5 has a bug: it does not support nfds == 0.  */
 588   if (nfds == 0)
 589     {
 590       nfds = 1;
 591       rfds = NULL;
 592       wfds = NULL;
 593       xfds = NULL;
 594     }
 595   return select (nfds, rfds, wfds, xfds, timeout);
 596 }
 597 
 598 #endif

/* [previous][next][first][last][top][bottom][index][help] */