Skip to content

Commit 61a2e94

Browse files
peterzhu2118HParker
andcommitted
Fix memory leak in Hash#rehash for ST hashes
We need to free the old ST table in Hash#rehash. Co-authored-by: Adam Hess <adamhess1991@gmail.com>
1 parent d80002c commit 61a2e94

File tree

2 files changed

+24
-8
lines changed

2 files changed

+24
-8
lines changed

hash.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -685,9 +685,8 @@ ar_find_entry(VALUE hash, st_hash_t hash_value, st_data_t key)
685685
return ar_find_entry_hint(hash, hint, key);
686686
}
687687

688-
//old one
689688
static inline void
690-
ar_free_and_clear_table(VALUE hash)
689+
hash_ar_free_and_clear_table(VALUE hash)
691690
{
692691
RHASH_AR_TABLE_CLEAR(hash);
693692

@@ -715,7 +714,7 @@ ar_try_convert_table(VALUE hash)
715714
st_add_direct(new_tab, pair->key, pair->val);
716715
}
717716

718-
ar_free_and_clear_table(hash);
717+
hash_ar_free_and_clear_table(hash);
719718
RHASH_ST_TABLE_SET(hash, new_tab);
720719
}
721720

@@ -742,7 +741,7 @@ ar_force_convert_table(VALUE hash, const char *file, int line)
742741
ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
743742
st_add_direct(new_tab, pair->key, pair->val);
744743
}
745-
ar_free_and_clear_table(hash);
744+
hash_ar_free_and_clear_table(hash);
746745
}
747746

748747
RHASH_ST_TABLE_SET(hash, new_tab);
@@ -1161,7 +1160,7 @@ ar_clear(VALUE hash)
11611160
}
11621161

11631162
static void
1164-
st_free_and_clear_table(VALUE hash)
1163+
hash_st_free_and_clear_table(VALUE hash)
11651164
{
11661165
HASH_ASSERT(RHASH_ST_TABLE_P(hash));
11671166

@@ -1962,7 +1961,8 @@ rb_hash_rehash(VALUE hash)
19621961
if (RHASH_AR_TABLE_P(hash)) {
19631962
tmp = hash_alloc(0);
19641963
rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
1965-
ar_free_and_clear_table(hash);
1964+
1965+
hash_ar_free_and_clear_table(hash);
19661966
ar_copy(hash, tmp);
19671967
}
19681968
else if (RHASH_ST_TABLE_P(hash)) {
@@ -1974,6 +1974,7 @@ rb_hash_rehash(VALUE hash)
19741974

19751975
rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
19761976

1977+
hash_st_free_and_clear_table(hash);
19771978
RHASH_ST_TABLE_SET(hash, tbl);
19781979
RHASH_ST_CLEAR(tmp);
19791980
}
@@ -2906,10 +2907,10 @@ rb_hash_replace(VALUE hash, VALUE hash2)
29062907
COPY_DEFAULT(hash, hash2);
29072908

29082909
if (RHASH_AR_TABLE_P(hash)) {
2909-
ar_free_and_clear_table(hash);
2910+
hash_ar_free_and_clear_table(hash);
29102911
}
29112912
else {
2912-
st_free_and_clear_table(hash);
2913+
hash_st_free_and_clear_table(hash);
29132914
}
29142915

29152916
hash_copy(hash, hash2);

test/ruby/test_hash.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,21 @@ def test_rehash
744744
assert_equal(100, h[a])
745745
end
746746

747+
def test_rehash_memory_leak
748+
assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true)
749+
ar_hash = 1.times.map { |i| [i, i] }.to_h
750+
st_hash = 10.times.map { |i| [i, i] }.to_h
751+
752+
code = proc do
753+
ar_hash.rehash
754+
st_hash.rehash
755+
end
756+
1_000.times(&code)
757+
PREP
758+
1_000_000.times(&code)
759+
CODE
760+
end
761+
747762
def test_reject
748763
assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].reject {|k, v| k + v < 7 })
749764

0 commit comments

Comments
 (0)