Skip to content

Commit d94ffe1

Browse files
committed
safely create cache with tmp locked string object
1 parent 4f3a5d9 commit d94ffe1

File tree

3 files changed

+60
-1
lines changed

3 files changed

+60
-1
lines changed

lib/memory/cache.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ def lookup_string(obj)
6464
# The string report can still list unique strings longer than 200 characters
6565
# separately because the object_id of the shortened string will be different
6666
@string_cache[obj] ||= String.new << obj[0, 64]
67+
rescue RuntimeError => e
68+
# It is possible for the String to be temporarily locked from another Fiber
69+
# which raises an error when we try to use it as a hash key.
70+
# ie: Socket#read locks a buffer string while reading data into it.
71+
# In this case we dup the string to get an unlocked copy.
72+
if e.message == "can't modify string; temporarily locked"
73+
@string_cache[obj.dup] ||= String.new << obj[0, 64]
74+
else
75+
raise
76+
end
6777
end
6878
end
6979
end

memory.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
77
spec.version = Memory::VERSION
88

99
spec.summary = "Memory profiling routines for Ruby 2.3+"
10-
spec.authors = ["Sam Saffron", "Dave Gynn", "Samuel Williams", "Nick LaMuro", "Jonas Peschla", "Ashwin Maroli", "Søren Skovsbøll", "Richard Schneeman", "Anton Davydov", "Benoit Tigeot", "Jean Boussier", "Vincent Woo", "Andrew Grimm", "Boris Staal", "Danny Ben Shitrit", "Espartaco Palma", "Florian Schwab", "Hamdi Akoğuz", "Jaiden Mispy", "John Bachir", "Luís Ferreira", "Mike Subelsky", "Olle Jonsson", "Vasily Kolesnikov", "William Tabi"]
10+
spec.authors = ["Sam Saffron", "Dave Gynn", "Samuel Williams", "Nick LaMuro", "Jonas Peschla", "Ashwin Maroli", "Søren Skovsbøll", "Richard Schneeman", "Anton Davydov", "Benoit Tigeot", "Jean Boussier", "Vincent Woo", "Andrew Grimm", "Boris Staal", "Danny Ben Shitrit", "Espartaco Palma", "Florian Schwab", "Hamdi Akoğuz", "Jaiden Mispy", "John Bachir", "Luís Ferreira", "Mike Subelsky", "Olle Jonsson", "Vasily Kolesnikov", "William Tabi", "Michael Go"]
1111
spec.license = "MIT"
1212

1313
spec.cert_chain = ["release.cert"]

test/memory/sampler.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
# Copyright, 2020-2025, by Samuel Williams.
55

66
require "memory"
7+
require "socket"
8+
require "fileutils"
79

810
class MyThing
911
end
@@ -39,6 +41,53 @@ class MyThing
3941
expect(allocation.retained).to be_truthy
4042
end
4143

44+
it "safely captures locked string objects" do
45+
socket_path = "/tmp/test_supervisor.ipc"
46+
FileUtils.rm_f(socket_path)
47+
48+
memory = Memory::Sampler.new
49+
memory.start
50+
51+
# Create server thread
52+
Thread.new do
53+
server = UNIXServer.new(socket_path)
54+
client = server.accept
55+
56+
2.times do
57+
buffer = String.new(capacity: 2)
58+
length_data = client.read(2, buffer) # buffer gets locked while reading
59+
break unless length_data && length_data.bytesize == 2
60+
61+
length = length_data.unpack1("n")
62+
client.read(length)
63+
end
64+
ensure
65+
client&.close
66+
server.close
67+
end
68+
69+
# Create a client thread
70+
Thread.new do
71+
socket = UNIXSocket.new(socket_path)
72+
73+
2.times do
74+
message = Time.now.to_s
75+
socket.write([message.bytesize].pack("n") + message)
76+
puts "hello #{message}"
77+
sleep(1)
78+
end
79+
ensure
80+
socket&.close
81+
end
82+
83+
sleep(0.1)
84+
85+
memory.stop
86+
memory.report # buffer string is locked while reading ObjectSpace#each_object
87+
ensure
88+
FileUtils.rm_f(socket_path)
89+
end
90+
4291
with "#as_json" do
4392
it "returns allocation count" do
4493
x = nil

0 commit comments

Comments
 (0)