diff options
author | Eric Wong <e@80x24.org> | 2013-10-20 04:29:55 +0000 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2013-10-20 04:32:04 +0000 |
commit | d90eebe1e50e2bdb9632b64591e4b84cbc0049a1 (patch) | |
tree | d6db0ea4f2300d1ba628bb1d4a0442e0ee37c375 /lib/unicorn/util.rb | |
parent | a9dfd48f9668d0a6e04cf009cea0c4ede962144d (diff) | |
download | unicorn-d90eebe1e50e2bdb9632b64591e4b84cbc0049a1.tar.gz |
In multithreaded apps, we must use dup2/dup3 with a temporary descriptor to reopen log files atomically. This is the only way to protect all concurrent userspace access to a file when reopening. ref: http://bugs.ruby-lang.org/issues/9036 ref: yahns commit bcb10abe53cfb1d6a8ef7daef59eb10ced397c8a
Diffstat (limited to 'lib/unicorn/util.rb')
-rw-r--r-- | lib/unicorn/util.rb | 24 |
1 files changed, 22 insertions, 2 deletions
diff --git a/lib/unicorn/util.rb b/lib/unicorn/util.rb index f84241c..94c4e37 100644 --- a/lib/unicorn/util.rb +++ b/lib/unicorn/util.rb @@ -39,7 +39,7 @@ module Unicorn::Util to_reopen.each do |fp| orig_st = begin fp.stat - rescue IOError, Errno::EBADF + rescue IOError, Errno::EBADF # race next end @@ -50,8 +50,28 @@ module Unicorn::Util end begin - File.open(fp.path, 'a') { |tmpfp| fp.reopen(tmpfp) } + # stdin, stdout, stderr are special. The following dance should + # guarantee there is no window where `fp' is unwritable in MRI + # (or any correct Ruby implementation). + # + # Fwiw, GVL has zero bearing here. This is tricky because of + # the unavoidable existence of stdio FILE * pointers for + # std{in,out,err} in all programs which may use the standard C library + if fp.fileno <= 2 + # We do not want to hit fclose(3)->dup(2) window for std{in,out,err} + # MRI will use freopen(3) here internally on std{in,out,err} + fp.reopen(fp.path, "a") + else + # We should not need this workaround, Ruby can be fixed: + # http://bugs.ruby-lang.org/issues/9036 + # MRI will not call call fclose(3) or freopen(3) here + # since there's no associated std{in,out,err} FILE * pointer + # This should atomically use dup3(2) (or dup2(2)) syscall + File.open(fp.path, "a") { |tmpfp| fp.reopen(tmpfp) } + end + fp.sync = true + fp.flush # IO#sync=true may not implicitly flush new_st = fp.stat # this should only happen in the master: |