diff options
author | Eric Wong <normalperson@yhbt.net> | 2011-03-10 02:12:46 +0000 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2011-03-10 02:12:46 +0000 |
commit | f04b6e5d6e5ad7a5144e4a01f113f24af32c8d25 (patch) | |
tree | 8c6cad36304f8b0c6f8feb0cc9395dc01d5f65a0 /ext | |
parent | 01d0b34524668a52261bc026b22bc74401db71b6 (diff) | |
download | sleepy_penguin-f04b6e5d6e5ad7a5144e4a01f113f24af32c8d25.tar.gz |
Might as well, since it forces me to understand these interfaces, too :)
Diffstat (limited to 'ext')
-rw-r--r-- | ext/sleepy_penguin/epoll.c | 166 | ||||
-rw-r--r-- | ext/sleepy_penguin/eventfd.c | 80 | ||||
-rw-r--r-- | ext/sleepy_penguin/inotify.c | 116 | ||||
-rw-r--r-- | ext/sleepy_penguin/signalfd.c | 64 | ||||
-rw-r--r-- | ext/sleepy_penguin/sleepy_penguin.h | 3 | ||||
-rw-r--r-- | ext/sleepy_penguin/timerfd.c | 59 |
6 files changed, 418 insertions, 70 deletions
diff --git a/ext/sleepy_penguin/epoll.c b/ext/sleepy_penguin/epoll.c index 395bd7f..7af159d 100644 --- a/ext/sleepy_penguin/epoll.c +++ b/ext/sleepy_penguin/epoll.c @@ -149,10 +149,11 @@ static void ep_check(struct rb_epoll *ep) } /* - * creates a new Epoll object with an optional +flags+ argument. - * +flags+ may currently be Epoll::CLOEXEC or +0+ (or +nil+) - * Epoll::CLOEXEC will be set by default if +nil+ or no - * argument is passed. + * call-seq: + * SleepyPenguin::Epoll.new([flags]) -> Epoll object + * + * Creates a new Epoll object with an optional +flags+ argument. + * +flags+ may currently be +:CLOEXEC+ or +0+ (or +nil+). */ static VALUE init(int argc, VALUE *argv, VALUE self) { @@ -203,8 +204,19 @@ static VALUE ctl(VALUE self, VALUE io, VALUE flags, int op) } /* - * used to avoid exceptions when your app is too lazy to check - * what state a descriptor is in + * call-seq: + * ep.set(io, flags) -> 0 + * + * Used to avoid exceptions when your app is too lazy to check + * what state a descriptor is in, this sets the epoll descriptor + * to watch an +io+ with the given +flags+ + * + * +flags+ may be an array of symbols or an unsigned Integer bit mask: + * + * - flags = [ :IN, :ET ] + * - flags = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ET + * + * See constants in Epoll for more information. */ static VALUE set(VALUE self, VALUE io, VALUE flags) { @@ -256,9 +268,12 @@ fallback_add: } /* - * Deletes an +io+ from an epoll set, but returns nil - * on ENOENT instead of raising an error. This is useful - * for apps that do not care to track the status of an + * call-seq: + * epoll.delete(io) -> io or nil + * + * Stops an +io+ object from being monitored. This is like Epoll#del + * but returns +nil+ on ENOENT instead of raising an error. This is + * useful for apps that do not care to track the status of an * epoll object itself. */ static VALUE delete(VALUE self, VALUE io) @@ -427,11 +442,14 @@ static VALUE real_epwait(struct rb_epoll *ep) #endif /* 1.8 Green thread compatibility code */ /* - * Calls epoll_wait(2) and yields + * call-seq: + * epoll.wait([maxevents[, timeout]]) { |flags, io| ... } * - * :call-seq: - * - * epoll.wait(64, 1000) { |flags, obj| ... } + * Calls epoll_wait(2) and yields Integer +flags+ and IO objects watched + * for. +maxevents+ is the maximum number of events to process at once, + * lower numbers may prevent starvation when used by dup-ed Epoll objects + * in multiple threads. +timeout+ is specified in milliseconds, +nil+ + * (the default) meaning it will block and wait indefinitely. */ static VALUE epwait(int argc, VALUE *argv, VALUE self) { @@ -453,23 +471,51 @@ static VALUE epwait(int argc, VALUE *argv, VALUE self) return real_epwait(ep); } -/* adds +io+ object the +self+ with +flags+ */ +/* + * call-seq: + * epoll.add(io, flags) -> 0 + * + * Starts watching a given +io+ object with +flags+ which may be an Integer + * bitmask or Array representing arrays to watch for. Consider Epoll#set + * instead as it is easier to use. + */ static VALUE add(VALUE self, VALUE io, VALUE flags) { return ctl(self, io, flags, EPOLL_CTL_ADD); } -/* adds +io+ object the +self+ with +flags+ */ +/* + * call-seq: + * epoll.del(io) -> 0 + * + * Disables an IO object from being watched. Consider Epoll#delete as + * it is easier to use. + */ static VALUE del(VALUE self, VALUE io) { - return ctl(self, io, INT2NUM(0), EPOLL_CTL_DEL); + return ctl(self, io, INT2FIX(0), EPOLL_CTL_DEL); } +/* + * call-seq: + * epoll.mod(io, flags) -> 0 + * + * Changes the watch for an existing IO object based on +flags+. + * Consider Epoll#set instead as it is easier to use. + */ static VALUE mod(VALUE self, VALUE io, VALUE flags) { return ctl(self, io, flags, EPOLL_CTL_MOD); } +/* + * call-seq: + * epoll.to_io -> Epoll::IO object + * + * Used to expose the given Epoll object as an Epoll::IO object for IO.select + * or IO#stat. This is unlikely to be useful directly, but is used internally + * by IO.select. + */ static VALUE to_io(VALUE self) { struct rb_epoll *ep = ep_get(self); @@ -482,6 +528,13 @@ static VALUE to_io(VALUE self) return ep->io; } +/* + * call-seq: + * epoll.close -> nil + * + * Closes an existing Epoll object and returns memory back to the kernel. + * Raises IOError if object is already closed. + */ static VALUE epclose(VALUE self) { struct rb_epoll *ep = ep_get(self); @@ -511,6 +564,12 @@ static VALUE epclose(VALUE self) return Qnil; } +/* + * call-seq: + * epoll.closed? -> true or false + * + * Returns whether or not an Epoll object is closed. + */ static VALUE epclosed(VALUE self) { struct rb_epoll *ep = ep_get(self); @@ -523,7 +582,7 @@ static int cloexec_dup(struct rb_epoll *ep) #ifdef F_DUPFD_CLOEXEC int flags = ep->flags & EPOLL_CLOEXEC ? F_DUPFD_CLOEXEC : F_DUPFD; int fd = fcntl(ep->fd, flags, 0); -#else +#else /* potentially racy on GVL-free systems: */ int fd = dup(ep->fd); if (fd >= 0) (void)fcntl(fd, F_SETFD, FD_CLOEXEC); @@ -531,6 +590,15 @@ static int cloexec_dup(struct rb_epoll *ep) return fd; } +/* + * call-seq: + * epoll.dup -> another Epoll object + * + * Duplicates an Epoll object and userspace buffers related to this library. + * This allows the same epoll object in the Linux kernel to be safely used + * across multiple native threads as long as there is one SleepyPenguin::Epoll + * object per-thread. + */ static VALUE init_copy(VALUE copy, VALUE orig) { struct rb_epoll *a = ep_get(orig); @@ -561,6 +629,14 @@ static VALUE init_copy(VALUE copy, VALUE orig) return copy; } +/* + * call-seq: + * epoll.io_for(io) -> object + * + * Returns the given IO object currently being watched for. Different + * IO objects may internally refer to the same process file descriptor. + * Mostly used for debugging. + */ static VALUE io_for(VALUE self, VALUE obj) { struct rb_epoll *ep = ep_get(self); @@ -569,11 +645,11 @@ static VALUE io_for(VALUE self, VALUE obj) } /* - * :call-seq: - * - * epoll.flags_for(io) => Integer + * call-seq: + * epoll.flags_for(io) -> Integer * * Returns the flags currently watched for in current Epoll object. + * Mostly used for debugging. */ static VALUE flags_for(VALUE self, VALUE obj) { @@ -583,8 +659,7 @@ static VALUE flags_for(VALUE self, VALUE obj) } /* - * :call-seq: - * + * call-seq: * epoll.include?(io) => true or false * * Returns whether or not a given IO is watched and prevented from being @@ -630,8 +705,47 @@ void sleepy_penguin_init_epoll(void) { VALUE mSleepyPenguin, cEpoll; + /* + * Document-module: SleepyPenguin + * + * require "sleepy_penguin" + * include SleepyPenguin + * + * The SleepyPenguin namespace includes the Epoll, Inotify, SignalFD, + * TimerFD, EventFD classes in its top level and no other constants. + * + * If you are uncomfortable including SleepyPenguin, you may also + * use the "SP" alias if it doesn't conflict with existing code: + * + * require "sleepy_penguin/sp" + * + * And then access classes via: + * + * - SP::Epoll + * - SP::EventFD + * - SP::Inotify + * - SP::SignalFD + * - SP::TimerFD + */ mSleepyPenguin = rb_define_module("SleepyPenguin"); + + /* + * Document-class: SleepyPenguin::Epoll + * + * The Epoll class provides access to epoll(7) functionality in the + * Linux 2.6 kernel. It provides fork and GC-safety for Ruby + * objects stored within the IO object and may be passed as an + * argument to IO.select. + */ cEpoll = rb_define_class_under(mSleepyPenguin, "Epoll", rb_cObject); + + /* + * Document-class: SleepyPenguin::Epoll::IO + * + * Epoll::IO is an internal class. Its only purpose is to be + * compatible with IO.select and related methods and should not + * be used directly, use Epoll instead. + */ cEpoll_IO = rb_define_class_under(cEpoll, "IO", rb_cIO); rb_define_method(cEpoll, "initialize", init, -1); rb_define_method(cEpoll, "initialize_copy", init_copy, 1); @@ -649,7 +763,7 @@ void sleepy_penguin_init_epoll(void) rb_define_method(cEpoll, "set", set, 2); rb_define_method(cEpoll, "wait", epwait, -1); - /* specifies wheter close-on-exec flag is set for Epoll.new */ + /* specifies whether close-on-exec flag is set for Epoll.new */ rb_define_const(cEpoll, "CLOEXEC", INT2NUM(EPOLL_CLOEXEC)); /* watch for read/recv operations */ @@ -659,9 +773,13 @@ void sleepy_penguin_init_epoll(void) rb_define_const(cEpoll, "OUT", UINT2NUM(EPOLLOUT)); #ifdef EPOLLRDHUP - /* watch a specified IO for shutdown(SHUT_WR) on the remote-end */ + /* + * Watch a specified io for shutdown(SHUT_WR) on the remote-end. + * Available since Linux 2.6.17. + */ rb_define_const(cEpoll, "RDHUP", UINT2NUM(EPOLLRDHUP)); #endif + /* watch for urgent read(2) data */ rb_define_const(cEpoll, "PRI", UINT2NUM(EPOLLPRI)); diff --git a/ext/sleepy_penguin/eventfd.c b/ext/sleepy_penguin/eventfd.c index 704df7e..e7d7daf 100644 --- a/ext/sleepy_penguin/eventfd.c +++ b/ext/sleepy_penguin/eventfd.c @@ -4,6 +4,22 @@ #include "nonblock.h" static ID id_for_fd; +/* + * call-seq: + * EventFD.new(initial_value [, flags]) -> EventFD IO object + * + * Creates an EventFD object. +initial_value+ is a non-negative Integer + * to start the internal counter at. + * + * Starting with Linux 2.6.27, +flags+ may be a mask that consists of any + * of the following: + * + * - :CLOEXEC - set the close-on-exec flag on the new object + * - :NONBLOCK - set the non-blocking I/O flag on the new object + * + * Since Linux 2.6.30, +flags+ may also include: + * - :SEMAPHORE - provides semaphore-like semantics (see EventFD#value) + */ static VALUE s_new(int argc, VALUE *argv, VALUE klass) { VALUE _initval, _flags; @@ -50,6 +66,14 @@ static VALUE efd_read(void *_args) return (VALUE)r; } +/* + * call-seq: + * efd.incr(integer_value) -> nil + * + * Increments the internal counter by +integer_value+ which is an unsigned + * Integer value. This will block if the internal counter will overflow + * the value of EventFD::MAX + */ static VALUE incr(VALUE self, VALUE value) { struct efd_args x; @@ -69,6 +93,19 @@ retry: return Qnil; } +/* + * call-seq: + * efd.value -> Integer + * + * If not created as a semaphore, returns the current value and resets + * the counter to zero. + * + * If created as a semaphore, this decrements the counter value by one + * and returns one. + * + * If the counter is zero at the time of the call, this will block until + * the counter becomes non-zero. + */ static VALUE getvalue(VALUE self) { struct efd_args x; @@ -125,7 +162,15 @@ retry: } #endif /* !HAVE_RB_THREAD_BLOCKING_REGION */ -static VALUE getvalue_nonblock(VALUE self) +/* + * call-seq: + * efd.value_nonblock -> Integer + * + * Exactly like EventFD#value, but forces the EventFD object + * into non-blocking mode if it is not already and raises Errno::EAGAIN + * if it is not ready for reading. + */ +static VALUE value_nonblock(VALUE self) { int fd = rb_sp_fileno(self); uint64_t val; @@ -139,6 +184,14 @@ static VALUE getvalue_nonblock(VALUE self) return ULL2NUM(val); } +/* + * call-seq: + * efd.incr_nonblock(integer_value) -> nil + * + * Exactly like EventFD#incr, but forces the EventFD object + * into non-blocking mode if it is not already and raises Errno::EAGAIN + * if it may overflow. + */ static VALUE incr_nonblock(VALUE self, VALUE value) { int fd = rb_sp_fileno(self); @@ -158,20 +211,37 @@ void sleepy_penguin_init_eventfd(void) VALUE mSleepyPenguin, cEventFD; mSleepyPenguin = rb_define_module("SleepyPenguin"); + + /* + * Document-class: SleepyPenguin::EventFD + * + * Applications may use EventFD instead of a pipe in cases where + * a pipe is only used to signal events. The kernel overhead for + * an EventFD descriptor is much lower than that of a pipe. + * + * As of Linux 2.6.30, an EventFD may also be used as a semaphore. + */ cEventFD = rb_define_class_under(mSleepyPenguin, "EventFD", rb_cIO); rb_define_singleton_method(cEventFD, "new", s_new, -1); + + /* + * the maximum value that may be stored in an EventFD, + * currently 0xfffffffffffffffe + */ + rb_define_const(cEventFD, "MAX", ULL2NUM(0xfffffffffffffffe)); + #ifdef EFD_NONBLOCK - rb_define_const(cEventFD, "NONBLOCK", INT2NUM(EFD_NONBLOCK)); + NODOC_CONST(cEventFD, "NONBLOCK", INT2NUM(EFD_NONBLOCK)); #endif #ifdef EFD_CLOEXEC - rb_define_const(cEventFD, "CLOEXEC", INT2NUM(EFD_CLOEXEC)); + NODOC_CONST(cEventFD, "CLOEXEC", INT2NUM(EFD_CLOEXEC)); #endif #ifdef EFD_SEMAPHORE - rb_define_const(cEventFD, "SEMAPHORE", INT2NUM(EFD_SEMAPHORE)); + NODOC_CONST(cEventFD, "SEMAPHORE", INT2NUM(EFD_SEMAPHORE)); #endif rb_define_method(cEventFD, "value", getvalue, 0); rb_define_method(cEventFD, "incr", incr, 1); - rb_define_method(cEventFD, "value_nonblock", getvalue_nonblock, 0); + rb_define_method(cEventFD, "value_nonblock", value_nonblock, 0); rb_define_method(cEventFD, "incr_nonblock", incr_nonblock, 1); id_for_fd = rb_intern("for_fd"); } diff --git a/ext/sleepy_penguin/inotify.c b/ext/sleepy_penguin/inotify.c index 28477c3..b1602a3 100644 --- a/ext/sleepy_penguin/inotify.c +++ b/ext/sleepy_penguin/inotify.c @@ -66,9 +66,11 @@ fcntl_err: /* * call-seq: - * include SleepyPenguin - * Inotify.new -> Inotify IO object - * Inotify.new(Inotify::CLOEXEC) -> Inotify IO object + * Inotify.new([flags]) -> Inotify IO object + * + * Flags may be any of the following as an Array of Symbols or Integer mask: + * - :NONBLOCK - sets the non-blocking flag on the descriptor watched. + * - :CLOEXEC - sets the close-on-exec flag */ static VALUE s_new(int argc, VALUE *argv, VALUE klass) { @@ -98,11 +100,43 @@ static VALUE s_new(int argc, VALUE *argv, VALUE klass) /* * call-seq: - * include SleepyPenguin - * in.add_watch("/path/to/something", Inotify::MODIFY) -> Integer + * in.add_watch(path, flags) -> Integer + * + * Adds a watch on an object specified by its +mask+, returns an unsigned + * Integer watch descriptor. +flags+ may be a mask of the following + * Inotify constants or array of their symbolic names. + * + * - :ACCESS - File was accessed (read) (*) + * - :ATTRIB - Metadata changed. + * - :CLOSE_WRITE - File opened for writing was closed (*) + * - :CLOSE_NOWRITE - File not opened for writing was closed (*) + * - :CREATE - File/directory created in watched directory (*) + * - :DELETE - File/directory deleted from watched directory (*) + * - :DELETE_SELF - Watched file/directory was itself deleted + * - :MODIFY - File was modified (*) + * - :MOVE_SELF - Watched file/directory was itself moved + * - :MOVED_FROM - File moved out of watched directory (*) + * - :MOVED_TO - File moved into watched directory (*) + * - :OPEN - File was opened (*) + * + * When monitoring a directory, the events marked with an asterisk (*) + * above can occur for files in the directory, in which case the name + * field in the Event structure identifies the name of the file in the + * directory. + * + * Shortcut flags: + * + * - :ALL_EVENTS - a bitmask of all the above events + * - :MOVE - :MOVED_FROM or :MOVED_TO + * - :CLOSE - :CLOSE_WRITE or :CLOSE_NOWRITE + * + * The following watch attributes may also be included in flags: * - * Adds a watch on an object specified by its mask, returns an unsigned - * Integer watch descriptor. + * - :DONT_FOLLOW - don't dereference symlinks (since Linux 2.6.15) + * - :EXCL_UNLINK - don't generate unlink events for children (since 2.6.36) + * - :MASK_ADD - add events to an existing watch mask if it exists + * - :ONESHOT - monitor for one event and then remove it from the watch + * - :ONLYDIR - only watch the pathname if it is a directory */ static VALUE add_watch(VALUE self, VALUE path, VALUE vmask) { @@ -124,7 +158,7 @@ static VALUE add_watch(VALUE self, VALUE path, VALUE vmask) /* * call-seq: - * in.rm_watch(watch_descriptor) -> 0 + * in.rm_watch(watch_descriptor) -> 0 * * Removes a watch based on a watch descriptor Integer. The watch * descriptor is a return value given by Inotify#add_watch @@ -160,11 +194,10 @@ static VALUE event_new(struct inotify_event *e) /* * call-seq: + * in.take([nonblock]) -> Inotify::Event or nil * - * in.take -> Inotify::Event - * in.take(true) -> Inotify::Event or nil - * - * Returns the next Inotify::Event processed. + * Returns the next Inotify::Event processed. May return +nil+ if +nonblock+ + * is +true+. */ static VALUE take(int argc, VALUE *argv, VALUE self) { @@ -228,7 +261,8 @@ static VALUE take(int argc, VALUE *argv, VALUE self) * call-seq: * inotify_event.events => [ :MOVED_TO, ... ] * - * Returns an array of symbolic event names based on the contents of #mask + * Returns an array of symbolic event names based on the contents of + * the +mask+ field. */ static VALUE events(VALUE self) { @@ -251,8 +285,12 @@ static VALUE events(VALUE self) } /* - * Ensure duplicated Inotify objects do not share read buffers, - * but do share the userspace Array buffer. + * call-seq: + * inotify.dup -> another Inotify object + * + * Duplicates an Inotify object, allowing it to be used in a blocking + * fashion in another thread. Ensures duplicated Inotify objects do + * not share read buffers, but do share the userspace Array buffer. */ static VALUE init_copy(VALUE dest, VALUE orig) { @@ -269,13 +307,50 @@ void sleepy_penguin_init_inotify(void) VALUE mSleepyPenguin, cInotify; mSleepyPenguin = rb_define_module("SleepyPenguin"); + + /* + * Document-class: SleepyPenguin::Inotify + * + * Inotify objects are used for monitoring file system events, + * it can monitor individual files or directories. When a directory + * is monitored it will return events for the directory itself and + * all files inside the directory. + * + * Inotify IO objects can be watched using IO.select or Epoll. + * IO#close may be called on the object when it is no longer needed. + * + * Inotify is available on Linux 2.6.13 or later. + */ cInotify = rb_define_class_under(mSleepyPenguin, "Inotify", rb_cIO); rb_define_method(cInotify, "add_watch", add_watch, 2); rb_define_method(cInotify, "rm_watch", rm_watch, 1); rb_define_method(cInotify, "initialize_copy", init_copy, 1); rb_define_method(cInotify, "take", take, -1); - cEvent = rb_struct_define(NULL, "wd", "mask", "cookie", "name", NULL); - rb_define_const(cInotify, "Event", cEvent); + + /* + * Document-class: SleepyPenguin::Inotify::Event + * + * Returned by SleepyPenguin::Inotify#take. It is a Struct with the + * following elements: + * + * - wd - watch descriptor (unsigned Integer) + * - mask - mask of events (unsigned Integer) + * - cookie - unique cookie associated related events (for rename) + * - name - optional string name (may be nil) + * + * The mask is a bitmask of the event flags accepted by + * Inotify#add_watch and may also include the following flags: + * + * - :IGNORED - watch was removed + * - :ISDIR - event occured on a directory + * - :Q_OVERFLOW - event queue overflowed (wd is -1) + * - :UNMOUNT - filesystem containing watched object was unmounted + * + * Use the Event#events method to get an array of symbols for the + * matched events. + */ + cEvent = rb_struct_define("Event", "wd", "mask", "cookie", "name", 0); + cEvent = rb_define_class_under(cInotify, "Event", cEvent); rb_define_method(cEvent, "events", events, 0); rb_define_singleton_method(cInotify, "new", s_new, -1); id_for_fd = rb_intern("for_fd"); @@ -292,8 +367,6 @@ void sleepy_penguin_init_inotify(void) rb_ary_push(checks, val); \ } while (0) - rb_define_const(cInotify, "FIONREAD", INT2NUM(FIONREAD)); - IN(ALL_EVENTS); /* events a user can watch for */ @@ -328,7 +401,8 @@ void sleepy_penguin_init_inotify(void) IN(ONESHOT); /* for inotify_init1() */ - rb_define_const(cInotify, "NONBLOCK", INT2NUM(IN_NONBLOCK)); - rb_define_const(cInotify, "CLOEXEC", INT2NUM(IN_CLOEXEC)); + + NODOC_CONST(cInotify, "NONBLOCK", INT2NUM(IN_NONBLOCK)); + NODOC_CONST(cInotify, "CLOEXEC", INT2NUM(IN_CLOEXEC)); } #endif /* HAVE_SYS_INOTIFY_H */ diff --git a/ext/sleepy_penguin/signalfd.c b/ext/sleepy_penguin/signalfd.c index d7d7434..8bb88f6 100644 --- a/ext/sleepy_penguin/signalfd.c +++ b/ext/sleepy_penguin/signalfd.c @@ -79,13 +79,9 @@ static int cur_flags(int fd) /* * call-seq: - * include SleepyPenguin - * sfd = SignalFD.new - * ... - * sfd.update! signals - * sfd.update! signals, flags + * sfd.update!(signals[, flags]) -> sfd * - * Updates the signal mask watched for by the given +sigfd+. + * Updates the signal mask watched for by the given +sfd+. * Takes the same arguments as SignalFD.new. */ static VALUE update_bang(int argc, VALUE *argv, VALUE self) @@ -108,10 +104,7 @@ static VALUE update_bang(int argc, VALUE *argv, VALUE self) /* * call-seq: - * include SleepyPenguin - * SignalFD.new -> SignalFD IO object - * SignalFD.new(signals) -> SignalFD IO object - * SignalFD.new(signals, flags) -> SignalFD IO object + * SignalFD.new(signals[, flags]) -> SignalFD IO object * * Creates a new SignalFD object to watch given +signals+ with +flags+. * @@ -122,7 +115,11 @@ static VALUE update_bang(int argc, VALUE *argv, VALUE self) * signals = :USR1 * signals = 15 * - * +flags+ is a mask of SignalFD::CLOEXEC and SignalFD::NONBLOCK + * Starting with Linux 2.6.27, +flags+ may be a mask that consists of any + * of the following: + * + * - :CLOEXEC - set the close-on-exec flag on the new object + * - :NONBLOCK - set the non-blocking I/O flag on the new object */ static VALUE s_new(int argc, VALUE *argv, VALUE klass) { @@ -155,6 +152,7 @@ static VALUE ssi_alloc(VALUE klass) return Data_Wrap_Struct(klass, NULL, -1, ssi); } +/* :nodoc: */ static VALUE ssi_init(VALUE self) { struct signalfd_siginfo *ssi = DATA_PTR(self); @@ -242,25 +240,61 @@ void sleepy_penguin_init_signalfd(void) VALUE mSleepyPenguin, cSignalFD; mSleepyPenguin = rb_define_module("SleepyPenguin"); + + /* + * Document-class: SleepyPenguin::SignalFD + * + * A SignalFD is an IO object for accepting signals. It provides + * an alternative to Signal.trap that may be monitored using + * IO.select or Epoll. + * + * It is not supported under (Matz) Ruby 1.8 + */ cSignalFD = rb_define_class_under(mSleepyPenguin, "SignalFD", rb_cIO); - cSigInfo = rb_define_class_under(cSignalFD, "SigInfo", rb_cObject); + /* + * Document-class: SleepyPenguin::SignalFD::SigInfo + * + * This class is returned by SignalFD#take. It consists of the + * following read-only members: + * + * - signo - signal number + * - errno - error number + * - code - signal code + * - pid - PID of sender + * - uid - real UID of sender + * - fd - file descriptor (SIGIO) + * - tid - kernel timer ID (POSIX timers) + * - band - band event (SIGIO) + * - overrun - POSIX timer overrun count + * - trapno - trap number that caused hardware-generated signal + * - exit status or signal (SIGCHLD) + * - int - integer sent by sigqueue(2) + * - ptr - Pointer sent by sigqueue(2) + * - utime - User CPU time consumed (SIGCHLD) + * - stime - System CPU time consumed (SIGCHLD) + * - addr - address that generated a hardware-generated signal + */ + cSigInfo = rb_define_class_under(cSignalFD, "SigInfo", rb_cObject); rb_define_alloc_func(cSigInfo, ssi_alloc); rb_define_private_method(cSigInfo, "initialize", ssi_init, 0); + /* TODO: si_code values */ + rb_define_singleton_method(cSignalFD, "new", s_new, -1); #ifdef SFD_NONBLOCK - rb_define_const(cSignalFD, "NONBLOCK", INT2NUM(SFD_NONBLOCK)); + NODOC_CONST(cSignalFD, "NONBLOCK", INT2NUM(SFD_NONBLOCK)); #endif #ifdef SFD_CLOEXEC - rb_define_const(cSignalFD, "CLOEXEC", INT2NUM(SFD_CLOEXEC)); + NODOC_CONST(cSignalFD, "CLOEXEC", INT2NUM(SFD_CLOEXEC)); #endif rb_define_method(cSignalFD, "take", sfd_take, 0); rb_define_method(cSignalFD, "update!", update_bang, -1); id_for_fd = rb_intern("for_fd"); ssi_members = rb_ary_new(); - rb_define_const(cSigInfo, "MEMBERS", ssi_members); + + NODOC_CONST(cSigInfo, "MEMBERS", ssi_members); #define SSI_READER(FIELD) do { \ rb_define_method(cSigInfo, #FIELD, ssi_##FIELD, 0); \ diff --git a/ext/sleepy_penguin/sleepy_penguin.h b/ext/sleepy_penguin/sleepy_penguin.h index ce71f58..dff414b 100644 --- a/ext/sleepy_penguin/sleepy_penguin.h +++ b/ext/sleepy_penguin/sleepy_penguin.h @@ -21,4 +21,7 @@ int rb_sp_fileno(VALUE io); #define get_flags rb_sp_get_flags #define my_io_closed rb_sp_io_closed #define my_fileno rb_sp_fileno + +#define NODOC_CONST(klass,name,value) \ + rb_define_const((klass),(name),(value)) #endif /* SLEEPY_PENGUIN_H */ diff --git a/ext/sleepy_penguin/timerfd.c b/ext/sleepy_penguin/timerfd.c index dccb255..edfb629 100644 --- a/ext/sleepy_penguin/timerfd.c +++ b/ext/sleepy_penguin/timerfd.c @@ -4,6 +4,22 @@ #include "value2timespec.h" static ID id_for_fd; +/* + * call-seq: + * TimerFD.new([clockid[, flags]]) -> TimerFD IO object + * + * Creates a new timer as an IO object. + * + * If set +clockid+ must be be one of the following: + * - :REALTIME - use the settable clock + * - :MONOTONIC - use the non-settable clock unaffected by manual changes + * + * +clockid+ defaults to :MONOTONIC if unspecified + * +flags+ may be any or none of the following: + * + * - :CLOEXEC - set the close-on-exec flag on the new object + * - :NONBLOCK - set the non-blocking I/O flag on the new object + */ static VALUE s_new(int argc, VALUE *argv, VALUE klass) { VALUE cid, fl; @@ -35,6 +51,18 @@ static VALUE itimerspec2ary(struct itimerspec *its) return rb_ary_new3(2, interval, value); } +/* + * call-seq: + * tfd.settime(flags, interval, value) -> [ old_interval, old_value ] + * + * Arms (starts) or disarms (stops) the timer referred by the TimerFD object + * and returns the old value of the timer. + * + * +flags+ is either zero (or nil) to start a relative timer or :ABSTIME + * to start an absolute timer. If the +interval+ is zero, the timer fires + * only once, otherwise the timer is fired every +interval+ seconds. + * +value+ is the time of the initial expiration in seconds. + */ static VALUE settime(VALUE self, VALUE fl, VALUE interval, VALUE value) { int fd = rb_sp_fileno(self); @@ -50,6 +78,12 @@ static VALUE settime(VALUE self, VALUE fl, VALUE interval, VALUE value) return itimerspec2ary(&old); } +/* + * call-seq: + * tfd#gettime -> [ interval, value ] + * + * Returns the current +interval+ and +value+ of the timer as an Array. + */ static VALUE gettime(VALUE self) { int fd = rb_sp_fileno(self); @@ -71,6 +105,13 @@ static VALUE tfd_read(void *args) return (VALUE)r; } +/* + * call-seq: + * tfd.expirations -> Integer + * + * Returns the number of expirations that have occurred. This will block + * if no expirations have occurred at the time of the call. + */ static VALUE expirations(VALUE self) { ssize_t r; @@ -108,16 +149,24 @@ void sleepy_penguin_init_timerfd(void) VALUE mSleepyPenguin, cTimerFD; mSleepyPenguin = rb_define_module("SleepyPenguin"); + + /* + * Document-class: SleepyPenguin::TimerFD + * + * TimerFD exposes kernel timers as IO objects that may be monitored + * by IO.select or Epoll. IO#close disarms the timers and returns + * resources back to the kernel. + */ cTimerFD = rb_define_class_under(mSleepyPenguin, "TimerFD", rb_cIO); rb_define_singleton_method(cTimerFD, "new", s_new, -1); - rb_define_const(cTimerFD, "REALTIME", UINT2NUM(CLOCK_REALTIME)); - rb_define_const(cTimerFD, "MONOTONIC", UINT2NUM(CLOCK_MONOTONIC)); - rb_define_const(cTimerFD, "ABSTIME", UINT2NUM(TFD_TIMER_ABSTIME)); + NODOC_CONST(cTimerFD, "REALTIME", UINT2NUM(CLOCK_REALTIME)); + NODOC_CONST(cTimerFD, "MONOTONIC", UINT2NUM(CLOCK_MONOTONIC)); + NODOC_CONST(cTimerFD, "ABSTIME", UINT2NUM(TFD_TIMER_ABSTIME)); #ifdef TFD_NONBLOCK - rb_define_const(cTimerFD, "NONBLOCK", UINT2NUM(TFD_NONBLOCK)); + NODOC_CONST(cTimerFD, "NONBLOCK", UINT2NUM(TFD_NONBLOCK)); #endif #ifdef TFD_CLOEXEC - rb_define_const(cTimerFD, "CLOEXEC", UINT2NUM(TFD_CLOEXEC)); + NODOC_CONST(cTimerFD, "CLOEXEC", UINT2NUM(TFD_CLOEXEC)); #endif rb_define_method(cTimerFD, "settime", settime, 3); |