From bcb10abe53cfb1d6a8ef7daef59eb10ced397c8a Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 20 Oct 2013 04:17:01 +0000 Subject: log: workaround atomicity issues for stdio vs non-stdio descriptors 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 --- lib/yahns/log.rb | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/lib/yahns/log.rb b/lib/yahns/log.rb index 59baa85..a62b27c 100644 --- a/lib/yahns/log.rb +++ b/lib/yahns/log.rb @@ -41,9 +41,9 @@ module Yahns::Log # :nodoc: ObjectSpace.each_object(File) { |fp| is_log?(fp) and to_reopen << fp } to_reopen.each do |fp| - orig_st = begin - fp.stat - rescue IOError, Errno::EBADF + begin + orig_st = fp.stat + rescue IOError, Errno::EBADF # race next end @@ -54,8 +54,27 @@ module Yahns::Log # :nodoc: 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: 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: -- cgit v1.2.3-24-ge0c7