summaryrefslogtreecommitdiff
path: root/dist/IO/poll.c
blob: 9d39d57f2f6230ce670232bff9edca5003b79628 (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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
 * poll.c
 *
 * Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved.
 * This program is free software; you can redistribute it and/or
 * modify it under the same terms as Perl itself.
 *
 * For systems that do not have the poll() system call (for example Linux
 * kernels < v2.1.23) try to emulate it as closely as possible using select()
 *
 */

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "poll.h"
#ifdef I_SYS_TIME
# include <sys/time.h>
#endif
#ifdef I_TIME
# include <time.h>
#endif
#include <sys/types.h>
#if defined(HAS_SOCKET) && !defined(VMS) && !defined(ultrix) /* VMS handles sockets via vmsish.h, ULTRIX dies of socket struct redefinitions */
#  include <sys/socket.h>
#endif
#include <sys/stat.h>
#include <errno.h>

#ifdef HAS_SELECT
#ifdef I_SYS_SELECT
#include <sys/select.h>
#endif
#endif

#ifdef EMULATE_POLL_WITH_SELECT

# define POLL_CAN_READ	(POLLIN | POLLRDNORM )
# define POLL_CAN_WRITE	(POLLOUT | POLLWRNORM | POLLWRBAND )
# define POLL_HAS_EXCP	(POLLRDBAND | POLLPRI )

# define POLL_EVENTS_MASK (POLL_CAN_READ | POLL_CAN_WRITE | POLL_HAS_EXCP)

int
poll(struct pollfd *fds, unsigned long nfds, int timeout)
{
    int i,err;
    fd_set rfd,wfd,efd,ifd;
    struct timeval timebuf;
    struct timeval *tbuf = (struct timeval *)0;
    int n = 0;
    int count;

    FD_ZERO(&ifd);

again:

    FD_ZERO(&rfd);
    FD_ZERO(&wfd);
    FD_ZERO(&efd);

    for(i = 0 ; i < (int)nfds ; i++) {
	int events = fds[i].events;
	int fd = fds[i].fd;

	fds[i].revents = 0;

	if(fd < 0 || FD_ISSET(fd, &ifd))
	    continue;

	if(fd > n)
	    n = fd;

	if(events & POLL_CAN_READ)
	    FD_SET(fd, &rfd);

	if(events & POLL_CAN_WRITE)
	    FD_SET(fd, &wfd);

	if(events & POLL_HAS_EXCP)
	    FD_SET(fd, &efd);
    }

    if(timeout >= 0) {
	timebuf.tv_sec = timeout / 1000;
	timebuf.tv_usec = (timeout % 1000) * 1000;
	tbuf = &timebuf;
    }

    err = select(n+1,&rfd,&wfd,&efd,tbuf);

    if(err < 0) {
#ifdef HAS_FSTAT
	if(errno == EBADF) {
	    for(i = 0 ; i < nfds ; i++) {
		struct stat buf;
		if((fstat(fds[i].fd,&buf) < 0) && (errno == EBADF)) {
		    FD_SET(fds[i].fd, &ifd);
		    goto again;
		}
	    }
	}
#endif /* HAS_FSTAT */
	return err;
    }

    count = 0;

    for(i = 0 ; i < (int)nfds ; i++) {
	int revents = (fds[i].events & POLL_EVENTS_MASK);
	int fd = fds[i].fd;

	if(fd < 0)
	    continue;

	if(FD_ISSET(fd, &ifd))
	    revents = POLLNVAL;
	else {
	    if(!FD_ISSET(fd, &rfd))
	        revents &= ~POLL_CAN_READ;

	    if(!FD_ISSET(fd, &wfd))
	        revents &= ~POLL_CAN_WRITE;

	    if(!FD_ISSET(fd, &efd))
	        revents &= ~POLL_HAS_EXCP;
	}

	if((fds[i].revents = revents) != 0)
	    count++;
    }

    return count; 
}

#endif /* EMULATE_POLL_WITH_SELECT */

/* gcc for SunOS 4 produces code from an empty (code/symbolwise)
 * source code file that makes the SunOS 4.x /usr/bin/ld fail with
 * ld: poll.o: premature EOF
 * To avoid this, have at least something in here.  */
#if defined(__sun) && !defined(__SVR4) && defined(__GNUC__)
static int dummy;
#endif