使用Set8087CW,SetMXCSR和TWebBrowser屏蔽浮点异常 [英] Masking floating point exceptions with Set8087CW, SetMXCSR and TWebBrowser

查看:439
本文介绍了使用Set8087CW,SetMXCSR和TWebBrowser屏蔽浮点异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我在不时使用TWebBrowser和TEmbeddedWB时收到浮点除零异常,我发现我需要屏蔽除零之外的Set8087CW或SetMXCSR。



Q1:执行此操作最好的方法是:


  1. 在应用程序启动早期屏蔽这些异常触摸他们(应用程序是多线程的)?

  2. 使用 OnBeforeNavigate OnDocumentComplete 事件屏蔽/取消屏蔽异常? (在加载文档之后是否有可能发生异常的机会?)

Q2:最好的命令掩码只有除以零,没有别的 - 如果应用程序是32位是否需要掩盖64位异常?



我正在使用它的应用程序有TWebBrowser控件可用于显示电子邮件内容。



另外,如果有人可以澄清 - 这是一个特定的错误,来自微软的TWebBrowser控件或只是Delphi / C ++ Builder和Microsoft工具?如果在Visual C ++应用程序中托管TWebBrowser,如果出现零错误将会出现 - 它不会被转换为异常但会发生什么会发生什么 - Visual C ++如何处理除以零异常?



这很奇怪,微软没有注意到这个问题这么长时间 - 也很奇怪,Embarcardero从来没有注意到它。因为屏蔽浮点异常有效地掩盖了您自己的程序异常。



更新



我的最终解决方案是:

  SetExceptionMask(GetExceptionMask()<< exZeroDivide); 

来自 GetExceptionMask()的默认状态返回: TFPUExceptionMask()<< exDenormalized< exUnderflow<< exPrecision 。显然,一些异常已被掩盖 - 这只是将$ code> exZeroDivide 添加到蒙版的异常中。



除以零,现在以浮点而不是例外的 + INF 结果。我可以这样生活 - 对于生产版本的代码,我将屏蔽它,以避免错误,调试版本将被解除侦测浮点除以零。



最好的方式是这样做:

  SetExceptionMask (exAllArithmeticExceptions); 

这将在32位目标上设置8087控制字,在64位目标上设置MXCSR。您将在 Math 单位中找到 SetExceptionMask



如果您希望在代码中隐藏浮点异常,那么它会变得棘手。一个策略是在专门的线程中运行浮点代码来解除异常。这肯定可以工作,但是如果您依赖于RTL函数 Set8087CW SetMXCSR 。请注意,控制FP单元的RTL中的所有内容都会通过这些功能进行路由。例如 SetExceptionMask



问题是 Set8087CW SetMXCSR 不是线程安全的。似乎很难相信,Embarcadero可能无法生成基于线程上下文操作的基本例程,但不能保证线程安全。但这正是他们所做的。



令人惊讶的是很难解决他们已经离开的混乱,并且这样做涉及相当多的代码修补。缺少线程安全性是由于(误)使用全局变量 Default8087CW DefaultMXCSR 。如果两个线程同时调用 Set8087CW SetMXCSR ,那么这些全局变量可能会影响到一个线程到另一个。



您可以将 Set8087CW SetMXCSR 替换为没有改变全球的状态,可惜不是那么简单。全球状态在其他地方使用。这可能看起来不成熟,但如果您想了解更多有关此事的信息,请阅读本QC档案附件中的文件: http://qc.embarcadero.com/wc/qcmain.aspx?d=107411


As I am receiving "Floating point division by zero" exception when using TWebBrowser and TEmbeddedWB from time to time, I discovered that I need to mask division by zero exceptions Set8087CW or SetMXCSR.

Q1: What would be the best approach to do this:

  1. to mask such exceptions early in the application startup and never touch them again (the app is multithreaded)?
  2. to use OnBeforeNavigate and OnDocumentComplete events to mask / unmask exceptions? (is there a chance that exception could occur after the document is loaded?)

Q2: What would be the best "command" to mask only "division by zero" and nothing else - if application is 32-bit is there a need to mask 64 bit exception too?

The application I am using it it has TWebBrowser control available all the time for displaying email contents.

Also, if anyone can clarify - is this a particular bug with TWebBrowser control from Microsoft or just difference between Delphi/C++ Builder and Microsoft tools? What would happen if I would host TWebBrowser inside Visual C++ application if division by zero error would appear - it wouldn't be translated into exception but what would happen then - how would Visual C++ handle "division by zero" exception then?

It is kind of strange that Microsoft didn't notice this problem for such a long time - also it is strange that Embarcardero never noticed it too. Because masking floating point exception effectively also masks your own program exception for that particular purpose.

UPDATE

My final solution after some examination is:

SetExceptionMask(GetExceptionMask() << exZeroDivide);

The default state from GetExceptionMask() returns: TFPUExceptionMask() << exDenormalized << exUnderflow << exPrecision. So obviously, some exceptions are already masked - this just adds exZeroDivide to the masked exceptions.

As a result every division by zero now results with +INF in floating point instead of exception. I can live with that - for the production version of the code it will me masked to avoid errors and for the debug version it will be unmasked to detect floating point division by zero.

解决方案

Assuming that you have no need for floating point exceptions to be unmasked in your application code, far and away the simplest thing to do is to mask exceptions at some point in your initialization code.

The best way to do this is as so:

SetExceptionMask(exAllArithmeticExceptions);

This will set the 8087 control word on 32 bit targets, and the MXCSR on 64 bit targets. You will find SetExceptionMask in the Math unit.

If you wish to have floating point exceptions unmasked in your code then it gets tricky. One strategy would be to run your floating point code in a dedicated thread that unmasks the exceptions. This certainly can work, but not if you rely on the RTL functions Set8087CW and SetMXCSR. Note that everything in the RTL that controls FP units routes through these functions. For example SetExceptionMask does.

The problem is that Set8087CW and SetMXCSR are not threadsafe. It seems hard to believe that Embarcadero could be so inept as to produce fundamental routines that operate on thread context and yet fail to be threadsafe. But that is what they have done.

It's surprisingly hard to undo the mess that they have left, and to do so involves quite a bit of code patching. The lack of thread safety is down to the (mis)use of the global variables Default8087CW and DefaultMXCSR. If two threads call Set8087CW or SetMXCSR at the same time then these global variables can have the effect of leaking the value from one thread to the other.

You could replace Set8087CW and SetMXCSR with versions that did not change global state, but it's sadly not that simple. The global state is used in various other places. This may seem immodest, but if you want to learn more about this matter, read my document attached to this QC report: http://qc.embarcadero.com/wc/qcmain.aspx?d=107411

这篇关于使用Set8087CW,SetMXCSR和TWebBrowser屏蔽浮点异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆