sleepy_penguin 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +3 -0
- data/.manifest +8 -2
- data/ChangeLog +295 -0
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/LATEST +19 -5
- data/LICENSE +2 -2
- data/NEWS +22 -0
- data/README +4 -4
- data/TODO +0 -2
- data/ext/sleepy_penguin/epoll.c +165 -88
- data/ext/sleepy_penguin/eventfd.c +89 -91
- data/ext/sleepy_penguin/extconf.rb +1 -0
- data/ext/sleepy_penguin/init.c +7 -0
- data/ext/sleepy_penguin/inotify.c +229 -91
- data/ext/sleepy_penguin/missing_epoll.h +30 -0
- data/ext/sleepy_penguin/missing_inotify.h +57 -0
- data/ext/sleepy_penguin/signalfd.c +335 -0
- data/ext/sleepy_penguin/sleepy_penguin.h +15 -55
- data/ext/sleepy_penguin/timerfd.c +78 -36
- data/ext/sleepy_penguin/util.c +149 -0
- data/lib/sleepy_penguin.rb +0 -6
- data/lib/sleepy_penguin/signalfd/sig_info.rb +20 -0
- data/lib/sleepy_penguin/sp.rb +4 -0
- data/pkg.mk +5 -4
- data/test/test_epoll.rb +29 -0
- data/test/test_epoll_gc.rb +1 -1
- data/test/test_epoll_optimizations.rb +5 -2
- data/test/test_eventfd.rb +24 -6
- data/test/test_inotify.rb +80 -1
- data/test/test_signalfd.rb +94 -0
- data/test/test_signalfd_siginfo.rb +32 -0
- data/test/test_timerfd.rb +34 -4
- metadata +22 -10
- data/ext/sleepy_penguin/nonblock.h +0 -19
- data/script/isolate_for_tests +0 -30
@@ -1,19 +1,34 @@
|
|
1
1
|
#ifdef HAVE_SYS_EVENTFD_H
|
2
2
|
#include "sleepy_penguin.h"
|
3
3
|
#include <sys/eventfd.h>
|
4
|
-
#include "nonblock.h"
|
5
4
|
static ID id_for_fd;
|
6
5
|
|
7
|
-
|
6
|
+
/*
|
7
|
+
* call-seq:
|
8
|
+
* EventFD.new(initial_value [, flags]) -> EventFD IO object
|
9
|
+
*
|
10
|
+
* Creates an EventFD object. +initial_value+ is a non-negative Integer
|
11
|
+
* to start the internal counter at.
|
12
|
+
*
|
13
|
+
* Starting with Linux 2.6.27, +flags+ may be a mask that consists of any
|
14
|
+
* of the following:
|
15
|
+
*
|
16
|
+
* - :CLOEXEC - set the close-on-exec flag on the new object
|
17
|
+
* - :NONBLOCK - set the non-blocking I/O flag on the new object
|
18
|
+
*
|
19
|
+
* Since Linux 2.6.30, +flags+ may also include:
|
20
|
+
* - :SEMAPHORE - provides semaphore-like semantics (see EventFD#value)
|
21
|
+
*/
|
22
|
+
static VALUE s_new(int argc, VALUE *argv, VALUE klass)
|
8
23
|
{
|
9
24
|
VALUE _initval, _flags;
|
10
25
|
unsigned initval;
|
11
|
-
int flags
|
26
|
+
int flags;
|
12
27
|
int fd;
|
13
28
|
|
14
29
|
rb_scan_args(argc, argv, "11", &_initval, &_flags);
|
15
30
|
initval = NUM2UINT(_initval);
|
16
|
-
flags =
|
31
|
+
flags = rb_sp_get_flags(klass, _flags);
|
17
32
|
|
18
33
|
fd = eventfd(initval, flags);
|
19
34
|
if (fd == -1) {
|
@@ -28,7 +43,6 @@ static VALUE create(int argc, VALUE *argv, VALUE klass)
|
|
28
43
|
return rb_funcall(klass, id_for_fd, 1, INT2NUM(fd));
|
29
44
|
}
|
30
45
|
|
31
|
-
#ifdef HAVE_RB_THREAD_BLOCKING_REGION
|
32
46
|
struct efd_args {
|
33
47
|
int fd;
|
34
48
|
uint64_t val;
|
@@ -37,7 +51,7 @@ struct efd_args {
|
|
37
51
|
static VALUE efd_write(void *_args)
|
38
52
|
{
|
39
53
|
struct efd_args *args = _args;
|
40
|
-
ssize_t w = write(args->fd, &args->
|
54
|
+
ssize_t w = write(args->fd, &args->val, sizeof(uint64_t));
|
41
55
|
|
42
56
|
return (VALUE)w;
|
43
57
|
}
|
@@ -45,111 +59,80 @@ static VALUE efd_write(void *_args)
|
|
45
59
|
static VALUE efd_read(void *_args)
|
46
60
|
{
|
47
61
|
struct efd_args *args = _args;
|
48
|
-
ssize_t r = read(args->fd, &args->
|
62
|
+
ssize_t r = read(args->fd, &args->val, sizeof(uint64_t));
|
49
63
|
|
50
64
|
return (VALUE)r;
|
51
65
|
}
|
52
66
|
|
53
|
-
|
67
|
+
/*
|
68
|
+
* call-seq:
|
69
|
+
* efd.incr(integer_value[, nonblock ]) -> true or nil
|
70
|
+
*
|
71
|
+
* Increments the internal counter by +integer_value+ which is an unsigned
|
72
|
+
* Integer value.
|
73
|
+
*
|
74
|
+
* If +nonblock+ is specified and true, this will return +nil+ if the
|
75
|
+
* internal counter will overflow the value of EventFD::MAX.
|
76
|
+
* Otherwise it will block until the counter may be incremented without
|
77
|
+
* overflowing.
|
78
|
+
*/
|
79
|
+
static VALUE incr(int argc, VALUE *argv, VALUE self)
|
54
80
|
{
|
55
81
|
struct efd_args x;
|
56
82
|
ssize_t w;
|
83
|
+
VALUE value, nonblock;
|
57
84
|
|
58
|
-
|
85
|
+
rb_scan_args(argc, argv, "11", &value, &nonblock);
|
86
|
+
x.fd = rb_sp_fileno(self);
|
87
|
+
RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
|
59
88
|
x.val = (uint64_t)NUM2ULL(value);
|
60
|
-
|
61
89
|
retry:
|
62
|
-
w = (ssize_t)
|
90
|
+
w = (ssize_t)rb_sp_io_region(efd_write, &x);
|
63
91
|
if (w == -1) {
|
92
|
+
if (errno == EAGAIN && RTEST(nonblock))
|
93
|
+
return Qfalse;
|
64
94
|
if (rb_io_wait_writable(x.fd))
|
65
95
|
goto retry;
|
66
96
|
rb_sys_fail("write(eventfd)");
|
67
97
|
}
|
68
98
|
|
69
|
-
return
|
99
|
+
return Qtrue;
|
70
100
|
}
|
71
101
|
|
72
|
-
|
102
|
+
/*
|
103
|
+
* call-seq:
|
104
|
+
* efd.value([nonblock]) -> Integer or nil
|
105
|
+
*
|
106
|
+
* If not created as a semaphore, returns the current value and resets
|
107
|
+
* the counter to zero.
|
108
|
+
*
|
109
|
+
* If created as a semaphore, this decrements the counter value by one
|
110
|
+
* and returns +1+.
|
111
|
+
*
|
112
|
+
* If the counter is zero at the time of the call, this will block until
|
113
|
+
* the counter becomes non-zero unless +nonblock+ is +true+, in which
|
114
|
+
* case it returns +nil+.
|
115
|
+
*/
|
116
|
+
static VALUE getvalue(int argc, VALUE argv, VALUE self)
|
73
117
|
{
|
74
118
|
struct efd_args x;
|
75
|
-
|
76
|
-
x.fd = my_fileno(self);
|
77
|
-
|
78
|
-
retry:
|
79
|
-
w = (ssize_t)rb_thread_blocking_region(efd_read, &x, RUBY_UBF_IO, 0);
|
80
|
-
if (w == -1) {
|
81
|
-
if (rb_io_wait_readable(x.fd))
|
82
|
-
goto retry;
|
83
|
-
rb_sys_fail("read(eventfd)");
|
84
|
-
}
|
85
|
-
|
86
|
-
return ULL2NUM(x.buf);
|
87
|
-
}
|
88
|
-
#else /* !HAVE_RB_THREAD_BLOCKING_REGION */
|
89
|
-
|
90
|
-
static VALUE incr(VALUE self, VALUE value)
|
91
|
-
{
|
92
|
-
int fd = my_fileno(self);
|
93
|
-
uint64_t val = (uint64_t)NUM2ULL(value);
|
94
119
|
ssize_t w;
|
120
|
+
VALUE nonblock;
|
95
121
|
|
96
|
-
|
122
|
+
rb_scan_args(argc, argv, "01", &nonblock);
|
123
|
+
x.fd = rb_sp_fileno(self);
|
124
|
+
RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
|
97
125
|
retry:
|
98
|
-
w =
|
126
|
+
w = (ssize_t)rb_sp_io_region(efd_read, &x);
|
99
127
|
if (w == -1) {
|
100
|
-
if (
|
101
|
-
|
102
|
-
|
103
|
-
}
|
104
|
-
|
105
|
-
return Qnil;
|
106
|
-
}
|
107
|
-
|
108
|
-
static VALUE getvalue(VALUE self)
|
109
|
-
{
|
110
|
-
int fd = my_fileno(self);
|
111
|
-
uint64_t val;
|
112
|
-
ssize_t r;
|
113
|
-
|
114
|
-
set_nonblock(fd);
|
115
|
-
retry:
|
116
|
-
r = read(fd, &val, sizeof(uint64_t));
|
117
|
-
if (r == -1) {
|
118
|
-
if (rb_io_wait_readable(fd))
|
128
|
+
if (errno == EAGAIN && RTEST(nonblock))
|
129
|
+
return Qnil;
|
130
|
+
if (rb_io_wait_readable(x.fd))
|
119
131
|
goto retry;
|
120
132
|
rb_sys_fail("read(eventfd)");
|
121
133
|
}
|
122
134
|
|
123
|
-
return ULL2NUM(val);
|
124
|
-
}
|
125
|
-
#endif /* !HAVE_RB_THREAD_BLOCKING_REGION */
|
126
|
-
|
127
|
-
static VALUE getvalue_nonblock(VALUE self)
|
128
|
-
{
|
129
|
-
int fd = my_fileno(self);
|
130
|
-
uint64_t val;
|
131
|
-
ssize_t r;
|
132
|
-
|
133
|
-
set_nonblock(fd);
|
134
|
-
r = read(fd, &val, sizeof(uint64_t));
|
135
|
-
if (r == -1)
|
136
|
-
rb_sys_fail("read(eventfd)");
|
137
|
-
|
138
|
-
return ULL2NUM(val);
|
139
|
-
}
|
140
|
-
|
141
|
-
static VALUE incr_nonblock(VALUE self, VALUE value)
|
142
|
-
{
|
143
|
-
int fd = my_fileno(self);
|
144
|
-
uint64_t val = (uint64_t)NUM2ULL(value);
|
145
|
-
ssize_t w;
|
146
|
-
|
147
|
-
set_nonblock(fd);
|
148
|
-
w = write(fd, &val, sizeof(uint64_t));
|
149
|
-
if (w == -1)
|
150
|
-
rb_sys_fail("write(eventfd)");
|
151
|
-
|
152
|
-
return Qnil;
|
135
|
+
return ULL2NUM(x.val);
|
153
136
|
}
|
154
137
|
|
155
138
|
void sleepy_penguin_init_eventfd(void)
|
@@ -157,21 +140,36 @@ void sleepy_penguin_init_eventfd(void)
|
|
157
140
|
VALUE mSleepyPenguin, cEventFD;
|
158
141
|
|
159
142
|
mSleepyPenguin = rb_define_module("SleepyPenguin");
|
143
|
+
|
144
|
+
/*
|
145
|
+
* Document-class: SleepyPenguin::EventFD
|
146
|
+
*
|
147
|
+
* Applications may use EventFD instead of a pipe in cases where
|
148
|
+
* a pipe is only used to signal events. The kernel overhead for
|
149
|
+
* an EventFD descriptor is much lower than that of a pipe.
|
150
|
+
*
|
151
|
+
* As of Linux 2.6.30, an EventFD may also be used as a semaphore.
|
152
|
+
*/
|
160
153
|
cEventFD = rb_define_class_under(mSleepyPenguin, "EventFD", rb_cIO);
|
161
|
-
rb_define_singleton_method(cEventFD, "new",
|
154
|
+
rb_define_singleton_method(cEventFD, "new", s_new, -1);
|
155
|
+
|
156
|
+
/*
|
157
|
+
* the maximum value that may be stored in an EventFD,
|
158
|
+
* currently 0xfffffffffffffffe
|
159
|
+
*/
|
160
|
+
rb_define_const(cEventFD, "MAX", ULL2NUM(0xfffffffffffffffe));
|
161
|
+
|
162
162
|
#ifdef EFD_NONBLOCK
|
163
|
-
|
163
|
+
NODOC_CONST(cEventFD, "NONBLOCK", INT2NUM(EFD_NONBLOCK));
|
164
164
|
#endif
|
165
165
|
#ifdef EFD_CLOEXEC
|
166
|
-
|
166
|
+
NODOC_CONST(cEventFD, "CLOEXEC", INT2NUM(EFD_CLOEXEC));
|
167
167
|
#endif
|
168
168
|
#ifdef EFD_SEMAPHORE
|
169
|
-
|
169
|
+
NODOC_CONST(cEventFD, "SEMAPHORE", INT2NUM(EFD_SEMAPHORE));
|
170
170
|
#endif
|
171
|
-
rb_define_method(cEventFD, "value", getvalue,
|
172
|
-
rb_define_method(cEventFD, "incr", incr, 1);
|
173
|
-
rb_define_method(cEventFD, "value_nonblock", getvalue_nonblock, 0);
|
174
|
-
rb_define_method(cEventFD, "incr_nonblock", incr_nonblock, 1);
|
171
|
+
rb_define_method(cEventFD, "value", getvalue, -1);
|
172
|
+
rb_define_method(cEventFD, "incr", incr, -1);
|
175
173
|
id_for_fd = rb_intern("for_fd");
|
176
174
|
}
|
177
175
|
#endif /* HAVE_SYS_EVENTFD_H */
|
@@ -5,6 +5,7 @@ have_header('sys/eventfd.h')
|
|
5
5
|
have_header('sys/signalfd.h')
|
6
6
|
have_header('sys/timerfd.h')
|
7
7
|
have_header('sys/inotify.h')
|
8
|
+
have_header('sys/signalfd.h')
|
8
9
|
have_header('ruby/io.h') and have_struct_member('rb_io_t', 'fd', 'ruby/io.h')
|
9
10
|
have_func('rb_memerror') or abort 'need rb_memerror()'
|
10
11
|
have_func('rb_io_close') or abort 'need rb_io_close()'
|
data/ext/sleepy_penguin/init.c
CHANGED
@@ -18,10 +18,17 @@ void sleepy_penguin_init_inotify(void);
|
|
18
18
|
# define sleepy_penguin_init_inotify() for(;0;)
|
19
19
|
#endif
|
20
20
|
|
21
|
+
#ifdef HAVE_SYS_SIGNALFD_H
|
22
|
+
void sleepy_penguin_init_signalfd(void);
|
23
|
+
#else
|
24
|
+
# define sleepy_penguin_init_signalfd() for(;0;)
|
25
|
+
#endif
|
26
|
+
|
21
27
|
void Init_sleepy_penguin_ext(void)
|
22
28
|
{
|
23
29
|
sleepy_penguin_init_epoll();
|
24
30
|
sleepy_penguin_init_timerfd();
|
25
31
|
sleepy_penguin_init_eventfd();
|
26
32
|
sleepy_penguin_init_inotify();
|
33
|
+
sleepy_penguin_init_signalfd();
|
27
34
|
}
|
@@ -1,77 +1,32 @@
|
|
1
1
|
#ifdef HAVE_SYS_INOTIFY_H
|
2
2
|
#include "sleepy_penguin.h"
|
3
|
-
#include "nonblock.h"
|
4
3
|
#include <sys/inotify.h>
|
5
4
|
#include <sys/ioctl.h>
|
5
|
+
#include "missing_inotify.h"
|
6
|
+
#if defined(RFILE) && defined(HAVE_ST_FD) && \
|
7
|
+
defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
8
|
+
# define NOGVL_CLOSE
|
9
|
+
#endif
|
10
|
+
|
6
11
|
static ID id_for_fd, id_inotify_buf, id_inotify_tmp, id_mask;
|
7
12
|
static VALUE cEvent, checks;
|
8
13
|
|
9
|
-
#ifndef IN_CLOEXEC
|
10
|
-
# define IN_CLOEXEC 02000000
|
11
|
-
#endif
|
12
|
-
#ifndef IN_NONBLOCK
|
13
|
-
# define IN_NONBLOCK O_NONBLOCK
|
14
|
-
#endif
|
15
|
-
#ifndef IN_ATTRIB
|
16
|
-
# define IN_ATTRIB 0x00000004
|
17
|
-
#endif
|
18
|
-
#ifndef IN_ONLYDIR
|
19
|
-
# define IN_ONLYDIR 0x01000000
|
20
|
-
#endif
|
21
|
-
#ifndef IN_DONT_FOLLOW
|
22
|
-
# define IN_DONT_FOLLOW 0x02000000
|
23
|
-
#endif
|
24
|
-
#ifndef IN_EXCL_UNLINK
|
25
|
-
# define IN_EXCL_UNLINK 0x04000000
|
26
|
-
#endif
|
27
|
-
#ifndef IN_MASK_ADD
|
28
|
-
# define IN_MASK_ADD 0x20000000
|
29
|
-
#endif
|
30
|
-
#ifndef IN_ONESHOT
|
31
|
-
# define IN_ONESHOT 0x80000000
|
32
|
-
#endif
|
33
|
-
|
34
|
-
#ifndef HAVE_INOTIFY_INIT1
|
35
14
|
/*
|
36
|
-
*
|
37
|
-
*
|
38
|
-
*
|
15
|
+
* call-seq:
|
16
|
+
* Inotify.new([flags]) -> Inotify IO object
|
17
|
+
*
|
18
|
+
* Flags may be any of the following as an Array of Symbols or Integer mask:
|
19
|
+
* - :NONBLOCK - sets the non-blocking flag on the descriptor watched.
|
20
|
+
* - :CLOEXEC - sets the close-on-exec flag
|
39
21
|
*/
|
40
|
-
static
|
41
|
-
{
|
42
|
-
int fd = inotify_init();
|
43
|
-
int tmp;
|
44
|
-
|
45
|
-
if (fd < 0)
|
46
|
-
return fd;
|
47
|
-
if ((flags & IN_CLOEXEC) && (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1))
|
48
|
-
goto fcntl_err;
|
49
|
-
if (flags & IN_NONBLOCK) {
|
50
|
-
tmp = fcntl(fd, F_GETFL);
|
51
|
-
if (tmp == -1)
|
52
|
-
goto fcntl_err;
|
53
|
-
if ((fcntl(fd, F_SETFL, tmp | O_NONBLOCK) != 0))
|
54
|
-
goto fcntl_err;
|
55
|
-
}
|
56
|
-
|
57
|
-
return fd;
|
58
|
-
fcntl_err:
|
59
|
-
tmp = errno;
|
60
|
-
close(fd);
|
61
|
-
errno = tmp;
|
62
|
-
rb_sys_fail("fcntl");
|
63
|
-
}
|
64
|
-
# define inotify_init1 my_inotify_init1
|
65
|
-
#endif /* HAVE_INOTIFY_INIT1 */
|
66
|
-
|
67
|
-
static VALUE s_init(int argc, VALUE *argv, VALUE klass)
|
22
|
+
static VALUE s_new(int argc, VALUE *argv, VALUE klass)
|
68
23
|
{
|
69
24
|
VALUE _flags, rv;
|
70
25
|
int flags;
|
71
26
|
int fd;
|
72
27
|
|
73
28
|
rb_scan_args(argc, argv, "01", &_flags);
|
74
|
-
flags =
|
29
|
+
flags = rb_sp_get_flags(klass, _flags);
|
75
30
|
|
76
31
|
fd = inotify_init1(flags);
|
77
32
|
if (fd == -1) {
|
@@ -90,11 +45,51 @@ static VALUE s_init(int argc, VALUE *argv, VALUE klass)
|
|
90
45
|
return rv;
|
91
46
|
}
|
92
47
|
|
48
|
+
/*
|
49
|
+
* call-seq:
|
50
|
+
* ino.add_watch(path, flags) -> Integer
|
51
|
+
*
|
52
|
+
* Adds a watch on an object specified by its +mask+, returns an unsigned
|
53
|
+
* Integer watch descriptor. +flags+ may be a mask of the following
|
54
|
+
* Inotify constants or array of their symbolic names.
|
55
|
+
*
|
56
|
+
* - :ACCESS - File was accessed (read) (*)
|
57
|
+
* - :ATTRIB - Metadata changed.
|
58
|
+
* - :CLOSE_WRITE - File opened for writing was closed (*)
|
59
|
+
* - :CLOSE_NOWRITE - File not opened for writing was closed (*)
|
60
|
+
* - :CREATE - File/directory created in watched directory (*)
|
61
|
+
* - :DELETE - File/directory deleted from watched directory (*)
|
62
|
+
* - :DELETE_SELF - Watched file/directory was itself deleted
|
63
|
+
* - :MODIFY - File was modified (*)
|
64
|
+
* - :MOVE_SELF - Watched file/directory was itself moved
|
65
|
+
* - :MOVED_FROM - File moved out of watched directory (*)
|
66
|
+
* - :MOVED_TO - File moved into watched directory (*)
|
67
|
+
* - :OPEN - File was opened (*)
|
68
|
+
*
|
69
|
+
* When monitoring a directory, the events marked with an asterisk (*)
|
70
|
+
* above can occur for files in the directory, in which case the name
|
71
|
+
* field in the Event structure identifies the name of the file in the
|
72
|
+
* directory.
|
73
|
+
*
|
74
|
+
* Shortcut flags:
|
75
|
+
*
|
76
|
+
* - :ALL_EVENTS - a bitmask of all the above events
|
77
|
+
* - :MOVE - :MOVED_FROM or :MOVED_TO
|
78
|
+
* - :CLOSE - :CLOSE_WRITE or :CLOSE_NOWRITE
|
79
|
+
*
|
80
|
+
* The following watch attributes may also be included in flags:
|
81
|
+
*
|
82
|
+
* - :DONT_FOLLOW - don't dereference symlinks (since Linux 2.6.15)
|
83
|
+
* - :EXCL_UNLINK - don't generate unlink events for children (since 2.6.36)
|
84
|
+
* - :MASK_ADD - add events to an existing watch mask if it exists
|
85
|
+
* - :ONESHOT - monitor for one event and then remove it from the watch
|
86
|
+
* - :ONLYDIR - only watch the pathname if it is a directory
|
87
|
+
*/
|
93
88
|
static VALUE add_watch(VALUE self, VALUE path, VALUE vmask)
|
94
89
|
{
|
95
|
-
int fd =
|
96
|
-
const char *pathname =
|
97
|
-
uint32_t mask =
|
90
|
+
int fd = rb_sp_fileno(self);
|
91
|
+
const char *pathname = StringValueCStr(path);
|
92
|
+
uint32_t mask = rb_sp_get_uflags(self, vmask);
|
98
93
|
int rc = inotify_add_watch(fd, pathname, mask);
|
99
94
|
|
100
95
|
if (rc == -1) {
|
@@ -108,10 +103,17 @@ static VALUE add_watch(VALUE self, VALUE path, VALUE vmask)
|
|
108
103
|
return UINT2NUM((uint32_t)rc);
|
109
104
|
}
|
110
105
|
|
106
|
+
/*
|
107
|
+
* call-seq:
|
108
|
+
* ino.rm_watch(watch_descriptor) -> 0
|
109
|
+
*
|
110
|
+
* Removes a watch based on a watch descriptor Integer. The watch
|
111
|
+
* descriptor is a return value given by Inotify#add_watch
|
112
|
+
*/
|
111
113
|
static VALUE rm_watch(VALUE self, VALUE vwd)
|
112
114
|
{
|
113
115
|
uint32_t wd = NUM2UINT(vwd);
|
114
|
-
int fd =
|
116
|
+
int fd = rb_sp_fileno(self);
|
115
117
|
int rc = inotify_rm_watch(fd, wd);
|
116
118
|
|
117
119
|
if (rc == -1)
|
@@ -137,14 +139,32 @@ static VALUE event_new(struct inotify_event *e)
|
|
137
139
|
return rb_struct_new(cEvent, wd, mask, cookie, name);
|
138
140
|
}
|
139
141
|
|
142
|
+
struct inread_args {
|
143
|
+
int fd;
|
144
|
+
struct inotify_event *ptr;
|
145
|
+
long len;
|
146
|
+
};
|
147
|
+
|
148
|
+
static VALUE inread(void *ptr)
|
149
|
+
{
|
150
|
+
struct inread_args *args = ptr;
|
151
|
+
|
152
|
+
return (VALUE)read(args->fd, args->ptr, args->len);
|
153
|
+
}
|
154
|
+
|
155
|
+
/*
|
156
|
+
* call-seq:
|
157
|
+
* ino.take([nonblock]) -> Inotify::Event or nil
|
158
|
+
*
|
159
|
+
* Returns the next Inotify::Event processed. May return +nil+ if +nonblock+
|
160
|
+
* is +true+.
|
161
|
+
*/
|
140
162
|
static VALUE take(int argc, VALUE *argv, VALUE self)
|
141
163
|
{
|
142
|
-
|
143
|
-
VALUE buf
|
164
|
+
struct inread_args args;
|
165
|
+
VALUE buf;
|
144
166
|
VALUE tmp = rb_ivar_get(self, id_inotify_tmp);
|
145
|
-
struct inotify_event *ptr;
|
146
167
|
struct inotify_event *e, *end;
|
147
|
-
long len;
|
148
168
|
ssize_t r;
|
149
169
|
VALUE rv = Qnil;
|
150
170
|
VALUE nonblock;
|
@@ -154,31 +174,41 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
|
|
154
174
|
|
155
175
|
rb_scan_args(argc, argv, "01", &nonblock);
|
156
176
|
|
157
|
-
|
158
|
-
|
177
|
+
args.fd = rb_sp_fileno(self);
|
178
|
+
buf = rb_ivar_get(self, id_inotify_buf);
|
179
|
+
args.len = RSTRING_LEN(buf);
|
180
|
+
args.ptr = (struct inotify_event *)RSTRING_PTR(buf);
|
181
|
+
|
182
|
+
if (RTEST(nonblock))
|
183
|
+
rb_sp_set_nonblock(args.fd);
|
184
|
+
else
|
185
|
+
blocking_io_prepare(args.fd);
|
159
186
|
do {
|
160
|
-
|
161
|
-
r
|
162
|
-
|
187
|
+
r = rb_sp_io_region(inread, &args);
|
188
|
+
if (r == 0 /* Linux < 2.6.21 */
|
189
|
+
||
|
190
|
+
(r < 0 && errno == EINVAL) /* Linux >= 2.6.21 */
|
191
|
+
) {
|
192
|
+
/* resize internal buffer */
|
163
193
|
int newlen;
|
164
|
-
if (len > 0x10000)
|
194
|
+
if (args.len > 0x10000)
|
165
195
|
rb_raise(rb_eRuntimeError, "path too long");
|
166
|
-
if (ioctl(fd, FIONREAD, &newlen) != 0)
|
196
|
+
if (ioctl(args.fd, FIONREAD, &newlen) != 0)
|
167
197
|
rb_sys_fail("ioctl(inotify,FIONREAD)");
|
168
198
|
rb_str_resize(buf, newlen);
|
169
|
-
ptr = (struct inotify_event *)RSTRING_PTR(buf);
|
170
|
-
len = newlen;
|
199
|
+
args.ptr = (struct inotify_event *)RSTRING_PTR(buf);
|
200
|
+
args.len = newlen;
|
171
201
|
} else if (r < 0) {
|
172
|
-
if (errno == EAGAIN) {
|
173
|
-
|
174
|
-
return Qnil;
|
175
|
-
rb_io_wait_readable(fd);
|
202
|
+
if (errno == EAGAIN && RTEST(nonblock)) {
|
203
|
+
return Qnil;
|
176
204
|
} else {
|
177
|
-
|
205
|
+
if (!rb_io_wait_readable(args.fd))
|
206
|
+
rb_sys_fail("read(inotify)");
|
178
207
|
}
|
179
208
|
} else {
|
180
|
-
|
181
|
-
|
209
|
+
/* buffer in userspace to minimize read() calls */
|
210
|
+
end = (struct inotify_event *)((char *)args.ptr + r);
|
211
|
+
for (e = args.ptr; e < end; ) {
|
182
212
|
VALUE event = event_new(e);
|
183
213
|
if (NIL_P(rv))
|
184
214
|
rv = event;
|
@@ -193,6 +223,13 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
|
|
193
223
|
return rv;
|
194
224
|
}
|
195
225
|
|
226
|
+
/*
|
227
|
+
* call-seq:
|
228
|
+
* inotify_event.events => [ :MOVED_TO, ... ]
|
229
|
+
*
|
230
|
+
* Returns an array of symbolic event names based on the contents of
|
231
|
+
* the +mask+ field.
|
232
|
+
*/
|
196
233
|
static VALUE events(VALUE self)
|
197
234
|
{
|
198
235
|
long len = RARRAY_LEN(checks);
|
@@ -213,30 +250,132 @@ static VALUE events(VALUE self)
|
|
213
250
|
return rv;
|
214
251
|
}
|
215
252
|
|
253
|
+
/*
|
254
|
+
* call-seq:
|
255
|
+
* inotify.dup -> another Inotify object
|
256
|
+
*
|
257
|
+
* Duplicates an Inotify object, allowing it to be used in a blocking
|
258
|
+
* fashion in another thread. Ensures duplicated Inotify objects do
|
259
|
+
* not share read buffers, but do share the userspace Array buffer.
|
260
|
+
*/
|
216
261
|
static VALUE init_copy(VALUE dest, VALUE orig)
|
217
262
|
{
|
218
263
|
VALUE tmp;
|
219
264
|
|
220
|
-
dest = rb_call_super(1, &orig);
|
265
|
+
dest = rb_call_super(1, &orig); /* copy all other ivars as-is */
|
221
266
|
rb_ivar_set(dest, id_inotify_buf, rb_str_new(0, 128));
|
222
267
|
|
223
268
|
return dest;
|
224
269
|
}
|
225
270
|
|
271
|
+
/*
|
272
|
+
* call-seq:
|
273
|
+
* ino.each { |event| ... } -> ino
|
274
|
+
*
|
275
|
+
* Yields each Inotify::Event received in a blocking fashion.
|
276
|
+
*/
|
277
|
+
static VALUE each(VALUE self)
|
278
|
+
{
|
279
|
+
VALUE argv = Qfalse;
|
280
|
+
|
281
|
+
while (1)
|
282
|
+
rb_yield(take(0, &argv, self));
|
283
|
+
|
284
|
+
return self;
|
285
|
+
}
|
286
|
+
|
287
|
+
#if defined(NOGVL_CLOSE)
|
288
|
+
static VALUE fptr_close(void *ptr)
|
289
|
+
{
|
290
|
+
rb_io_t *fptr = ptr;
|
291
|
+
return (VALUE)close(fptr->fd);
|
292
|
+
}
|
293
|
+
|
294
|
+
/*
|
295
|
+
* call-seq:
|
296
|
+
* ino.close -> nil
|
297
|
+
*
|
298
|
+
* Closes the underlying file descriptor and releases resources used by the
|
299
|
+
* kernel. Unlike other file descriptors, Inotify descriptors can take
|
300
|
+
* a long time to close(2). Calling this explicitly releases the GVL under
|
301
|
+
* Ruby 1.9
|
302
|
+
*/
|
303
|
+
static VALUE nogvl_close(VALUE self)
|
304
|
+
{
|
305
|
+
rb_io_t *fptr;
|
306
|
+
|
307
|
+
GetOpenFile(self, fptr);
|
308
|
+
|
309
|
+
if ((int)rb_sp_io_region(fptr_close, fptr) < 0)
|
310
|
+
rb_sys_fail("close(inotify)");
|
311
|
+
fptr->fd = -1;
|
312
|
+
|
313
|
+
return Qnil;
|
314
|
+
}
|
315
|
+
#endif /* NOGVL_CLOSE */
|
316
|
+
|
226
317
|
void sleepy_penguin_init_inotify(void)
|
227
318
|
{
|
228
319
|
VALUE mSleepyPenguin, cInotify;
|
229
320
|
|
230
321
|
mSleepyPenguin = rb_define_module("SleepyPenguin");
|
322
|
+
|
323
|
+
/*
|
324
|
+
* Document-class: SleepyPenguin::Inotify
|
325
|
+
*
|
326
|
+
* Inotify objects are used for monitoring file system events,
|
327
|
+
* it can monitor individual files or directories. When a directory
|
328
|
+
* is monitored it will return events for the directory itself and
|
329
|
+
* all files inside the directory.
|
330
|
+
*
|
331
|
+
* Inotify IO objects can be watched using IO.select or Epoll.
|
332
|
+
* IO#close may be called on the object when it is no longer needed.
|
333
|
+
*
|
334
|
+
* Inotify is available on Linux 2.6.13 or later.
|
335
|
+
*
|
336
|
+
* require "sleepy_penguin/sp"
|
337
|
+
* ino = SP::Inotify.new
|
338
|
+
* ino.add_watch("/path/to/foo", :OPEN)
|
339
|
+
* ino.each do |event|
|
340
|
+
* p event.events # => [ :OPEN ]
|
341
|
+
* end
|
342
|
+
*/
|
231
343
|
cInotify = rb_define_class_under(mSleepyPenguin, "Inotify", rb_cIO);
|
232
344
|
rb_define_method(cInotify, "add_watch", add_watch, 2);
|
233
345
|
rb_define_method(cInotify, "rm_watch", rm_watch, 1);
|
234
346
|
rb_define_method(cInotify, "initialize_copy", init_copy, 1);
|
235
347
|
rb_define_method(cInotify, "take", take, -1);
|
236
|
-
|
237
|
-
|
348
|
+
rb_define_method(cInotify, "each", each, 0);
|
349
|
+
#ifdef NOGVL_CLOSE
|
350
|
+
rb_define_method(cInotify, "close", nogvl_close, 0);
|
351
|
+
#endif
|
352
|
+
|
353
|
+
/*
|
354
|
+
* Document-class: SleepyPenguin::Inotify::Event
|
355
|
+
*
|
356
|
+
* Returned by SleepyPenguin::Inotify#take. It is a Struct with the
|
357
|
+
* following elements:
|
358
|
+
*
|
359
|
+
* - wd - watch descriptor (unsigned Integer)
|
360
|
+
* - mask - mask of events (unsigned Integer)
|
361
|
+
* - cookie - unique cookie associated related events (for rename)
|
362
|
+
* - name - optional string name (may be nil)
|
363
|
+
*
|
364
|
+
* The mask is a bitmask of the event flags accepted by
|
365
|
+
* Inotify#add_watch and may also include the following flags:
|
366
|
+
*
|
367
|
+
* - :IGNORED - watch was removed
|
368
|
+
* - :ISDIR - event occured on a directory
|
369
|
+
* - :Q_OVERFLOW - event queue overflowed (wd is -1)
|
370
|
+
* - :UNMOUNT - filesystem containing watched object was unmounted
|
371
|
+
*
|
372
|
+
* Use the Event#events method to get an array of symbols for the
|
373
|
+
* matched events.
|
374
|
+
*/
|
375
|
+
cEvent = rb_struct_define("Event", "wd", "mask", "cookie", "name", 0);
|
376
|
+
cEvent = rb_define_class_under(cInotify, "Event", cEvent);
|
238
377
|
rb_define_method(cEvent, "events", events, 0);
|
239
|
-
rb_define_singleton_method(cInotify, "new",
|
378
|
+
rb_define_singleton_method(cInotify, "new", s_new, -1);
|
240
379
|
id_for_fd = rb_intern("for_fd");
|
241
380
|
id_inotify_buf = rb_intern("@inotify_buf");
|
242
381
|
id_inotify_tmp = rb_intern("@inotify_tmp");
|
@@ -251,8 +390,6 @@ void sleepy_penguin_init_inotify(void)
|
|
251
390
|
rb_ary_push(checks, val); \
|
252
391
|
} while (0)
|
253
392
|
|
254
|
-
rb_define_const(cInotify, "FIONREAD", INT2NUM(FIONREAD));
|
255
|
-
|
256
393
|
IN(ALL_EVENTS);
|
257
394
|
|
258
395
|
/* events a user can watch for */
|
@@ -287,7 +424,8 @@ void sleepy_penguin_init_inotify(void)
|
|
287
424
|
IN(ONESHOT);
|
288
425
|
|
289
426
|
/* for inotify_init1() */
|
290
|
-
|
291
|
-
|
427
|
+
|
428
|
+
NODOC_CONST(cInotify, "NONBLOCK", INT2NUM(IN_NONBLOCK));
|
429
|
+
NODOC_CONST(cInotify, "CLOEXEC", INT2NUM(IN_CLOEXEC));
|
292
430
|
}
|
293
431
|
#endif /* HAVE_SYS_INOTIFY_H */
|