Reputation: 474
i am porting a Server built on Indy TCP Component from Delphi 7 to XE5 for enabling 64-bit support. Now after i did all the porting and ran the server it worked just fine in testing environment. It worked great up till the user count exceeded 400. After that it starts creating Access violation errors. Sometimes breaking on the error points me into the indy source files and sometimes to the CPU window. I cant pinpoint the area of code in my sources where the error is generated.
While this kind of access violation error means i am accessing some object which is not yet instantiated, but why isnt the error generating when less users are online and the whole code is exactly same as the Delphi 7 code, the objects are accessed and freed in the same way as i was doing in Delphi 7.
I read somewhere that casting pointer related stuff should use NativeInt instead of Integer/Cardinal, Now in the code where i process incoming data, the code looks like this
procedure TMyContext.AddToPacketBuffer(Buffer: Pointer; Size: Integer);
var
DestPtr: Pointer;
begin
if PacketBufferPtr + Size<65536 then
begin
DestPtr := Pointer(Cardinal(FPacketBuffer)+Cardinal(PacketBufferPtr));
Move(Buffer^,DestPtr^,Size);
PacketBufferPtr := PacketBufferPtr + Size;
end
else
begin
end;
end;
FPacketBuffe
r is a global Pointer declared in each TMyContext class, and PacketBufferPtr
is an Integer variable declared in each TMyContext classShould i use NativeInt here instead of Cardinal? could this be the root of the issue? if yes why is it not creating error when the user count is below 400, i tested using all the functions in local environment and no single portion of code generates an error.
Thanks
Upvotes: 2
Views: 559
Reputation: 612964
What you must understand about 64 bit code is that pointers are 64 bits wide. In contrast to 32 bit code where pointers are 32 bits wide. Now, the native integer types Integer
and Cardinal
are always 32 bits wide. Clearly you cannot fit all 64 bit values in a 32 bit type.
You are correct that this code is broken under 64 bit. Casting a 64 bit pointer to a 32 bit integer may lead to truncation. The code may work if the address fits in your 32 bit type. If you must perform such a cast then you need to cast to NativeInt
or NativeUInt
. And as well as changing the casts, you would need to declare any variables that hold pointers as NativeInt
or NativeUInt
.
Of course, ideally you should strive to avoid such casts altogether. You can do that by not using an Integer
variable at to store pointers. Store pointers as pointers, avoid casting to integers and never suffer pointer truncation bugs.
That said, the name of PacketBufferPtr
is very misleading. It is not a pointer as the name would suggest. It is an offset. It should be named PacketBufferOffset
. It seems plausible that it will never exceed high(Integer)
and so perhaps Integer
is a sound choice. But it would never hurt to declare it to be NativeUInt
.
So, assuming that you declare the pointers as PByte
, and the offset as NativeUInt
then the code would be written like this:
var
FPacketBuffer: PByte;
PacketBufferOffset: NativeUInt;
....
procedure TMyContext.AddToPacketBuffer(Buffer: Pointer; Size: Integer);
var
DestPtr: PByte;
begin
if PacketBufferOffset + Size<65536 then
begin
DestPtr := FPacketBuffer + PacketBufferOffset;
Move(Buffer^, DestPtr^, Size);
inc(PacketBufferOffset, Size);
end
else
begin
end;
end;
And now the code is free of casts.
I recommend that you enable top down memory allocation as a debugging aid. This will flush out more bugs of this nature.
Upvotes: 4