Module: ThreadingTestTools
- Defined in:
- lib/threading_test_tools.rb
Defined Under Namespace
Classes: RaceConditionFound
Instance Method Summary collapse
- #analyze_race_condition_test(data) ⇒ Object
- #check_race_condition(options = {}, &test) ⇒ Object
- #disable_synchronizations ⇒ Object
- #enable_synchronizations ⇒ Object
- #extract_variables(binding) ⇒ Object
- #filter_action(event, file, line, id, classname) ⇒ Object
- #perform_race_condition_test(data) ⇒ Object
- #restore_variables(binding, variables) ⇒ Object
Instance Method Details
#analyze_race_condition_test(data) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/threading_test_tools.rb', line 134 def analyze_race_condition_test(data) data[:initial_variables] = extract_variables(data[:test]) actions = [] begin set_trace_func lambda { |event, file, line, id, binding, classname| return if !filter_action(event, file, line, id, classname) actions << [event, file, line, id, classname] } data[:test].call ensure set_trace_func nil end data[:actions] = actions.uniq data[:expected_variables] = extract_variables(data[:test]) end |
#check_race_condition(options = {}, &test) ⇒ Object
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 128 129 130 131 132 |
# File 'lib/threading_test_tools.rb', line 90 def check_race_condition( = {}, &test) data = data[:test] = test analyze_race_condition_test(data) assert_raise RaceConditionFound, 'No race condition could be found (with disabled synchronizations).' do begin $stdout.print '? R3T: Step 1/2 - Try ' disable_synchronizations perform_race_condition_test(data) ensure $stdout.print "\b" * 22 end end assert_nothing_raised 'An unhandled race condition has been found (with enabled synchronizations).' do begin begin $stdout.print '? R3T: Step 2/2 - Try ' enable_synchronizations perform_race_condition_test(data) ensure $stdout.print "\b" * 22 end rescue RaceConditionFound => e if data[:create_ographs] require 'ograph' File.open('ograph_expected.dot', 'w') { |file| file.write ObjectGraph.graph(data[:expected_variables]) } system('dot -Tpng < ograph_expected.dot > ograph_expected.png') File.unlink('ograph_expected.dot') File.open('ograph_race_condition.dot', 'w') { |file| file.write ObjectGraph.graph(e.result_variables) } system('dot -Tpng < ograph_race_condition.dot > ograph_race_condition.png') File.unlink('ograph_race_condition.dot') end raise e end end ensure $stdout.print ' ' * 40, "\b" * 40 enable_synchronizations end |
#disable_synchronizations ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/threading_test_tools.rb', line 58 def disable_synchronizations Object.module_eval <<-CODE_END class Mutex def lock self end end module Mutex_m def mu_lock self end end module MonitorMixin def mon_enter self end end module Sync_m def sync_lock(m = EX) self end end CODE_END end |
#enable_synchronizations ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/threading_test_tools.rb', line 38 def enable_synchronizations Object.module_eval <<-CODE_END class Mutex alias_method :lock, :lock_orig end module Mutex_m alias_method :mu_lock, :mu_lock_orig end module MonitorMixin alias_method :mon_enter, :mon_enter_orig end module Sync_m alias_method :sync_lock, :sync_lock_orig end CODE_END end |
#extract_variables(binding) ⇒ Object
183 184 185 186 187 188 189 |
# File 'lib/threading_test_tools.rb', line 183 def extract_variables(binding) variables = {} eval('local_variables', binding).each do |name| variables[name] = Marshal.load(Marshal.dump(eval(name, binding))) end variables end |
#filter_action(event, file, line, id, classname) ⇒ Object
86 87 88 |
# File 'lib/threading_test_tools.rb', line 86 def filter_action(event, file, line, id, classname) ['call', 'c-call'].include?(event) && !['threading_test_tools.rb', 'thread.rb', 'thwait.rb', 'mutex_m.rb', 'monitor.rb', 'sync.rb'].include?(File.basename(file)) end |
#perform_race_condition_test(data) ⇒ Object
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/threading_test_tools.rb', line 150 def perform_race_condition_test(data) count = data[:actions].size digits = count.to_s.length 0.upto(count - 1) do |index| begin display = sprintf "%#{digits}d/%#{digits}d", index, count $stdout.print display $stdout.flush action = data[:actions][index] restore_variables(data[:test], data[:initial_variables]) begin set_trace_func lambda { |event, file, line, id, binding, classname| if action == [event, file, line, id, classname] Thread.critical = false Thread.pass Thread.critical = true end } data[:test].call result_variables = extract_variables(data[:test]) ensure set_trace_func nil end = [] if not data[:expected_variables].data_equal?(result_variables, , {}, true) raise RaceConditionFound.new("Race condition found when passing to next thread at #{action[1]}:#{action[2]}: #{.join}", result_variables) end ensure $stdout.print "\b" * display.size end end end |
#restore_variables(binding, variables) ⇒ Object
191 192 193 194 195 196 |
# File 'lib/threading_test_tools.rb', line 191 def restore_variables(binding, variables) variables.each do |name, value| $variable_value = Marshal.load(Marshal.dump(value)) eval("#{name} = $variable_value", binding) end end |