Module: Kino::Check
- Defined in:
- lib/kino/check.rb
Overview
The shareability doctor behind kino --check: explains WHY an app
can't run in :ractor mode, instead of leaving you to decode
Ractor::IsolationError one ivar at a time.
The walk is strictly non-mutating: Ractor.make_shareable would freeze the user's object graph, so we never call it. Instead we recurse into whatever Ractor.shareable? rejects and name the leaves: instance variables by path, proc captures by variable name and definition site, and the class-ivar trap that bites class-style apps (a Class is always "shareable", but reading its unshareable ivars from a worker ractor raises on the first request).
Defined Under Namespace
Classes: Finding
Constant Summary collapse
- MAX_FINDINGS =
Stop after this many findings: the first few name the problem.
20- MAX_NODES =
Walk budget, so a pathological object graph cannot hang the check.
5_000
Class Method Summary collapse
-
.print_report(app, io: $stdout) ⇒ Boolean
Pretty-printed report; returns true when the app is ractor-ready.
-
.report(app) ⇒ Hash
+Boolean, findings: Array
+.
Class Method Details
.print_report(app, io: $stdout) ⇒ Boolean
Pretty-printed report; returns true when the app is ractor-ready.
57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/kino/check.rb', line 57 def print_report(app, io: $stdout) result = report(app) if result[:shareable] io.puts CLI.paint("32", "check: app is Ractor-shareable — mode :ractor will work", io: io) true else io.puts CLI.red("check: app is NOT Ractor-shareable", io: io) result[:findings].each { |finding| io.puts " - #{finding}" } io.puts dim_hint(io) false end end |
.report(app) ⇒ Hash
Returns +Boolean, findings: Array
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/kino/check.rb', line 34 def report(app) findings = [] seen = {}.compare_by_identity budget = {nodes: 0} if app.is_a?(Module) # Classes/modules pass Ractor.shareable? unconditionally, but their # unshareable class-level state is main-ractor-only at runtime. scan_module(app, "app (#{app.inspect})", findings) {shareable: findings.empty?, findings: findings} elsif Ractor.shareable?(app) {shareable: true, findings: []} else walk(app, "app", findings, seen, budget) findings << Finding.new(path: "app", message: unshareable_note(app)) if findings.empty? {shareable: false, findings: findings} end end |