From: Eric Wong <normalperson@yhbt.net>
To: sleepy.penguin@librelist.org
Subject: [sleepy.penguin] [PATCH 04/17] fork-safe "to_io" in high-level epoll/kqueue
Date: Tue, 30 Apr 2013 02:39:29 +0000 [thread overview]
Message-ID: <1367289582-31293-5-git-send-email-normalperson@yhbt.net> (raw)
In-Reply-To: 1367289582-31293-1-git-send-email-normalperson@yhbt.net
We need to validate the underlying IO object before using
it in a forked child.
---
lib/sleepy_penguin/epoll.rb | 51 ++++++++++++++++++++++++--------------------
lib/sleepy_penguin/kqueue.rb | 39 ++++++++++++++++++---------------
2 files changed, 50 insertions(+), 40 deletions(-)
diff --git a/lib/sleepy_penguin/epoll.rb b/lib/sleepy_penguin/epoll.rb
index 8d78e46..f29189a 100644
--- a/lib/sleepy_penguin/epoll.rb
+++ b/lib/sleepy_penguin/epoll.rb
@@ -1,45 +1,50 @@
require 'thread'
class SleepyPenguin::Epoll
- # Epoll objects may be watched by IO.select and similar methods
- attr_reader :to_io
-
# 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+).
def initialize(create_flags = nil)
- @to_io = SleepyPenguin::Epoll::IO.new(create_flags)
+ @io = SleepyPenguin::Epoll::IO.new(create_flags)
@mtx = Mutex.new
@events = []
@marks = []
@pid = $$
@create_flags = create_flags
- @copies = { @to_io => self }
+ @copies = { @io => self }
end
def __ep_reinit # :nodoc:
@events.clear
@marks.clear
- @to_io = SleepyPenguin::Epoll::IO.new(@create_flags)
+ @io = SleepyPenguin::Epoll::IO.new(@create_flags)
end
# auto-reinitialize the Epoll object after forking
def __ep_check # :nodoc:
return if @pid == $$
- return if @to_io.closed?
+ return if @io.closed?
objects = @copies.values
@copies.each_key { |epio| epio.close }
@copies.clear
__ep_reinit
objects.each do |obj|
- io_dup = @to_io.dup
+ io_dup = @io.dup
@copies[io_dup] = obj
end
@pid = $$
end
+ # Epoll objects may be watched by IO.select and similar methods
+ def to_io
+ @mtx.synchronize do
+ __ep_check
+ @io
+ end
+ end
+
# Calls epoll_wait(2) and yields Integer +events+ and IO objects watched
# for. +maxevents+ is the maximum number of events to process at once,
# lower numbers may prevent starvation when used by epoll_wait in multiple
@@ -59,7 +64,7 @@ def wait(maxevents = 64, timeout = nil)
# we keep a snapshot of @marks around in case another thread closes
# the IO while it is being transferred to userspace. We release mtx
# so another thread may add events to us while we're sleeping.
- @to_io.epoll_wait(maxevents, timeout) { |events, io| yield(events, io) }
+ @io.epoll_wait(maxevents, timeout) { |events, io| yield(events, io) }
ensure
# hopefully Ruby does not optimize this array away...
snapshot[0]
@@ -72,7 +77,7 @@ def add(io, events)
events = __event_flags(events)
@mtx.synchronize do
__ep_check
- @to_io.epoll_ctl(CTL_ADD, io, events)
+ @io.epoll_ctl(CTL_ADD, io, events)
@events[fd] = events
@marks[fd] = io
end
@@ -87,7 +92,7 @@ def del(io)
fd = io.to_io.fileno
@mtx.synchronize do
__ep_check
- @to_io.epoll_ctl(CTL_DEL, io, 0)
+ @io.epoll_ctl(CTL_DEL, io, 0)
@events[fd] = @marks[fd] = nil
end
0
@@ -110,7 +115,7 @@ def delete(io)
__ep_check
cur_io = @marks[fd]
return if nil == cur_io || cur_io.to_io.closed?
- @to_io.epoll_ctl(CTL_DEL, io, 0)
+ @io.epoll_ctl(CTL_DEL, io, 0)
@events[fd] = @marks[fd] = nil
end
io
@@ -127,7 +132,7 @@ def mod(io, events)
fd = io.to_io.fileno
@mtx.synchronize do
__ep_check
- @to_io.epoll_ctl(CTL_MOD, io, events)
+ @io.epoll_ctl(CTL_MOD, io, events)
@marks[fd] = io # may be a different object with same fd/file
@events[fd] = events
end
@@ -159,18 +164,18 @@ def set(io, events)
cur_events = @events[fd]
return 0 if (cur_events & ONESHOT) == 0 && cur_events == events
begin
- @to_io.epoll_ctl(CTL_MOD, io, events)
+ @io.epoll_ctl(CTL_MOD, io, events)
rescue Errno::ENOENT
warn "epoll event cache failed (mod -> add)"
- @to_io.epoll_ctl(CTL_ADD, io, events)
+ @io.epoll_ctl(CTL_ADD, io, events)
@marks[fd] = io
end
else
begin
- @to_io.epoll_ctl(CTL_ADD, io, events)
+ @io.epoll_ctl(CTL_ADD, io, events)
rescue Errno::EEXIST
warn "epoll event cache failed (add -> mod)"
- @to_io.epoll_ctl(CTL_MOD, io, events)
+ @io.epoll_ctl(CTL_MOD, io, events)
end
@marks[fd] = io
end
@@ -186,8 +191,8 @@ def set(io, events)
# Raises IOError if object is already closed.
def close
@mtx.synchronize do
- @copies.delete(@to_io)
- @to_io.close
+ @copies.delete(@io)
+ @io.close
end
end
@@ -197,7 +202,7 @@ def close
# Returns whether or not an Epoll object is closed.
def closed?
@mtx.synchronize do
- @to_io.closed?
+ @io.closed?
end
end
@@ -254,9 +259,9 @@ def initialize_copy(src) # :nodoc:
@mtx.synchronize do
__ep_check
rv = super
- unless @to_io.closed?
- @to_io = @to_io.dup
- @copies[@to_io] = self
+ unless @io.closed?
+ @io = @io.dup
+ @copies[@io] = self
end
rv
end
diff --git a/lib/sleepy_penguin/kqueue.rb b/lib/sleepy_penguin/kqueue.rb
index fbbde8a..1eeb641 100644
--- a/lib/sleepy_penguin/kqueue.rb
+++ b/lib/sleepy_penguin/kqueue.rb
@@ -8,23 +8,28 @@
# Events registered to a Kqueue object cannot be shared across fork
# due to the underlying implementation of kqueue in *BSDs.
class SleepyPenguin::Kqueue
- # Kqueue objects may be watched by IO.select and similar methods
- attr_reader :to_io
-
def initialize
- @to_io = SleepyPenguin::Kqueue::IO.new
+ @io = SleepyPenguin::Kqueue::IO.new
@mtx = Mutex.new
@pid = $$
- @copies = { @to_io => self }
+ @copies = { @io => self }
+ end
+
+ # Kqueue objects may be watched by IO.select and similar methods
+ def to_io
+ @mtx.synchronize do
+ __kq_check
+ @io
+ end
end
def __kq_reinit # :nodoc:
- @to_io = SleepyPenguin::Kqueue::IO.new
+ @io = SleepyPenguin::Kqueue::IO.new
end
def __kq_check # :nodoc:
- return if @pid == $$ || @to_io.closed?
- unless @to_io.respond_to?(:autoclose=)
+ return if @pid == $$ || @io.closed?
+ unless @io.respond_to?(:autoclose=)
raise RuntimeError,
"Kqueue is not safe to use without IO#autoclose=, upgrade to Ruby 1.9+"
end
@@ -35,7 +40,7 @@ def __kq_check # :nodoc:
@copies.clear
__kq_reinit
objects.each do |obj|
- io_dup = @to_io.dup
+ io_dup = @io.dup
@copies[io_dup] = obj
end
@pid = $$
@@ -61,7 +66,7 @@ def kevent(changelist = nil, *args)
end
if block_given?
- n = @to_io.kevent(changelist, *args) do |ident,filter,flags,
+ n = @io.kevent(changelist, *args) do |ident,filter,flags,
fflags,data,udata|
# This may raise and cause events to be lost,
# that's the users' fault/problem
@@ -70,7 +75,7 @@ def kevent(changelist = nil, *args)
fflags, data, udata)
end
else
- n = @to_io.kevent(changelist, *args)
+ n = @io.kevent(changelist, *args)
end
end
@@ -78,9 +83,9 @@ def initialize_copy(src) # :nodoc:
@mtx.synchronize do
__kq_check
rv = super
- unless @to_io.closed?
- @to_io = @to_io.dup
- @copies[@to_io] = self
+ unless @io.closed?
+ @io = @io.dup
+ @copies[@io] = self
end
rv
end
@@ -93,8 +98,8 @@ def initialize_copy(src) # :nodoc:
# Raises IOError if object is already closed.
def close
@mtx.synchronize do
- @copies.delete(@to_io)
- @to_io.close
+ @copies.delete(@io)
+ @io.close
end
end
@@ -104,7 +109,7 @@ def close
# Returns whether or not an Kqueue object is closed.
def closed?
@mtx.synchronize do
- @to_io.closed?
+ @io.closed?
end
end
end
--
1.8.2.1.367.gc875ca7
next prev parent reply other threads:[~2013-04-30 2:40 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-04-30 2:39 [sleepy.penguin] [PATCH 0/17] kqueue and epoll fixes Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 01/17] test_epoll: remove assert_nothing_raised Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 02/17] test: remove Rubinius-specific checks and skips Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 03/17] test_epoll: avoid sleeping inside a signal handler Eric Wong
2013-04-30 2:39 ` Eric Wong [this message]
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 05/17] test_kqueue: join thread after test Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 06/17] test_kqueue_io: test for multiple event return Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 07/17] test_timerfd: relax timing-sensitive test Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 08/17] kqueue: set zero timeout if not retrieving events Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 09/17] test_epoll: workaround MRI 1.8 threading bug Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 10/17] test_kqueue_io: join thread in test when done using Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 11/17] test_kqueue: only test if IO#autoclose= exists Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 12/17] kqueue/io: fix MRI 1.8 support code for event retrieval Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 13/17] kqueue: workaround lack of RSTRUCT* macros on Rubinius Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 14/17] test_epoll: join thread before return from test Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 15/17] test_epoll: increase delay between signal spamming Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 16/17] epoll: clear FD marks snapshot before returning Eric Wong
2013-04-30 2:39 ` [sleepy.penguin] [PATCH 17/17] test_epoll: workaround race condition in test_close Eric Wong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://yhbt.net/sleepy_penguin/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1367289582-31293-5-git-send-email-normalperson@yhbt.net \
--to=normalperson@yhbt.net \
--cc=sleepy.penguin@librelist.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://yhbt.net/sleepy_penguin.git/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).