-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add workaround to handle zero sized files in /proc #3411
Conversation
@@ -417,6 +417,11 @@ class File < IO::FileDescriptor | |||
# ``` | |||
def self.read(filename, encoding = nil, invalid = nil) : String | |||
File.open(filename, "r") do |file| | |||
{% if flag?(:linux) %} | |||
# workaround to handle zero sized files in /proc Linux virtual filesystem | |||
encoding ||= "UTF-8" if File.expand_path(filename) =~ /^\/proc/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use starts_with?
? Regexes are quite slow and unnecessary for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be more clear to add a specific boolean variable that makes it enter the gets_to_end
branch instead of overloading encoding?
@@ -37,6 +37,13 @@ describe "File" do | |||
str.should eq("Hello World\n" * 20) | |||
end | |||
|
|||
{% if !flag?(:windows) %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be {% if flag?(:linux) %}
; this doesn't work on OSX.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, /proc
is only pretty much guaranteed to be mounted on windows, but it can be mounted on most unixes.
I would fix this by changing |
8479c45
to
b3c826c
Compare
@@ -417,6 +417,10 @@ class File < IO::FileDescriptor | |||
# ``` | |||
def self.read(filename, encoding = nil, invalid = nil) : String | |||
File.open(filename, "r") do |file| | |||
{% if flag?(:linux) %} | |||
return file.gets_to_end if File.expand_path(filename).starts_with?("/proc") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now ignores encoding for /proc*
.
@Val we finally decided with @waj that the safest thing to do, even if it's a bit slower, is to always invoke def self.read(filename, encoding = nil, invalid = nil) : String
File.open(filename, "r") do |file|
file.set_encoding(encoding, invalid: invalid) if encoding
file.gets_to_end
end
end But I'll let you change this in your PR because you have the specs, and you found the bug :-) |
@asterite I think |
path = "/proc/self/cmdline"
def read1(filename, encoding = nil, invalid = nil) : String
File.open(filename, "r") do |file|
if encoding
file.set_encoding(encoding, invalid: invalid)
file.gets_to_end
else
{% if flag?(:linux) %}
return file.gets_to_end if File.expand_path(filename).starts_with?("/proc")
{% end %}
size = file.size.to_i
String.new(size) do |buffer|
file.read Slice.new(buffer, size)
{size.to_i, 0}
end
end
end
end
def read2(filename, encoding = nil, invalid = nil) : String
File.open(filename, "r") do |file|
file.set_encoding(encoding, invalid: invalid) if encoding
file.gets_to_end
end
end
require "benchmark"
Benchmark.ips do |x|
x.report("read1") { read1 path }
x.report("read2") { read2 path }
end On a Debian jessie amd64:
|
@Val can you also test on various sizes of normal file, say 1kb, 1mb,10mb? |
path = "/tmp/#{File.basename(__FILE__)}"
def read1(filename, encoding = nil, invalid = nil) : String
File.open(filename, "r") do |file|
if encoding
file.set_encoding(encoding, invalid: invalid)
file.gets_to_end
else
{% if flag?(:linux) %}
return file.gets_to_end if File.expand_path(filename).starts_with?("/proc")
{% end %}
size = file.size.to_i
String.new(size) do |buffer|
file.read Slice.new(buffer, size)
{size.to_i, 0}
end
end
end
end
def read2(filename, encoding = nil, invalid = nil) : String
File.open(filename, "r") do |file|
file.set_encoding(encoding, invalid: invalid) if encoding
file.gets_to_end
end
end
BLOBS = {} of Symbol => String
kb_01 = BLOBS[:kb_01] = "X" * 1024
mb_01 = BLOBS[:mb_01] = kb_01 * 1024
BLOBS[:mb_10] = mb_01 * 10
require "benchmark"
BLOBS.each do |name, blob|
File.open(path, "w") { |f| f.print blob }
puts "#{name} _____________________________________\n"
Benchmark.ips do |x|
x.report("read1 #{name}") { read1 path }
x.report("read2 #{name}") { read2 path }
end
puts
end
File.delete path Output
|
Subtle, changing
|
Fix #3410