Repository navigation
Conversation
Declare the C extension Ractor-safe when SQLite is built thread-safe, so that each Ractor can open and use its own connections. This finishes sparklemotion#365, rebased onto main, with these changes: - Fill the statement-status symbol table at Init. It was filled lazily on first use, and the "already filled?" check only looked at slot 0, so a second Ractor could see a partly filled table. - define_aggregator: capture the template and name in closures instead of class instance variables, which can't be set outside the main Ractor (Ractor::IsolationError). - Make VERSION_INFO deeply frozen so any Ractor can read it. - Drop the FULLMUTEX "shareable database" part. A Database also holds Ruby state (registered functions, aggregators, collations, handlers), which can't be shared between Ractors even when SQLite's calls are serialized. - Run the Ractor tests in a child process with a timeout. Once a process has started a Ractor, ObjectSpace.each_object stops seeing objects (Ruby 4.0), which broke test_aggregate_instances_are_released_after_each_query when the Ractor tests ran first. Co-authored-by: Roman Sandler <5535625+sandlerr@users.noreply.github.com> Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org> Co-authored-by: Mike Dalessio <mike.dalessio@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Skip the send-a-Database test before Ruby 3.3, which made a shallow copy of T_DATA objects instead of raising (ruby/ruby@ce47ee00). - Compare only the child's stdout; stderr carries unrelated warnings, such as "Free at exit is experimental". - Unset RUBY_FREE_AT_EXIT for the child processes. With it set, Ruby 4.0.7 itself reads freed memory in ractor_free while tearing down any process that created a Ractor: RUBY_FREE_AT_EXIT=1 valgrind ruby -e 'Ractor.new { 1 }.value' Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
valgrind follows the child processes, and a Ruby process that has created a Ractor can't exit cleanly under it: with RUBY_FREE_AT_EXIT, Ruby 4.0.7 reads freed memory in ractor_free; without it, everything Ruby allocated is reported as leaked. Neither involves sqlite3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
|
Thanks for the contribution, I'll review when I get a chance. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #365, rebased onto main. The original commits' authors are credited as co-authors.
What this does
Declares the C extension Ractor-safe when SQLite is built thread-safe, so each Ractor can open and use its own connections. Passing a
Databasebetween Ractors still raisesRactor::Error(seeRACTOR.md).Changes beyond #365
setup_stmt_stat_symbolswas lazy, and its "already filled?" check only looked at slot 0, so a second Ractor could see a partly filled table. It now runs once ininit_sqlite3_statement.define_aggregatorworks outside the main Ractor. The proxy class kept the template and name in class instance variables, which raisesRactor::IsolationErrorin a non-main Ractor. They are now captured in closures (define_singleton_method). No API change.VERSION_INFOis deeply frozen, so any Ractor can read it.Databasealso holds Ruby state (registered functions, aggregators, collations, handlers) that can't be shared between Ractors. Marking it shareable would expose that state.ObjectSpace.each_objectreturns nothing, which broketest_aggregate_instances_are_released_after_each_querywhenever the Ractor tests ran first. A child process isolates that, and turns a hang into a failure instead of a stalled job. This may account for the hangs seen on Initial Ractor support #365. I only tested on 4.0.5, so CI will tell for the other Rubies.Testing
On Ruby 4.0.5, arm64-darwin:
define_aggregatororVERSION_INFOchange makes the matching new test fail.stress script
A related observation (not addressed here)
If the main Ractor exits while worker Ractors are still in the middle of queries, the process can hang during exit. Ruby's exit-time finalizer frees a leftover
Statement, andsqlite3_finalizethen waits on a connection mutex held by a Ractor that has stopped. I haven't reduced it to a minimal repro yet, so I don't know whether it belongs here or in Ruby. I'll follow up separately.🤖 Generated with Claude Code