@@ -13,21 +13,29 @@ module OverflowMessageType
13
13
SPECIAL_SYMBOL_MESSAGE = lambda { |e | '<?>' }
14
14
end
15
15
16
- class ExecError < StandardError
16
+ class ExecError
17
17
attr_reader :message
18
- attr_reader :trace_point
19
18
attr_reader :backtrace
20
19
21
- def initialize ( message , trace_point , backtrace = [ ] )
20
+ def initialize ( message , backtrace = [ ] )
22
21
@message = message
23
- @trace_point = trace_point
24
22
@backtrace = backtrace
25
23
end
26
24
end
27
25
28
- class MemoryLimitError < ExecError ; end
26
+ class SimpleTimeLimitError < StandardError
27
+ attr_reader :message
28
+
29
+ def initialize ( message )
30
+ @message = message
31
+ end
32
+ end
33
+
34
+ class MemoryLimitError < ExecError ;
35
+ end
29
36
30
- class TimeLimitError < ExecError ; end
37
+ class TimeLimitError < ExecError ;
38
+ end
31
39
32
40
class XmlPrinter # :nodoc:
33
41
class ExceptionProxy
@@ -163,48 +171,76 @@ def print_string(string)
163
171
end
164
172
end
165
173
174
+ def exec_with_timeout ( sec , error_message )
175
+ return yield if sec == nil or sec . zero?
176
+ if Thread . respond_to? ( :critical ) and Thread . critical
177
+ raise ThreadError , "timeout within critical session"
178
+ end
179
+ begin
180
+ x = Thread . current
181
+ y = DebugThread . start {
182
+ sleep sec
183
+ x . raise SimpleTimeLimitError . new ( error_message ) if x . alive?
184
+ }
185
+ yield sec
186
+ ensure
187
+ y . kill if y and y . alive?
188
+ end
189
+ end
190
+
166
191
def exec_with_allocation_control ( value , memory_limit , time_limit , exec_method , overflow_message_type )
167
- return value . send exec_method if RUBY_VERSION < '2.0'
192
+ return value . send exec_method if !Debugger . trace_to_s
193
+ return exec_with_timeout ( time_limit * 1e-3 , "Timeout: evaluation of #{ exec_method } took longer than #{ time_limit } ms." ) { value . send exec_method } if defined? ( JRUBY_VERSION ) || memory_limit <= 0 || ( RUBY_VERSION < '2.0' && time_limit > 0 )
194
+
168
195
169
- check_memory_limit = !defined? ( JRUBY_VERSION ) && ENV [ 'DEBUGGER_MEMORY_LIMIT' ] . to_i > 0
170
196
curr_thread = Thread . current
197
+ control_thread = Debugger . control_thread
171
198
172
199
result = nil
200
+
201
+ trace_queue = Queue . new
202
+
173
203
inspect_thread = DebugThread . start do
174
- start_alloc_size = ObjectSpace . memsize_of_all if check_memory_limit
204
+ start_alloc_size = ObjectSpace . memsize_of_all
175
205
start_time = Time . now . to_f
176
206
177
- trace_point = TracePoint . new ( :c_call , :call ) do | |
178
- next unless Thread . current == inspect_thread
179
- next unless rand > 0.75
180
-
207
+ trace_point = TracePoint . new ( :c_call , :call ) do |tp |
181
208
curr_time = Time . now . to_f
182
209
183
210
if ( curr_time - start_time ) * 1e3 > time_limit
184
- curr_thread . raise TimeLimitError . new ( "Timeout: evaluation of #{ exec_method } took longer than #{ time_limit } ms." , trace_point , caller . to_a )
211
+ trace_queue << TimeLimitError . new ( "Timeout: evaluation of #{ exec_method } took longer than #{ time_limit } ms." , caller . to_a )
212
+ trace_point . disable
213
+ inspect_thread . kill
185
214
end
186
215
187
- if check_memory_limit
188
- curr_alloc_size = ObjectSpace . memsize_of_all
189
- start_alloc_size = curr_alloc_size if curr_alloc_size < start_alloc_size
216
+ next unless rand > 0.75
190
217
191
- if curr_alloc_size - start_alloc_size > 1e6 * memory_limit
192
- curr_thread . raise MemoryLimitError . new ( "Out of memory: evaluation of #{ exec_method } took more than #{ memory_limit } mb." , trace_point , caller . to_a )
193
- end
218
+ curr_alloc_size = ObjectSpace . memsize_of_all
219
+ start_alloc_size = curr_alloc_size if curr_alloc_size < start_alloc_size
220
+
221
+ if curr_alloc_size - start_alloc_size > 1e6 * memory_limit
222
+ trace_queue << MemoryLimitError . new ( "Out of memory: evaluation of #{ exec_method } took more than #{ memory_limit } mb." , caller . to_a )
223
+ trace_point . disable
224
+ inspect_thread . kill
194
225
end
195
226
end
196
227
trace_point . enable
197
228
result = value . send exec_method
229
+ trace_queue << result
198
230
trace_point . disable
199
231
end
200
- inspect_thread . join
201
- return result
202
- rescue ExecError => e
203
- e . trace_point . disable
204
- print_debug ( e . message + "\n " + e . backtrace . map { |l | "\t #{ l } " } . join ( "\n " ) )
232
+
233
+ while ( mes = trace_queue . pop )
234
+ if ( mes . is_a? TimeLimitError or mes . is_a? MemoryLimitError )
235
+ print_debug ( mes . message + "\n " + mes . backtrace . map { |l | "\t #{ l } " } . join ( "\n " ) )
236
+ return overflow_message_type . call ( mes )
237
+ else
238
+ return mes
239
+ end
240
+ end
241
+ rescue SimpleTimeLimitError => e
242
+ print_debug ( e . message )
205
243
return overflow_message_type . call ( e )
206
- ensure
207
- inspect_thread . kill if inspect_thread && inspect_thread . alive?
208
244
end
209
245
210
246
def print_variable ( name , value , kind )
0 commit comments