Skip to content

Commit

Permalink
Ensure the IO always gets closed, exactly once
Browse files Browse the repository at this point in the history
We achieve this by fully relying on Ruby's IO class: we instantiate it
immediately, and trust it to do the right thing.
  • Loading branch information
matthewd authored and ioquatix committed Nov 17, 2017
1 parent 59ccd9c commit c6a161c
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 22 deletions.
3 changes: 0 additions & 3 deletions lib/rb-inotify/native.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,5 @@ class Event < FFI::Struct
attach_function :inotify_add_watch, [:int, :string, :uint32], :int
attach_function :inotify_rm_watch, [:int, :uint32], :int
attach_function :fpathconf, [:int, :int], :long

attach_function :read, [:int, :pointer, :size_t], :ssize_t
attach_function :close, [:int], :int
end
end
33 changes: 14 additions & 19 deletions lib/rb-inotify/notifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,21 @@ class Notifier
# (except under JRuby -- see \{#to\_io}).
#
# @return [Fixnum]
attr_reader :fd
def fd
@handle.fileno
end

# Creates a new {Notifier}.
#
# @return [Notifier]
# @raise [SystemCallError] if inotify failed to initialize for some reason
def initialize
@fd = Native.inotify_init
fd = Native.inotify_init
@watchers = {}
return unless @fd < 0
unless fd < 0
@handle = IO.new(fd)
return
end

raise SystemCallError.new(
"Failed to initialize inotify" +
Expand Down Expand Up @@ -76,7 +81,7 @@ def initialize
# @return [IO] An IO object wrapping the file descriptor
# @raise [NotImplementedError] if this is being called in JRuby
def to_io
@io ||= IO.new(@fd)
@handle
end

# Watches a file or directory for changes,
Expand Down Expand Up @@ -241,17 +246,8 @@ def process
# @raise [SystemCallError] if closing the underlying file descriptor fails.
def close
stop
if Native.close(@fd) == 0
@watchers.clear
return
end

raise SystemCallError.new("Failed to properly close inotify socket" +
case FFI.errno
when Errno::EBADF::Errno; ": invalid or closed file descriptior"
when Errno::EIO::Errno; ": an I/O error occured"
end,
FFI.errno)
@handle.close
@watchers.clear
end

# Blocks until there are one or more filesystem events that this notifier
Expand Down Expand Up @@ -293,11 +289,10 @@ def read_events

# Same as IO#readpartial, or as close as we need.
def readpartial(size)
to_io.readpartial(size)
rescue Errno::EBADF, IOError
@handle.readpartial(size)
rescue Errno::EBADF
# If the IO has already been closed, reading from it will cause
# Errno::EBADF. In JRuby it can raise IOError with invalid or
# closed file descriptor.
# Errno::EBADF.
nil
end
end
Expand Down

0 comments on commit c6a161c

Please sign in to comment.