summaryrefslogtreecommitdiff
path: root/src/iopause.c
blob: b8614b2219d87a55473d37b4152c8c97c844e746 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include "iopause.h"

#include <poll.h>

#include "select.h"
#include "taia.h"

/**
  @file iopause.c
  @author djb
  @ref qmail
  @brief stateful reading from net
  @return > 0 if successful
*/

int iopause(iopause_fd *x, unsigned int len, struct taia *deadline, struct taia *stamp)
{
  struct taia t;
  int millisecs;
  double d;
  int i, r;

  if (taia_less(deadline, stamp)) {
    millisecs = 0;
  } else {
    t = *stamp;
    taia_sub(&t, deadline, &t);
    d = taia_approx(&t);
    if (d > 1000.0) d = 1000.0;
    millisecs = d * 1000.0 + 20.0;
    if (millisecs < 0) millisecs = 20.0;
  }

  for (i = 0; i < len; ++i) x[i].revents = 0;

#ifdef IOPAUSE_POLL
  r = poll(x, len, millisecs);

  /* XXX: some kernels apparently need x[0] even if len is 0 */
  /* XXX: how to handle EAGAIN? are kernels really this dumb? */
  /* XXX: how to handle EINVAL? when exactly can this happen? */

#else
  struct timeval tv;
  fd_set rfds;
  fd_set wfds;
  int nfds;
  int fd;

  FD_ZERO(&rfds);
  FD_ZERO(&wfds);

  nfds = 1;
  for (i = 0; i < len; ++i) {
    fd = x[i].fd;
    if (fd < 0) continue;
    if (fd >= 8 * sizeof(fd_set)) continue; /*XXX*/

    if (fd >= nfds) nfds = fd + 1;
    if (x[i].events & IOPAUSE_READ) FD_SET(fd, &rfds);
    if (x[i].events & IOPAUSE_WRITE) FD_SET(fd, &wfds);
  }

  tv.tv_sec = millisecs / 1000;
  tv.tv_usec = 1000 * (millisecs % 1000);

  r = select(nfds, &rfds, &wfds, (fd_set *)0, &tv);
  if (r <= 0) return r;

  /* XXX: for EBADF, could seek out and destroy the bad descriptor */

  for (i = 0; i < len; ++i) {
    fd = x[i].fd;
    if (fd < 0) continue;
    if (fd >= 8 * sizeof(fd_set)) continue; /*XXX*/

    if (x[i].events & IOPAUSE_READ)
      if (FD_ISSET(fd, &rfds)) x[i].revents |= IOPAUSE_READ;
    if (x[i].events & IOPAUSE_WRITE)
      if (FD_ISSET(fd, &wfds)) x[i].revents |= IOPAUSE_WRITE;
  }

#endif
  return r;
}