有时您需要使用未维护的,旧的,脏的,庞大的种类的库,这些库可能会对我们的程序造成危险。
是否有以安全方式执行此代码的最佳实践?
最近,我发现(可能是在我的知识和经验水平上)没有捕捉到异常。直到今天,我一直使用的通用做法是将代码包装到Fiber中,捕获内部异常并通过Channel发送出去。目前,这是行不通的(我无法在光纤中放入Yield或Proc)。
危险的lib可能看起来像是带有方法的普通类,该方法将Fiber.yield封装在Fiber中,以便立即交换执行到其他光纤。在现实生活中,这种光纤可能包含与IO一起使用的内部功能,没关系。
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
轰! (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 ???
可能由于交换可执行上下文而发生:我的代码和异常在不同的上下文中并且无法交互?如果卸下光纤,则照常捕获异常。
是否有可能在不修改原始库的情况下解决此问题?
有时您需要使用未维护,旧,脏,庞大且种类繁多的库,这些库可能会对我们的程序造成危险。是否存在以安全方式执行此代码的最佳实践? ...
否,如果不修补到原始错误代码中,您将无法处理。但是,Crystal的开放类系统使这一切在您看来始终是可能的,直到确定上游行为为止,您只需在代码中重新定义该方法即可。
[请注意,这只是处理操作失败这一事实的问题。如果您可以通过其他方式获得该信息,例如通过使用select
超时等待结果,或者您根本不在乎操作是否成功,则唯一真正的问题是一些垃圾邮件。光纤不是主要光纤,崩溃不会破坏您的程序! (请参见https://play.crystal-lang.org/#/r/98da)