class ThreadWeaver::ControllableThread
Attributes
last_trace_point_summary[R]
Public Class Methods
new(context, name:, &blk)
click to toggle source
Calls superclass method
# File lib/thread_weaver/controllable_thread.rb, line 15 def initialize(context, name:, &blk) @waiting = T.let(false, T::Boolean) @execution_counter = T.let(-1, Integer) @last_trace_point_summary = T.let("<no traces detected>", String) @line_counts_by_class = T.let({}, T::Hash[Module, Integer]) @current_instruction = T.let(PauseAtThreadStart.new, ThreadInstruction) self.name = name self.report_on_exception = false super do tracer = TracePoint.new(:line, :call, :return, :b_call, :b_return, :thread_begin, :thread_end, :c_call, :c_return) { |tp| current_thread = Thread.current if current_thread == self current_thread.handle_trace_point(tp) end } handle_thread_start tracer.enable blk.call(context) handle_thread_end ensure tracer&.disable end wait_until_next_instruction_complete end
Public Instance Methods
handle_trace_point(tp)
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 85 def handle_trace_point(tp) event = T.let(tp.event, Symbol) klass = T.let(tp.defined_class, T.nilable(Module)) path = T.let(tp.path, T.nilable(String)) line = T.let(tp.lineno, T.nilable(Integer)) method_name = T.let(tp.method_id, T.nilable(Symbol)) @last_trace_point_summary = "#{event} #{klass}##{method_name} #{path}#L#{line}" if klass current_count = @line_counts_by_class.fetch(klass, 0) @line_counts_by_class[klass] = (current_count + 1) end case @current_instruction when PauseAtThreadStart if event == :thread_begin wait_until_released end when ContinueToThreadEnd # do nothing when PauseWhenLineCount current_count = @current_instruction.target_classes.map { |klass| @line_counts_by_class.fetch(klass, 0) }.sum required_count = @current_instruction.count if required_count == current_count wait_until_released end when PauseAtMethodCall if @current_instruction.klass == klass && @current_instruction.method_name == method_name wait_until_released end when PauseAtMethodReturn if @current_instruction.klass == klass && @current_instruction.method_name == method_name wait_until_released end when PauseAtSourceLine if path&.end_with?(@current_instruction.path_suffix) && @current_instruction.line == line wait_until_released end else T.absurd(@current_instruction) end end
join()
click to toggle source
Calls superclass method
# File lib/thread_weaver/controllable_thread.rb, line 130 def join while alive? release do_nothing end super() end
next()
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 58 def next assert_self_is_not_current_thread case @current_instruction when PauseWhenLineCount, PauseAtSourceLine set_next_instruction( @current_instruction.next ) else raise "Next is only supported when paused on a #{PauseWhenLineCount.name} or a #{PauseAtSourceLine} instruction " end end
release()
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 51 def release assert_self_is_not_current_thread @waiting = false end
set_and_wait_for_next_instruction(instruction)
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 79 def set_and_wait_for_next_instruction(instruction) set_next_instruction(instruction) wait_until_next_instruction_complete end
set_next_instruction(instruction)
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 72 def set_next_instruction(instruction) assert_self_is_not_current_thread @current_instruction = instruction release end
wait_until_next_instruction_complete()
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 44 def wait_until_next_instruction_complete assert_self_is_not_current_thread do_nothing while alive? && !@waiting end
Private Instance Methods
assert_self_is_current_thread()
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 168 def assert_self_is_current_thread raise "illegal call from thread other than self" unless Thread.current == self end
assert_self_is_not_current_thread()
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 173 def assert_self_is_not_current_thread raise "illegal call from self" unless Thread.current != self end
do_nothing()
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 163 def do_nothing Thread.pass end
handle_thread_end()
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 149 def handle_thread_end assert_self_is_current_thread unless @current_instruction.is_a?(ContinueToThreadEnd) raise ThreadCompletedEarlyError.new("Thread #{name} completed while attempting to match instruction #{@current_instruction}") end end
handle_thread_start()
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 141 def handle_thread_start assert_self_is_current_thread if @current_instruction.is_a?(PauseAtThreadStart) wait_until_released end end
wait_until_released()
click to toggle source
# File lib/thread_weaver/controllable_thread.rb, line 157 def wait_until_released @waiting = true do_nothing while @waiting end