about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--.document3
-rw-r--r--LICENSE4
-rw-r--r--README4
-rw-r--r--ext/sleepy_penguin/epoll.c166
-rw-r--r--ext/sleepy_penguin/eventfd.c80
-rw-r--r--ext/sleepy_penguin/inotify.c116
-rw-r--r--ext/sleepy_penguin/signalfd.c64
-rw-r--r--ext/sleepy_penguin/sleepy_penguin.h3
-rw-r--r--ext/sleepy_penguin/timerfd.c59
-rw-r--r--lib/sleepy_penguin/signalfd/sig_info.rb3
-rw-r--r--lib/sleepy_penguin/sp.rb (renamed from lib/sp.rb)2
11 files changed, 430 insertions, 74 deletions
diff --git a/.document b/.document
index 50ea20c..87f32d8 100644
--- a/.document
+++ b/.document
@@ -7,4 +7,7 @@ lib
 ext/sleepy_penguin/init.c
 ext/sleepy_penguin/epoll.c
 ext/sleepy_penguin/eventfd.c
+ext/sleepy_penguin/init.c
+ext/sleepy_penguin/inotify.c
+ext/sleepy_penguin/signalfd.c
 ext/sleepy_penguin/timerfd.c
diff --git a/LICENSE b/LICENSE
index 814ed0d..9cb1ff0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -5,8 +5,8 @@ You can redistribute it and/or modify it under the terms of the GNU
 Lesser General Public License (LGPL) as published by the Free Software
 Foundation, version {2.1}[http://www.gnu.org/licenses/lgpl-2.1.txt] or
 or {3}[http://www.gnu.org/licenses/lgpl-3.0.txt] (see link:COPYING).
-The project leader (Eric Wong) reserves the right to relicense raindrops
-under future versions of the LGPL.
+The project leader (Eric Wong) reserves the right to relicense
+sleepy_penguin under future versions of the LGPL.
 
 sleepy_penguin is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
diff --git a/README b/README
index 02d8e54..4cfd24d 100644
--- a/README
+++ b/README
@@ -2,13 +2,13 @@
 
 sleepy_penguin provides access to newer, Linux-only system calls to wait
 on events from traditionally non-I/O sources.  Bindings to the eventfd,
-timerfd, and epoll interfaces are provided.
+timerfd, inotify, signalfd and epoll interfaces are provided.
 
 == Features
 
 * Thread-safe blocking operations under both Ruby 1.8 and 1.9.
 
-* Mostly working under Rubinius
+* Mostly working under Rubinius and Ruby 1.9
 
 * IO-like objects are backwards-compatible with IO.select.
 
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);
diff --git a/lib/sleepy_penguin/signalfd/sig_info.rb b/lib/sleepy_penguin/signalfd/sig_info.rb
index d750621..492360a 100644
--- a/lib/sleepy_penguin/signalfd/sig_info.rb
+++ b/lib/sleepy_penguin/signalfd/sig_info.rb
@@ -1,4 +1,6 @@
+# :stopdoc:
 class SleepyPenguin::SignalFD::SigInfo
+
   def to_hash
     Hash[*MEMBERS.inject([]) { |ary,k| ary << k << __send__(k) }]
   end
@@ -15,3 +17,4 @@ class SleepyPenguin::SignalFD::SigInfo
     other.kind_of?(self.class) && to_hash == other.to_hash
   end
 end
+# :startdoc:
diff --git a/lib/sp.rb b/lib/sleepy_penguin/sp.rb
index 89f9ca5..62e000f 100644
--- a/lib/sp.rb
+++ b/lib/sleepy_penguin/sp.rb
@@ -1,2 +1,4 @@
+# :stopdoc:
 require "sleepy_penguin"
 SP = SleepyPenguin
+# :startdoc: