Эквивалент InterlockedExchangeAdd для Linux с использованием Delphi 10.2)

Delphi 10.2 (с поддержкой Linux) имеет кросс-платформенную функцию AtomicExchange, которая эквивалентна Windows InterlocekdEchange. Пока что так хорошо...

Мне нужно перенести код Win32, используя InterlockedExchangeAdd, который не имеет эквивалента AtomicExchangeAdd.

Мой вопрос: что я могу использовать для замены InterlockedExchangeAdd при компиляции для Linux?

Ответ 1

InterlockedExchangeAdd() "выполняет атомное добавление значения к значению, указанному Addend. Результат сохраняется в адресе, указанном Addend."

У блока System.SyncObjs есть класс TInterlocked, который перегружен Add() сделать то же самое:

Приращение целочисленного значения с другим.

Есть два перегруженных метода Add. Оба метода Add увеличивают a Target на Increment.

class function Add(var Target: Integer; Increment: Integer): Integer; overload; static; inline;

class function Add(var Target: Int64; Increment: Int64): Int64; overload; static; inline;

Разница заключается в том, что InterlockedExchangeAdd() "возвращает начальное значение переменной, на которую указывает Addend", тогда как TInterlocked.Add() "возвращает значение параметра с приращением вместо этого. Итак, если вы используете возвращаемое значение, вам придется учитывать эту разницу, например:

function InterlockedExchangeAdd(var Addend: Integer; Value: Integer): Integer;
begin
  Result := TInterlocked.Add(Addend, Value) - Value;
end;

Ответ 2

Существует скрытая реализация этой функции в System.SysUtils.pas:

function AtomicExchangeAdd(var Addend: Integer; Value: Integer): Integer;
begin
  Result := AtomicIncrement(Addend, Value) - Value;
end;

Используется тот факт, что AtomicIncrement возвращает новое значение Addend, а InterlockedExchangeAdd возвращает старое значение. Вычитание значения дает ожидаемый результат и, очевидно, является потокобезопасным.