如何处理光纤外的光纤异常?
How to handle fiber exception outside of fiber?
有时您需要使用无人维护的、陈旧的、肮脏的、庞大的库,这些库对我们的程序来说可能是危险的。
是否有安全执行此代码的最佳实践?
最近我发现(可能以我的知识和经验水平)非捕获异常。直到今天,我常用的做法是将代码包装到 Fiber 中,在内部捕获异常并通过 Channel 发送出去。现在它不起作用(我不能将 Yield 或 Proc 放入 Fiber 中)。
危险的库看起来像普通的 class,其方法是使用 Fiber.yield 封装 Fiber 以立即交换执行到其他 fiber。现实生活中这个Fiber可能包含内部work with IO,没关系。
class LibDangerous
def exec_remote
spawn do
raise IO::Error.new
end
Fiber.yield
end
end
应该处理异常的包装器由 begin ... rescue
上的嵌套方法组成。我从顶层和最后一个包装器方法调用方法,我 return 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
轰! (this code on 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 ???
可能是交换可执行上下文导致的:我的代码和异常在不同的上下文中,无法交互?如果您删除 Fiber,那么异常会像往常一样被捕获。
是否可以在不修改原库的情况下解决这个问题?
不,如果不修补原始错误代码,您将无法处理此问题。但是 Crystal 的开放 class 系统使这完全可以从您这边实现,直到上游行为得到修复,您可以在代码中重新定义该方法。
请注意,这只是处理操作失败的问题。如果您可以通过其他方式获得该信息,例如通过使用 select
超时等待结果,或者您根本不关心操作是否成功,唯一真正的问题是有点记录垃圾邮件。一根纤程,那不是主纤程,崩溃不会影响你的程序! (参见 https://play.crystal-lang.org/#/r/98da)
这是为什么?引发异常意味着向上遍历当前堆栈,直到找到处理程序。当您看到 "Unhanded exception" 时,这只是默认处理程序 Crystal 放在每个堆栈的根部。现在什么是纤维?这是一个单独的堆栈!因此,在纤维内部提升不会展开任何其他堆叠,尤其是您的主纤维。
有时您需要使用无人维护的、陈旧的、肮脏的、庞大的库,这些库对我们的程序来说可能是危险的。
是否有安全执行此代码的最佳实践?
最近我发现(可能以我的知识和经验水平)非捕获异常。直到今天,我常用的做法是将代码包装到 Fiber 中,在内部捕获异常并通过 Channel 发送出去。现在它不起作用(我不能将 Yield 或 Proc 放入 Fiber 中)。
危险的库看起来像普通的 class,其方法是使用 Fiber.yield 封装 Fiber 以立即交换执行到其他 fiber。现实生活中这个Fiber可能包含内部work with IO,没关系。
class LibDangerous
def exec_remote
spawn do
raise IO::Error.new
end
Fiber.yield
end
end
应该处理异常的包装器由 begin ... rescue
上的嵌套方法组成。我从顶层和最后一个包装器方法调用方法,我 return 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
轰! (this code on 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 ???
可能是交换可执行上下文导致的:我的代码和异常在不同的上下文中,无法交互?如果您删除 Fiber,那么异常会像往常一样被捕获。
是否可以在不修改原库的情况下解决这个问题?
不,如果不修补原始错误代码,您将无法处理此问题。但是 Crystal 的开放 class 系统使这完全可以从您这边实现,直到上游行为得到修复,您可以在代码中重新定义该方法。
请注意,这只是处理操作失败的问题。如果您可以通过其他方式获得该信息,例如通过使用 select
超时等待结果,或者您根本不关心操作是否成功,唯一真正的问题是有点记录垃圾邮件。一根纤程,那不是主纤程,崩溃不会影响你的程序! (参见 https://play.crystal-lang.org/#/r/98da)
这是为什么?引发异常意味着向上遍历当前堆栈,直到找到处理程序。当您看到 "Unhanded exception" 时,这只是默认处理程序 Crystal 放在每个堆栈的根部。现在什么是纤维?这是一个单独的堆栈!因此,在纤维内部提升不会展开任何其他堆叠,尤其是您的主纤维。