[ruby-core:62590] Re: [ruby-trunk - Bug #9835] IGNORE signal handler has possible race condition in Ruby 2.1.2

From: Eric Wong <normalperson@...>
Date: 2014-05-14 19:28:00 UTC
List: ruby-core #62590
It looks like a race condition in your code.  You were lucky to not hit
it in 1.8.7 or with a different use of trap (because execution speeds
may be different different).

    parent execution timeline
    -----------------------------+
    trap(USR1, show_children)    |
                                 |
    fork ------------------------+-- child execution timeline -----
                                 |
    *** kill(USR1, -getpgid($$)) | trap(USR1, IGNORE)
                                 |

Once you fork, parent and child run in parallel.  So the order of
execution where '***' is highlighted is outside your control.

So to ensure your child ignores properly, you must setup the ignore
handler before forking.  There will always be a moment in time where
the parent and child will have the same signal handler.

You also do not want to a signal in the parent which arrive immediately
before or after the fork, either.  So I suggest something like the
following to defer signals in the parent:

    # note: this count nals in general) is racy
    usr1_queue = []
    show_children = trap("USR1") { usr1_queue << nil } # defer signals
    pid = fork
    if pid # parent
      trap("USR1", show_children) # restore immediate handling

      # process deferred signals
      usr1_queue.each { show_children.call }
      usr1_queue.clear
      ...
    else # child
      trap("USR1", "IGNORE")
      # do nothing with usr1_queue here, implicitly ignored
      ...
    end

In This Thread

Prev Next