Иногда вам нужно работать с необслуживаемыми, старыми, грязными, огромными библиотеками, которые могут быть опасны для нашей программы.
Есть ли лучшие практики для безопасного выполнения этого кода?
Недавно я обнаружил (вероятно, на уровне моих знаний и опыта) неуловимое исключение. Обычная практика, которую я использовал до сегодняшнего дня, - это оборачивать код в Fiber, захватывать исключение внутри и отправлять через канал. Пока это не работает (я не могу поместить Yield или Proc в Fiber).
Опасная библиотека может выглядеть как общий класс с методом, который инкапсулирует Fiber с Fiber.yield для выполнения замены на другие волокна прямо сейчас. В реальной жизни это волокно может содержать внутреннюю работу с вводом-выводом, это не имеет значения.
class LibDangerous
def exec_remote
spawn do
raise IO::Error.new
end
Fiber.yield
end
end
Оболочка, которая должна обрабатывать исключение, состоит из вложенных методов более begin ... rescue
. Я вызываю методы с верхнего уровня, а из последнего метода оболочки возвращаю метод lib, который всегда взрывает программу даже с блоком begin ... rescue
.
class Wrapper
def capture
begin
yield self
rescue
puts "rescued from :capture"
end
end
def guard
begin
capture do |this|
yield this
end
rescue
puts "rescued from :guard"
end
end
def run
begin
yield LibDangerous.new
rescue ex
puts "rescued from :run"
end
end
end
Похоже, это связано с тем, что вам нужно обрабатывать исключение на том же уровне, на котором оно произошло, но я не могу изменить код чужой библиотеки по ряду причин.
wrapper = Wrapper.new
result = wrapper.guard do |sandbox|
begin
sandbox.run do |library|
library.exec_remote
end
rescue
puts "rescued from top-level"
end
end
Бум! (этот код на play.crystal-lang.org)
Unhandled exception in spawn: (IO::Error)
from /eval:4:7 in '->'
from /usr/lib/crystal/fiber.cr:255:3 in 'run'
from /usr/lib/crystal/fiber.cr:92:34 in '->'
from ???
Это может произойти из-за обмена исполняемыми контекстами: мой код и исключение находятся в разных контекстах и не могут взаимодействовать? Если вы удалите волокно, исключение будет обнаружено как обычно.
Можно ли решить эту проблему без изменения исходной библиотеки?