@@ -13,21 +13,29 @@ module OverflowMessageType
1313 SPECIAL_SYMBOL_MESSAGE = lambda { |e | '<?>' }
1414 end
1515
16- class ExecError < StandardError
16+ class ExecError
1717 attr_reader :message
18- attr_reader :trace_point
1918 attr_reader :backtrace
2019
21- def initialize ( message , trace_point , backtrace = [ ] )
20+ def initialize ( message , backtrace = [ ] )
2221 @message = message
23- @trace_point = trace_point
2422 @backtrace = backtrace
2523 end
2624 end
2725
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
2936
30- class TimeLimitError < ExecError ; end
37+ class TimeLimitError < ExecError ;
38+ end
3139
3240 class XmlPrinter # :nodoc:
3341 class ExceptionProxy
@@ -163,48 +171,76 @@ def print_string(string)
163171 end
164172 end
165173
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+
166191 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+
168195
169- check_memory_limit = !defined? ( JRUBY_VERSION ) && ENV [ 'DEBUGGER_MEMORY_LIMIT' ] . to_i > 0
170196 curr_thread = Thread . current
197+ control_thread = Debugger . control_thread
171198
172199 result = nil
200+
201+ trace_queue = Queue . new
202+
173203 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
175205 start_time = Time . now . to_f
176206
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 |
181208 curr_time = Time . now . to_f
182209
183210 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
185214 end
186215
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
190217
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
194225 end
195226 end
196227 trace_point . enable
197228 result = value . send exec_method
229+ trace_queue << result
198230 trace_point . disable
199231 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 )
205243 return overflow_message_type . call ( e )
206- ensure
207- inspect_thread . kill if inspect_thread && inspect_thread . alive?
208244 end
209245
210246 def print_variable ( name , value , kind )
0 commit comments