/
thread.rb
174 lines (142 loc) · 4.14 KB
/
thread.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
73
74
75
76
77
78
79
80
81
82
83
84
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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
#--
# Be very careful about calling raise in here! Thread has its own
# raise which, if you're calling raise, you probably don't want. Use
# Kernel.raise to call the proper raise.
#++
class Thread
MUTEX_FOR_THREAD_EXCLUSIVE = Mutex.new
def self.exclusive
MUTEX_FOR_THREAD_EXCLUSIVE.synchronize { yield }
end
attr_reader :recursive_objects
# Implementation note: ideally, the recursive_objects
# lookup table would be different per method call.
# Currently it doesn't cause problems, but if ever
# a method :foo calls a method :bar which could
# recurse back to :foo, it could require making
# the tables independant.
def self.recursion_guard(obj)
id = obj.object_id
objects = current.recursive_objects
objects[id] = true
begin
yield
ensure
objects.delete id
end
end
def self.guarding?(obj)
current.recursive_objects[obj.object_id]
end
# detect_recursion will return if there's a recursion
# on obj (or the pair obj+paired_obj).
# If there is one, it returns true.
# Otherwise, it will yield once and return false.
def self.detect_recursion(obj, paired_obj=undefined)
id = obj.object_id
pair_id = paired_obj.object_id
objects = current.recursive_objects
case objects[id]
# Default case, we haven't seen +obj+ yet, so we add it and run the block.
when nil
objects[id] = pair_id
begin
yield
ensure
objects.delete id
end
# We've seen +obj+ before and it's got multiple paired objects associated
# with it, so check the pair and yield if there is no recursion.
when Rubinius::LookupTable
return true if objects[id][pair_id]
objects[id][pair_id] = true
begin
yield
ensure
objects[id].delete pair_id
end
# We've seen +obj+ with one paired object, so check the stored one for
# recursion.
#
# This promotes the value to a LookupTable since there is another new paired
# object.
else
previous = objects[id]
return true if previous == pair_id
objects[id] = Rubinius::LookupTable.new(previous => true, pair_id => true)
begin
yield
ensure
objects[id] = previous
end
end
false
end
# Similar to detect_recursion, but will short circuit all inner recursion
# levels (using a throw)
class InnerRecursionDetected < Exception; end
def self.detect_outermost_recursion(obj, paired_obj=undefined, &block)
rec = current.recursive_objects
if rec[:__detect_outermost_recursion__]
if detect_recursion(obj, paired_obj, &block)
raise InnerRecursionDetected
end
false
else
begin
rec[:__detect_outermost_recursion__] = true
begin
detect_recursion(obj, paired_obj, &block)
rescue InnerRecursionDetected
return true
end
return nil
ensure
rec.delete :__detect_outermost_recursion__
end
end
end
def randomizer
@randomizer ||= Rubinius::Randomizer.new
end
def backtrace
mri_backtrace.map do |tup|
code = tup[0]
line = tup[1]
is_block = tup[2]
name = tup[3]
"#{code.active_path}:#{line}:in `#{name}'"
end
end
# This is a class in MRI, although you can't initialize it.
class Backtrace
# Stores location of a single call frame, available since Ruby 2.0.
class Location
attr_reader :label
attr_reader :absolute_path
attr_reader :lineno
def initialize(label, absolute_path, lineno)
@label = label
@absolute_path = absolute_path
@lineno = lineno
end
alias_method :base_label, :label
##
# The MRI documentation states this method should return the file name. In
# reality however this method just returns the full path.
#
# Rubinius here sticks to whatever the documentation specifies, thus we
# return only the filename.
#
def path
File.basename(absolute_path)
end
def to_s
"#{absolute_path}:#{lineno}:in `#{label}'"
end
def inspect
to_s
end
end
end
end