PDA

Ver la Versión Completa : InterlockedExchange en 64 bits


xangiesaurx
18-02-2016, 03:01:45
Hola a todos, ya vengo a molestarlos de nuevo, ahora tengo una gran duda, estoy migrando un proyecto de Delphi 2010 a Delphi XE5 (Win64), en el que tengo esta línea de código Thread := InterlockedExchange(Integer(FThread), 0);
Pero al parecer el InterlockedExchange no se puede importar a Delphi XE5, alguien sabe de que manera puedo usarlo para que funcione en 64bits?
Gracias :)

AgustinOrtu
18-02-2016, 03:44:39
Conozco dos "InterlockedExchange"

Una de ellas esta definida en la unidad Windows.Winapi, y es una funcion que depende de una biblioteca externa; si vemos la declaracion:


function InterlockedExchange; external kernel32 name 'InterlockedExchange';

Por otro lado, la RTL de Delphi incluye una unidad, la System.SyncObjs en donde se define una clase
estatica que implementa la misma funcionalidad pero usando codigo Delphi; es decir, sin dependencias. De hecho, alguna de las funciones son implementadas "dentro del compilador", en lo que comunmente se le llama "magia" o en ingles "compiler magic", o el termino mas tecnico Intrinsic Routines (http://docwiki.embarcadero.com/RADStudio/Seattle/en/Delphi_Intrinsic_Routines)

La clase en cuestion es la TInerlocked (http://docwiki.embarcadero.com/Libraries/Seattle/en/System.SyncObjs.TInterlocked)

La misma dispone de la funcion estatica Exchange que me imagino que servira para cualquiera de las plataformas que soporta Delphi

Saludos

Al González
18-02-2016, 08:33:31
Toma muy en cuenta la respuesta de AgustinOrtu. :) ^\||/

Una pregunta: ¿de qué tipo es FThread?

En caso de ser puntero (u objeto), ten presente que una expresión como Integer(FThread), sólo considerará los cuatro bytes más bajos, ya que Integer es un tipo de dato de 32 bits (aun en plataformas de 64 bits), mientras que un puntero ocupa ocho bytes en sistemas operativos de 64 bits. Es decir, los punteros (los cuales abarcan a las variables objeto también) contienen un valor de 32 o de 64 bits, dependiendo de la plataforma para la que se haya compilado la aplicación.

Un saludo.

Al González.

xangiesaurx
18-02-2016, 19:42:38
Muchas gracias por su ayuda, lo eh intentado y me sigue marcando este error:
"[dcc64 Error] BaseClass.pas(9911): E2197 Constant object cannot be passed as var parameter"

Y el código lo eh dejado de la siguiente manera en lo que encuentro como hacerlo funcionar:

procedure TBCAMThread.Close;
var
Thread: THandle;
begin
{$IFDEF WIN32}
Thread := InterlockedExchange(Integer(FThread), 0);
{$ELSE}
{$IFDEF WIN64}


{$ENDIF}
{$ENDIF}
if BOOL(Thread) then
begin
WaitForSingleObject(Thread, INFINITE);
CloseHandle(Thread);
end;
end;

AgustinOrtu
18-02-2016, 21:47:52
El problema es que estas usando la funcion incorrectamente

Si vemos la firma (http://docwiki.embarcadero.com/Libraries/XE8/en/System.SyncObjs.TInterlocked.Exchange) de la funcion, tenemos:

class function Exchange(var Target: Pointer; Value: Pointer): Pointer; overload; static; inline;
class function Exchange(var Target: Integer; Value: Integer): Integer; overload; static; inline;
class function Exchange(var Target: Int64; Value: Int64): Int64; overload; static; inline;
class function Exchange(var Target: TObject; Value: TObject): TObject; overload; static; inline;
class function Exchange(var Target: Double; Value: Double): Double; overload; static; inline;
class function Exchange(var Target: Single; Value: Single): Single; overload; static; inline;
class function Exchange<T: class>(var Target: T; Value: T): T; overload; static; inline;

Es importante notar que el primer parametro esta pasado por referencia, de ahi el modificador var

Quiere decir que no se puede enviar por parametro una expresion constante (http://docwiki.embarcadero.com/RADStudio/Seattle/en/Declared_Constants#Constant_Expressions)

Un ejemplo de uso correcto de la funcion TInterlocked.Exchange podria ser:

uses
System.SyncObjs;

procedure TForm1.Button1Click(Sender: TObject);
var
A, B, C: Integer;
begin
A := 1;
B := 2;
C := TInterlocked.Exchange(A, B);

ShowMessageFmt('A = %d, B = %d, C = %d', [A, B, C]);
end;

En donde el cuadro de dialogo imprime el mensaje:

A = 2, B = 1, C = 1

Lo cual es correcto, ya que si revisamos la implementacion de TInerlocked.Exchange, simplemente invoca a otra rutina del compilador, la AtomicExchange, donde la documentación (http://docwiki.embarcadero.com/Libraries/Seattle/en/System.AtomicExchange) explica:

Writes the given value to the target and returns the previous value of the target.

--

Por otro lado tené muy en cuenta lo que comento Al Gonzalez; para responder con precision deberiamos saber el tipo de FThread

Si bien Delphi hace bastante sencilla la transicion de 32 a 64 bits, hay algunos puntos para revisar:

Revisa esta entrada (https://delphiaball.co.uk/2016/02/16/delphi-64bit-code/)

xangiesaurx
19-02-2016, 03:23:00
AgustinOrtu, gracias por tu ayuda, parece que ya quedo solucionado, el código quedo de la siguiente manera

{$IFDEF WIN32}
Thread := InterlockedExchange(Integer(FThread), 0);
{$ELSE}
{$IFDEF WIN64}
Thread := TInterlocked.Exchange(Int64(FThread), 0);
{$ENDIF}
{$ENDIF}


Lo hice de esta manera ya que me pidieron que tuviera el conditional compilation

AgustinOrtu
19-02-2016, 03:30:08
Sólo una cosa más para puntualizar

Se recomienda el uso del tipo NativeInt para los casos en los que, como este, hay que lidiar con el diferente tamaño de los punteros

El tipo NativeInt, esta definido como un entero de 32 bits para el compilador de 32 bits, y como entero de 64 bits para el compilador de 64 bits

Es una manera de escribir código compatible a través de todas las plataformas, asegurando compatibilidad hacia adelante y atrás

xangiesaurx
20-02-2016, 01:39:11
Si, muchas gracias, se me había pasado lo del NativeInt :P Pero ya esta implementado :)

Ahora tengo otra duda (ya se que soy algo latosa en esto), pero me acabo de encontrar parte de código ensamblador, pero a lo que eh leido, ya no es compatible con XE5, existe alguna forma de lograr esta compatibilidad?
(Aquí dejo el código en cuestion)

function FastCpySSE(const D:Pointer; const S:Pointer; const count:dword):integer;
var dwNumElements, dwNumPacks:DWORD;
begin
dwNumElements := count div sizeof(integer);
// not really using it, just for debuging. it keeps number of looping.
// it also means number of packed data.
dwNumPacks := dwNumElements div (128 div (sizeof(integer)*8));

asm
// remember for cleanup
pusha;
@@begin:
// init counter to SizeInBytes
mov ecx,count
// get destination pointer
mov edi,D
// get source pointer
mov esi,S
@@begina:
// check if counter is 0, yes end loop.
cmp ecx,0
jz @@end
@@body:
// calculate offset
mov ebx,count
sub ebx,ecx
// copy source's content to 128 bits registers
movdqa xmm1,[esi+ebx]
// copy 128 bits registers to destination
movdqa [edi+ebx],xmm1;

@@bodya:
// we've done "1 packed == 4 * sizeof(int)" already.
sub ecx,16;
jmp @@begina
@@end:
// cleanup
popa;
end;

result := 0;;
end;

Gracias por toda su ayuda y de nuevo una disculpa por las molestias

AgustinOrtu
20-02-2016, 02:15:06
xangiesaurx

Realmente no domino ASM, pero de todos modos deberías iniciar una consulta nueva y no mezclar en un mismo hilo varias preguntas; asi lo dice la guía de estilo (http://www.clubdelphi.com/foros/guiaestilo.php)

xangiesaurx
20-02-2016, 02:17:34
De acuerdo, entonces este tema se puede dar por cerrado.
Abriré uno nuevo con referencia a ASM y XE5, muchas gracias por toda la ayuda :)