Project

General

Profile

Actions

Feature #22067

closed

New RUBY_TYPED_THREAD_SAFE_FREE bit to declare thread safe dfree functions

Feature #22067: New RUBY_TYPED_THREAD_SAFE_FREE bit to declare thread safe dfree functions

Added by luke-gru (Luke Gruber) about 2 months ago. Updated 14 days ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:125475]

Description

CRuby TypedData types are used internally in the VM and in C extensions. Currently any dfree functions, which usually are declared RUBY_TYPED_FREE_IMMEDIATELY, are run with the VM lock held and a VM barrier stopping all ractors and threads. In short, no other Ruby threads or GC threads can run while these free functions are called. To be specific these can be run any time an allocation (newobj or xmalloc) happens (or CHECK_INTS), which is when GC may trigger. The function will be called synchronously and finish before the allocation returns.

Though much more relaxed than what's required without RUBY_TYPED_FREE_IMMEDIATELY, this expectation limits what GC implementations can do.

To allow more flexibility in GC implementations, we want to let worker GC threads free these TypedData objects concurrently with other Ruby code or in parallel with other GC threads.

Most TypedData dfree functions are simply calls to xfree and are trivially thread safe. The exceptions are dfree functions that read or modify global state without locking.

Examples

Safe

static void
safe_data_free(void *ptr)
{
    // thread safe
    safe_data *data = ptr;
    xfree(data->buffer);
    xfree(data);
}

Unsafe due to shared state

static void
example_data_free(void *ptr)
{
    st_delete(live_example_datas, (st_data_t*)&ptr, NULL); // Not thread-safe!
}

There are two reasons this would not be thread-safe to free:

  • Unsafe for parallel free - 2 of these TypedData objects could be freed at the same time, corrupting the st_table
  • Unsafe for concurrent free - live_example_datas would be inserted into and iterated by Ruby code

For types like this which mutate a piece of shared state, they should either not be declared as thread-safe or some form of locking should be used when manipulating the table.

Most types are trivially thread-safe, but there are enough exceptions in CRuby itself that it needs to be opt-in, the same way RUBY_TYPED_FREE_IMMEDIATELY is.

Proposal

We propose adding a new flag to TypedData, RUBY_TYPED_THREAD_SAFE_FREE that allows CRuby itself and extension authors to opt-in to declaring their TypedData type as thread safe.

Proposed documentation to doc/extension.rdoc

RUBY_TYPED_THREAD_SAFE_FREE ::

  This flag declares that the dfree() function is thread-safe.

  With this flag set the garbage collector MAY invoke dfree()
    * On any thread, whether a Ruby Thread or a native thread
    * In parallel with calls to the same or other dfree functions
    * Concurrently with other Ruby code

  This should NOT be set on dfree() functions which access shared state in a
  thread-unsafe way. A dfree() function which uses only xfree() on the struct
  and memory it exclusively owns is thread-safe.

  This flag implies RUBY_TYPED_FREE_IMMEDIATELY, since a thread-safe dfree()
  is also safe to invoke immediately.

Per that doc, this is purely a more strict dfree constraint from the TypedData implementer's perspective, and more flexible from the GC author's perspective. We can treat anything with this flag set the same as RUBY_TYPED_FREE_IMMEDIATELY (or the same as no flag set, they are purely incremental in how "friendly" the dfree function is).

Many gems can s/RUBY_TYPED_FREE_IMMEDIATELY/RUBY_TYPED_THREAD_SAFE_FREE/g with a cursory audit. It's helpful to have one imply the other to avoid cluttering the flags (and they happen to be the exact same length 馃槄).

This forms a series of bits with increasingly relaxed constraints on when dfree may run: no flag (mutator thread, holding the GVL, at an interrupt check), RUBY_TYPED_FREE_IMMEDIATELY (synchronously during GC, world stopped), and RUBY_TYPED_THREAD_SAFE_FREE (any thread, in parallel, concurrent with Ruby code).


Files

Screenshot_20260513_185452.webp (99.8 KB) Screenshot_20260513_185452.webp A GC with the MMTk GC module running railsbench wks (Kunshan Wang), 05/13/2026 11:13 AM
Screenshot_20260513_192449.webp (31.9 KB) Screenshot_20260513_192449.webp Default GC running railsbench wks (Kunshan Wang), 05/13/2026 11:30 AM
Screenshot_20260513_192734.webp (22.4 KB) Screenshot_20260513_192734.webp Default GC zoom-in 1 wks (Kunshan Wang), 05/13/2026 11:30 AM
Screenshot_20260513_192910.webp (27.3 KB) Screenshot_20260513_192910.webp Defautl GC zoom-in 2 wks (Kunshan Wang), 05/13/2026 11:30 AM
Actions

Also available in: PDF Atom