Class: ThreadWeaver::ControllableThread

Inherits:
Thread
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/thread_weaver/controllable_thread.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context, name:, &blk) ⇒ ControllableThread

Returns a new instance of ControllableThread.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 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

Instance Attribute Details

#last_trace_point_summaryObject (readonly)

Returns the value of attribute last_trace_point_summary.



10
11
12
# File 'lib/thread_weaver/controllable_thread.rb', line 10

def last_trace_point_summary
  @last_trace_point_summary
end

Instance Method Details

#handle_trace_point(tp) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# 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

#joinObject



130
131
132
133
134
135
136
# File 'lib/thread_weaver/controllable_thread.rb', line 130

def join
  while alive?
    release
    do_nothing
  end
  super()
end

#nextObject



58
59
60
61
62
63
64
65
66
67
68
69
# 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

#releaseObject



51
52
53
54
55
# 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) ⇒ Object



79
80
81
82
# 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) ⇒ Object



72
73
74
75
76
# 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_completeObject



44
45
46
47
48
# 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