about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2013-11-07 09:12:58 +0000
committerEric Wong <e@80x24.org>2013-11-07 09:12:58 +0000
commit33eaf25f43fd73d8f4f7b0a066b689809d733191 (patch)
tree125188374272ce1be414b390f7b79f662e8284cd
parent07e5a1d7e8624e57d640af1d641c7ca51a948ecd (diff)
downloadyahns-33eaf25f43fd73d8f4f7b0a066b689809d733191.tar.gz
Our QueueQuitter may race with epoll_wait and GC, potentially
triggering memory corruption if a level-trigger object was
placed into the ready list and no longer referenced.

We'll work around this problem by making the QueueQuitter object
a thread-local (on thread failure) to prevent concurency issues
where the epoll pointer no longer points to a Ruby object.
-rw-r--r--lib/yahns/fdmap.rb5
-rw-r--r--lib/yahns/server.rb4
2 files changed, 9 insertions, 0 deletions
diff --git a/lib/yahns/fdmap.rb b/lib/yahns/fdmap.rb
index a9125e7..5ba4399 100644
--- a/lib/yahns/fdmap.rb
+++ b/lib/yahns/fdmap.rb
@@ -36,6 +36,11 @@ class Yahns::Fdmap # :nodoc:
   # as one which got accept-ed (a brand new IO object) so we must prevent
   # IO#close in worker threads from racing with any threads which may run
   # __expire
+  #
+  # We must never, ever call this while it is capable of being on the
+  # epoll ready list and returnable by epoll_wait.  So we can only call
+  # this on objects which were epoll_ctl-ed with EPOLLONESHOT (and now
+  # retrieved).
   def sync_close(io)
     @fdmap_mtx.synchronize do
       @count -= 1
diff --git a/lib/yahns/server.rb b/lib/yahns/server.rb
index f956d50..ddba8f2 100644
--- a/lib/yahns/server.rb
+++ b/lib/yahns/server.rb
@@ -416,6 +416,10 @@ class Yahns::Server # :nodoc:
 
     # cleanup, our job is done
     @queues.each(&:close).clear
+
+    # we must not let quitter get GC-ed if we have any worker threads leftover
+    @wthr.each { |t| t[:yahns_quitter] = quitter }
+
     quitter.close
   rescue => e
     Yahns::Log.exception(@logger, "quit finish", e)