sleepy_penguin.git  about / heads / tags
Linux I/O events for Ruby
blob 8dd68420247980eef1ce0840d73579fe8b229c6b 3554 bytes (raw)
$ git show pu:ext/sleepy_penguin/util.c	# shows this blob on the CLI

  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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
 
#include "sleepy_penguin.h"

#ifndef HAVE_RB_IO_GET_IO
static VALUE my_io_get_io(VALUE io)
{
	return rb_convert_type(io, T_FILE, "IO", "to_io");
}
#  define rb_io_get_io(io) my_io_get_io((io))
#endif /* HAVE_RB_IO_GET_IO */

static VALUE klass_for(VALUE klass)
{
	return (TYPE(klass) == T_CLASS) ? klass : CLASS_OF(klass);
}

int rb_sp_get_flags(VALUE klass, VALUE flags, int default_flags)
{
	switch (TYPE(flags)) {
	case T_NIL: return default_flags;
	case T_FIXNUM: return FIX2INT(flags);
	case T_BIGNUM: return NUM2INT(flags);
	case T_SYMBOL:
		return NUM2INT(rb_const_get(klass_for(klass), SYM2ID(flags)));
	case T_ARRAY: {
		long i;
		long len = RARRAY_LEN(flags);
		int rv = 0;

		klass = klass_for(klass);
		for (i = 0; i < len; i++) {
			VALUE tmp = rb_ary_entry(flags, i);

			Check_Type(tmp, T_SYMBOL);
			tmp = rb_const_get(klass, SYM2ID(tmp));
			rv |= NUM2INT(tmp);
		}
		return rv;
		}
	}
	rb_raise(rb_eTypeError, "invalid flags");
	return 0;
}

unsigned rb_sp_get_uflags(VALUE klass, VALUE flags)
{
	switch (TYPE(flags)) {
	case T_NIL: return 0;
	case T_FIXNUM: return FIX2UINT(flags);
	case T_BIGNUM: return NUM2UINT(flags);
	case T_SYMBOL:
		return NUM2UINT(rb_const_get(klass_for(klass), SYM2ID(flags)));
	case T_ARRAY: {
		long i;
		long len = RARRAY_LEN(flags);
		unsigned rv = 0;

		klass = klass_for(klass);
		for (i = 0; i < len; i++) {
			VALUE tmp = rb_ary_entry(flags, i);

			Check_Type(tmp, T_SYMBOL);
			tmp = rb_const_get(klass, SYM2ID(tmp));
			rv |= NUM2UINT(tmp);
		}
		return rv;
		}
	}
	rb_raise(rb_eTypeError, "invalid flags");
	return 0;
}

#if ! HAVE_RB_IO_T
#  define rb_io_t OpenFile
#endif

#ifdef GetReadFile
#  define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
#else
#  if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
#    define FPTR_TO_FD(fptr) fileno(fptr->f)
#  else
#    define FPTR_TO_FD(fptr) fptr->fd
#  endif
#endif

static int fixint_closed_p(VALUE io)
{
	return (fcntl(FIX2INT(io), F_GETFD) == -1 && errno == EBADF);
}

#if defined(RFILE) && defined(HAVE_ST_FD)
static int my_rb_io_closed(VALUE io)
{
	return RFILE(io)->fptr->fd < 0;
}
#else
static int my_rb_io_closed(VALUE io)
{
	return rb_funcall(io, rb_intern("closed?"), 0) == Qtrue;
}
#endif

int rb_sp_io_closed(VALUE io)
{
	switch (TYPE(io)) {
	case T_FIXNUM:
		return fixint_closed_p(io);
	case T_FILE:
		break;
	default:
		io = rb_io_get_io(io);
	}

	return my_rb_io_closed(io);
}

int rb_sp_fileno(VALUE io)
{
	rb_io_t *fptr;

	if (RB_TYPE_P(io, T_FIXNUM))
		return FIX2INT(io);

	io = rb_io_get_io(io);
	GetOpenFile(io, fptr);
	return FPTR_TO_FD(fptr);
}

void rb_sp_set_nonblock(int fd)
{
	int flags = fcntl(fd, F_GETFL);

	if (flags == -1)
		rb_sys_fail("fcntl(F_GETFL)");
	if ((flags & O_NONBLOCK) == O_NONBLOCK)
		return;
	/*
	 * Note: while this is Linux-only and we could safely rely on
	 * ioctl(FIONBIO), needing F_SETFL is an uncommon path, and
	 * F_GETFL is lockless.  ioctl(FIONBIO) always acquires a spin
	 * lock, so it's more expensive even if we do not need to change
	 * anything.
	 */
	flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
	if (flags < 0)
		rb_sys_fail("fcntl(F_SETFL)");
}

int rb_sp_wait(rb_sp_waitfn waiter, VALUE obj, int *fd)
{
	/*
	 * we need to check the fileno before and after waiting, a close()
	 * could've happened at any time (especially when outside of GVL).
	 */
	int rc = waiter(rb_sp_fileno(obj));
	*fd = rb_sp_fileno(obj);
	return rc;
}

int rb_sp_gc_for_fd(int err)
{
	if (err == EMFILE || err == ENFILE || err == ENOMEM) {
		rb_gc();
		return 1;
	}
	return 0;
}

git clone https://yhbt.net/sleepy_penguin.git