Class: Temporalio::Internal::Worker::WorkflowInstance::IllegalCallTracer
- Inherits:
-
Object
- Object
- Temporalio::Internal::Worker::WorkflowInstance::IllegalCallTracer
- Defined in:
- lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb
Overview
Class that installs TracePoint to disallow illegal calls.
Class Method Summary collapse
Instance Method Summary collapse
- #disable(&block) ⇒ Object
- #enable(&block) ⇒ Object
-
#initialize(illegal_calls) ⇒ IllegalCallTracer
constructor
Illegal calls are Hash[String, :all | Hash[Symbol, Bool]].
Constructor Details
#initialize(illegal_calls) ⇒ IllegalCallTracer
Illegal calls are Hash[String, :all | Hash[Symbol, Bool]]
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb', line 37 def initialize(illegal_calls) @tracepoint = TracePoint.new(:call, :c_call) do |tp| # Manual check for proper thread since we have seen issues in Ruby 3.2 where it leaks next unless Thread.current == @enabled_thread cls = tp.defined_class next unless cls.is_a?(Module) # Extract the class name from the defined class. This is more difficult than it seems because you have to # resolve the attached object of the singleton class. But in older Ruby (at least <= 3.1), the singleton # class of things like `Date` does not have `attached_object` so you have to fall back in these rare cases # to parsing the string output. Reaching the string parsing component is rare, so this should not have # significant performance impact. cls_name = if cls.singleton_class? if cls.respond_to?(:attached_object) cls = cls.attached_object # steep:ignore next unless cls.is_a?(Module) cls.name.to_s else cls.to_s.delete_prefix('#<Class:').delete_suffix('>') end else cls.name.to_s end # Check if the call is considered illegal vals = illegal_calls[cls_name] if vals == :all || vals&.[](tp.callee_id) # steep:ignore raise Workflow::NondeterminismError, "Cannot access #{cls_name} #{tp.callee_id} from inside a " \ 'workflow. If this is known to be safe, the code can be run in ' \ 'a Temporalio::Workflow::Unsafe.illegal_call_tracing_disabled block.' end end end |
Class Method Details
.frozen_validated_illegal_calls(illegal_calls) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb', line 11 def self.frozen_validated_illegal_calls(illegal_calls) illegal_calls.to_h do |key, val| raise TypeError, 'Invalid illegal call map, top-level key must be a String' unless key.is_a?(String) # @type var fixed_val: :all | Hash[Symbol, bool] fixed_val = case val when Array val.to_h do |sub_val| unless sub_val.is_a?(Symbol) raise TypeError, 'Invalid illegal call map, each value must be a Symbol' end [sub_val, true] end.freeze when :all :all else raise TypeError, 'Invalid illegal call map, top-level value must be an Array or :all' end [key.frozen? ? key : key.dup.freeze, fixed_val] end.freeze end |
Instance Method Details
#disable(&block) ⇒ Object
85 86 87 88 89 90 91 92 |
# File 'lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb', line 85 def disable(&block) previous_thread = @enabled_thread @tracepoint.disable do block.call ensure @enabled_thread = previous_thread end end |
#enable(&block) ⇒ Object
74 75 76 77 78 79 80 81 82 83 |
# File 'lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb', line 74 def enable(&block) # We've seen leaking issues in Ruby 3.2 where the TracePoint inadvertently remains enabled even for threads # that it was not started on. So we will check the thread ourselves. @enabled_thread = Thread.current @tracepoint.enable do block.call ensure @enabled_thread = nil end end |