Introduce free function to rb_concurrent_set_funcs

If we create a key but don't insert it (due to other Ractor winning the
race), then it would leak memory if we don't free it. This introduces a
new function to free that memory for this case.
This commit is contained in:
Peter Zhu 2025-07-18 10:58:41 -04:00
parent 061224f3cb
commit 66349692f0
4 changed files with 22 additions and 7 deletions

View file

@ -304,6 +304,14 @@ rb_concurrent_set_find_or_insert(VALUE *set_obj_ptr, VALUE key, void *data)
if (set->funcs->cmp(key, curr_key)) {
// We've found a match.
RB_GC_GUARD(set_obj);
if (inserting) {
// We created key using set->funcs->create, but we didn't end
// up inserting it into the set. Free it here to prevent memory
// leaks.
if (set->funcs->free) set->funcs->free(key);
}
return curr_key;
}

View file

@ -4,14 +4,11 @@
#include "ruby/atomic.h"
#include "ruby/ruby.h"
typedef VALUE (*rb_concurrent_set_hash_func)(VALUE key);
typedef bool (*rb_concurrent_set_cmp_func)(VALUE a, VALUE b);
typedef VALUE (*rb_concurrent_set_create_func)(VALUE key, void *data);
struct rb_concurrent_set_funcs {
rb_concurrent_set_hash_func hash;
rb_concurrent_set_cmp_func cmp;
rb_concurrent_set_create_func create;
VALUE (*hash)(VALUE key);
bool (*cmp)(VALUE a, VALUE b);
VALUE (*create)(VALUE key, void *data);
void (*free)(VALUE key);
};
VALUE rb_concurrent_set_new(const struct rb_concurrent_set_funcs *funcs, int capacity);

View file

@ -552,6 +552,7 @@ static const struct rb_concurrent_set_funcs fstring_concurrent_set_funcs = {
.hash = fstring_concurrent_set_hash,
.cmp = fstring_concurrent_set_cmp,
.create = fstring_concurrent_set_create,
.free = NULL,
};
void

View file

@ -315,10 +315,19 @@ sym_set_create(VALUE sym, void *data)
}
}
static void
sym_set_free(VALUE sym)
{
if (sym_set_sym_static_p(sym)) {
xfree(sym_set_static_sym_untag(sym));
}
}
static const struct rb_concurrent_set_funcs sym_set_funcs = {
.hash = sym_set_hash,
.cmp = sym_set_cmp,
.create = sym_set_create,
.free = sym_set_free,
};
static VALUE