summaryrefslogtreecommitdiff
path: root/ext/io/wait/wait.c
blob: 24e4141162ec076da08157d543c58b813aa19e6d (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
/**********************************************************************

  io/wait.c -

  $Author$
  created at: Tue Aug 28 09:08:06 JST 2001

  All the files in this distribution are covered under the Ruby's
  license (see the file COPYING).

**********************************************************************/

#include "ruby.h"
#include "ruby/io.h"

#include <sys/types.h>
#if defined(HAVE_SYS_IOCTL_H)
#include <sys/ioctl.h>
#endif
#if defined(FIONREAD_HEADER)
#include FIONREAD_HEADER
#endif

#ifdef HAVE_RB_W32_IOCTLSOCKET
#define ioctl ioctlsocket
#define ioctl_arg u_long
#define ioctl_arg2num(i) ULONG2NUM(i)
#else
#define ioctl_arg int
#define ioctl_arg2num(i) INT2NUM(i)
#endif

#ifdef HAVE_RB_W32_IS_SOCKET
#define FIONREAD_POSSIBLE_P(fd) rb_w32_is_socket(fd)
#else
#define FIONREAD_POSSIBLE_P(fd) ((void)(fd),Qtrue)
#endif

static VALUE io_ready_p _((VALUE io));
static VALUE io_wait _((int argc, VALUE *argv, VALUE io));
void Init_wait _((void));

EXTERN struct timeval rb_time_interval _((VALUE time));

/*
 * call-seq:
 *   io.ready? -> true, false or nil
 *
 * Returns true if input available without blocking, or false.
 * Returns nil if no information available.
 */

static VALUE
io_ready_p(VALUE io)
{
    rb_io_t *fptr;
    ioctl_arg n;

    GetOpenFile(io, fptr);
    rb_io_check_readable(fptr);
    if (rb_io_read_pending(fptr)) return Qtrue;
    if (!FIONREAD_POSSIBLE_P(fptr->fd)) return Qnil;
    if (ioctl(fptr->fd, FIONREAD, &n)) return Qnil;
    if (n > 0) return Qtrue;
    return Qfalse;
}

struct wait_readable_arg {
    rb_fdset_t fds;
    struct timeval *timeout;
};

#ifdef HAVE_RB_FD_INIT
static VALUE
wait_readable(VALUE p)
{
    struct wait_readable_arg *arg = (struct wait_readable_arg *)p;
    rb_fdset_t *fds = &arg->fds;

    return (VALUE)rb_thread_select(rb_fd_max(fds), rb_fd_ptr(fds), NULL, NULL, arg->timeout);
}
#endif

/*
 * call-seq:
 *   io.wait          -> IO, true, false or nil
 *   io.wait(timeout) -> IO, true, false or nil
 *
 * Waits until input is available or times out and returns self or nil when
 * EOF is reached.
 */

static VALUE
io_wait(int argc, VALUE *argv, VALUE io)
{
    rb_io_t *fptr;
    struct wait_readable_arg arg;
    int fd, i;
    ioctl_arg n;
    VALUE timeout;
    struct timeval timerec;

    GetOpenFile(io, fptr);
    rb_io_check_readable(fptr);
    rb_scan_args(argc, argv, "01", &timeout);
    if (NIL_P(timeout)) {
	arg.timeout = 0;
    }
    else {
	timerec = rb_time_interval(timeout);
	arg.timeout = &timerec;
    }

    if (rb_io_read_pending(fptr)) return Qtrue;
    if (!FIONREAD_POSSIBLE_P(fptr->fd)) return Qfalse;
    fd = fptr->fd;
    rb_fd_init(&arg.fds);
    rb_fd_set(fd, &arg.fds);
#ifdef HAVE_RB_FD_INIT
    i = (int)rb_ensure(wait_readable, (VALUE)&arg,
		       (VALUE (*)_((VALUE)))rb_fd_term, (VALUE)&arg.fds);
#else
    i = rb_thread_select(fd + 1, rb_fd_ptr(&arg.fds), NULL, NULL, arg.timeout);
#endif
    if (i < 0)
	rb_sys_fail(0);
    rb_io_check_closed(fptr);
    if (ioctl(fptr->fd, FIONREAD, &n)) rb_sys_fail(0);
    if (n > 0) return io;
    return Qnil;
}

/*
 * IO wait methods
 */

void
Init_wait()
{
    rb_define_method(rb_cIO, "ready?", io_ready_p, 0);
    rb_define_method(rb_cIO, "wait", io_wait, -1);
}