AtomicCmpExchange() 坏了吗?
Is AtomicCmpExchange() broken?
如果新建多机应用项目,设置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 <> LCompareKey then raise Exception.Create('Error 2');
上的 Win32 上崩溃?
我正在使用 Delphi 10.4 悉尼更新 3。我还没有尝试 Delphi 11 Alexandria,所以我不知道它是否适用于那个版本。
除了停用优化之外,是否有任何解决方法?
另一个问题-激活优化真的安全吗?
是的,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代码不受此问题影响,可以在应用中开启优化.
注意:编译器中可能存在其他我们不知道的优化错误。
如果新建多机应用项目,设置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 <> LCompareKey then raise Exception.Create('Error 2');
上的 Win32 上崩溃?
我正在使用 Delphi 10.4 悉尼更新 3。我还没有尝试 Delphi 11 Alexandria,所以我不知道它是否适用于那个版本。
除了停用优化之外,是否有任何解决方法?
另一个问题-激活优化真的安全吗?
是的,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代码不受此问题影响,可以在应用中开启优化.
注意:编译器中可能存在其他我们不知道的优化错误。