【问题标题】:Is AtomicCmpExchange() broken?AtomicCmpExchange() 坏了吗?
【发布时间】:2021-12-31 13:29:32
【问题描述】:

如果新建多设备应用项目,设置Project > Option > Compiling > Optimization : True,然后将下面的代码复制到unit1.pas

unit Unit1;

interface

uses
  System.SysUtils,
  FMX.Forms,
  FMX.StdCtrls,
  System.Classes,
  FMX.Types,
  FMX.Controls,
  FMX.Controls.Presentation;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FKey: integer;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
begin

  FKey := 2;
  var LCompareKey: integer := 2;
  AtomicCmpExchange(FKey{target}, LCompareKey{NewValue}, LCompareKey{Comparand});
  if FKey <> LCompareKey then raise Exception.Create('Error 2');

  TThread.queue(nil,
    procedure
    begin
      if LCompareKey <> FKey
        then raise Exception.Create('Error 3');
    end);

end;

end.

为什么这段代码在 if FKey &lt;&gt; LCompareKey then raise Exception.Create('Error 2'); 上的 Win32 上会崩溃?

我正在使用 Delphi 10.4 Sydney Update 3。我还没有在 Delphi 11 Alexandria 中尝试过,所以我不知道它是否在那个版本中工作。

除了停用优化之外,还有其他解决方法吗?

另一个问题——激活优化真的安全吗?

【问题讨论】:

  • 如果变量被捕获,似乎 codegen 被破坏了。试试TInterlocked.CompareExchange
  • 是的 TInterlocked.CompareExchange 工作得很好(奇怪,因为它只在内部使用 atomicComExchange 和内联),但是 AtomicCmpExchange 已经在任何地方使用,不仅在我的代码中,而且在层代码中:(你知道它是否是亚历山大也崩溃了?
  • @DavidHeffernan :( 没有人是完美的,我猜这个编译器也不要绝望 :( 我会在 QP 再次工作时添加一个条目,因为 QP 也不完美 :(
  • 在 Delphi 11 Alexandria 上也失败了

标签: delphi delphi-10.4-sydney


【解决方案1】:

是的,当优化开启时,AtomicCmpExchange 的代码生成在 Win32 编译器上被破坏。

问题与TThread.Queue 调用中发生的匿名方法变量捕获相结合。如果没有变量捕获,AtomicCmpExchange 的汇编代码会正确生成。

该问题的解决方法是使用TInterlocked.CompareExchange

...
var LCompareKey: integer := 2;
TInterlocked.CompareExchange(FKey{target}, LCompareKey{NewValue}, LCompareKey{Comparand});
if FKey <> LCompareKey then raise Exception.Create('Error 2');
...

TInterlocked.CompareExchange 函数仍然使用AtomicCmpExchange,但在调用位置它通过参数而不是直接使用捕获的变量,并且在这些情况下生成的代码是正确的。

class function TInterlocked.CompareExchange(var Target: Integer; Value, Comparand: Integer): Integer;
begin
  Result := AtomicCmpExchange(Target, Value, Comparand);
end;

另一个不太理想的解决方案是使用 {$O-} 编译器指令关闭对损坏方法 Button1Click 的优化,然后使用 {$O+} 重新打开它

由于AtomicCmpExchange 是 Delphi 内部函数,它的代码在调用时由编译器直接生成,错误的代码生成只影响该过程,而不影响一般代码 - 换句话说,匿名方法捕获在其他代码中正常工作(除非存在是编译器中的其他错误,与这个特定的错误无关)。

在RTL中其他使用AtomicCmpExchange的地方,没有涉及变量捕获的代码,所以RTL、VCL和FMX代码不受此问题影响,可以在应用中开启优化。

注意:编译器中可能还有其他我们不知道的优化错误。

【讨论】:

    猜你喜欢
    • 2010-11-21
    • 2015-03-04
    • 2016-09-08
    • 2011-07-14
    • 2020-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多