Skip to content

Commit 90bbdb6

Browse files
committed
new implementation of execution with allocation control
1 parent 1e9da6d commit 90bbdb6

File tree

1 file changed

+30
-23
lines changed

1 file changed

+30
-23
lines changed

lib/ruby-debug-ide/xml_printer.rb

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,15 @@ module OverflowMessageType
1515

1616
class ExecError < StandardError
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 JrubyTimeLimitError < StandardError
26+
class SimpleTimeLimitError < StandardError
2927
attr_reader :message
3028

3129
def initialize(message)
@@ -173,7 +171,7 @@ def print_string(string)
173171
end
174172
end
175173

176-
def jruby_timeout(sec)
174+
def exec_with_timeout(sec)
177175
return yield if sec == nil or sec.zero?
178176
if Thread.respond_to?(:critical) and Thread.critical
179177
raise ThreadError, "timeout within critical session"
@@ -182,7 +180,7 @@ def jruby_timeout(sec)
182180
x = Thread.current
183181
y = DebugThread.start {
184182
sleep sec
185-
x.raise JrubyTimeLimitError.new("Timeout: evaluation took longer than #{sec} seconds.") if x.alive?
183+
x.raise SimpleTimeLimitError.new("Timeout: evaluation took longer than #{sec} seconds.") if x.alive?
186184
}
187185
yield sec
188186
ensure
@@ -191,48 +189,57 @@ def jruby_timeout(sec)
191189
end
192190

193191
def exec_with_allocation_control(value, memory_limit, time_limit, exec_method, overflow_message_type)
194-
return jruby_timeout(time_limit / 1e3) {value.send exec_method} if defined?(JRUBY_VERSION)
192+
return exec_with_timeout(time_limit * 1e-3) {value.send exec_method} if defined?(JRUBY_VERSION) || memory_limit <= 0 || (RUBY_VERSION < '2.0' && time_limit > 0)
195193
return value.send exec_method if RUBY_VERSION < '2.0'
196194

197195
curr_thread = Thread.current
196+
control_thread = Debugger.control_thread
198197

199198
result = nil
199+
200+
trace_queue = Queue.new
201+
200202
inspect_thread = DebugThread.start do
201-
start_alloc_size = ObjectSpace.memsize_of_all if check_memory_limit
203+
start_alloc_size = ObjectSpace.memsize_of_all
202204
start_time = Time.now.to_f
203205

204-
trace_point = TracePoint.new(:c_call, :call) do | |
205-
next unless Thread.current == inspect_thread
206-
next unless rand > 0.75
207-
206+
trace_point = TracePoint.new(:c_call, :call) do |tp|
208207
curr_time = Time.now.to_f
209208

210209
if (curr_time - start_time) * 1e3 > time_limit
211-
curr_thread.raise TimeLimitError.new("Timeout: evaluation of #{exec_method} took longer than #{time_limit}ms.", trace_point, caller.to_a)
210+
trace_queue << TimeLimitError.new("Timeout: evaluation of #{exec_method} took longer than #{time_limit}ms.", caller.to_a)
211+
trace_point.disable
212+
inspect_thread.kill
212213
end
213214

215+
next unless rand > 0.75
216+
214217
curr_alloc_size = ObjectSpace.memsize_of_all
215218
start_alloc_size = curr_alloc_size if curr_alloc_size < start_alloc_size
216219

217220
if curr_alloc_size - start_alloc_size > 1e6 * memory_limit
218-
curr_thread.raise MemoryLimitError.new("Out of memory: evaluation of #{exec_method} took more than #{memory_limit}mb.", trace_point, caller.to_a)
221+
trace_queue << MemoryLimitError.new("Out of memory: evaluation of #{exec_method} took more than #{memory_limit}mb.", caller.to_a)
222+
trace_point.disable
223+
inspect_thread.kill
219224
end
220225
end
221226
trace_point.enable
222227
result = value.send exec_method
228+
trace_queue << result
223229
trace_point.disable
224230
end
225-
inspect_thread.join
226-
return result
227-
rescue ExecError => e
228-
e.trace_point.disable
229-
print_debug(e.message + "\n" + e.backtrace.map {|l| "\t#{l}"}.join("\n"))
230-
return overflow_message_type.call(e)
231-
rescue JrubyTimeLimitError => e
231+
232+
while(mes = trace_queue.pop)
233+
if(mes.is_a? TimeLimitError or mes.is_a? MemoryLimitError)
234+
print_debug(mes.message + "\n" + mes.backtrace.map {|l| "\t#{l}"}.join("\n"))
235+
return overflow_message_type.call(mes)
236+
else
237+
return mes
238+
end
239+
end
240+
rescue SimpleTimeLimitError => e
232241
print_debug(e.message)
233242
return overflow_message_type.call(e)
234-
ensure
235-
inspect_thread.kill if inspect_thread && inspect_thread.alive?
236243
end
237244

238245
def print_variable(name, value, kind)

0 commit comments

Comments
 (0)