Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2013-07

Improper initialization of manged types

15 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Anthony Walter

Problem: Free Pascal is not properly initializing and finalizing an interface type resulting in code being invoked for a garbage pointer. The error is highlighted at the end.

Previously I filed a bug report on this issue and received feedback that the bug could not be reproduced, but it's hitting me me big time when building on 32-bit Linux (I been working on 64-bit Linux withe same same compiler source without these problems). I'd like to post some code and get feedback on how to fix this problem, if it is either something I am doing or a problem with the compiler.

I have an interface type, what I would call a managed type since the compiler ought to generate code to initialize and finalize its value, which is used to share 1 or more contiguous floats:

  IDependencyProperty = interface
  ['{E021AD95-9985-48AB-B29F-8D25A7BBE10E}']
    function GetCount: Integer;
    function GetValue(Index: Integer): Float;
    procedure SetValue(Value: Float; Index: Integer);
    property Count: Integer read GetCount;
  end;

The interface is exposed by records TVec(n)Prop where n represents the count or available floats. These properties are used to share float vectors with animations/storyboards or other objects. Here is an the listing of TVec1Prop:

  TVec1Prop = record
  private
    function GetValue: TVec1;
    procedure SetValue(Value: TVec1);
    function GetVec(Index: Integer): TVec1Prop;
    procedure SetVec(Index: Integer; const Value: TVec1Prop);
  public
    class operator Implicit(const Value: TVec1): TVec1Prop;
    class operator Implicit(const Value: TVec1Prop): TVec1;
    class operator Negative(const A: TVec1Prop): TVec1;
    class operator Positive(const A: TVec1Prop): TVec1;
    class operator Equal(const A, B: TVec1Prop) : Boolean;
    class operator NotEqual(const A, B: TVec1Prop): Boolean;
    class operator GreaterThan(const A, B: TVec1Prop): Boolean;
    class operator GreaterThanOrEqual(const A, B: TVec1Prop): Boolean;
    class operator LessThan(const A, B: TVec1Prop): Boolean;
    class operator LessThanOrEqual(const A, B: TVec1Prop): Boolean;
    class operator Add(const A, B: TVec1Prop): TVec1;
    class operator Subtract(const A, B: TVec1Prop): TVec1;
    class operator Multiply(const A, B: TVec1Prop): TVec1;
    class operator Divide(const A, B: TVec1Prop): TVec1;
    procedure Link(OnChange: TDependencyChangeNotify = nil); overload;
    procedure Link(Prop: IDependencyProperty; Index: LongInt); overload;
    procedure Unlink;
    function Linked: Boolean;
    function Equals(const A: TVec1Prop): Boolean;
    function Same(const A: TVec1Prop): Boolean;
    property X: TVec1Prop index 0 read GetVec write SetVec;
    property Value: TVec1 read GetValue write SetValue;
    property Vec[Index: Integer]: TVec1Prop read GetVec write SetVec;
  private
    FProp: IDependencyProperty;
    case Boolean of
      True: (FIndex: LongInt);
      False: (FValue: TVec1);
  end;

Part of the implementation looks like this with an example of the ERROR in comments:

{ TVec1Prop }

class operator TVec1Prop.Implicit(const Value: TVec1Prop): TVec1;
begin
  Result := Value.Value;
end;
class operator TVec1Prop.Implicit(const Value: TVec1): TVec1Prop;
begin
  // ERROR: WriteLn(UIntPtr(Result.FProp)); writes out a random memory
location
  // ERROR: Result.FProp should be nil already but it is not
  Result.FProp := nil; // ERROR: Code is generated which attempts to call
release on random garbage
  Result.FIndex := 0;
  Result.FValue := Value;
end;
function TVec1Prop.GetValue: TVec1;
begin
  if FProp = nil then
    Result := FValue
  else
    Result := FProp.GetValue(FIndex);
end;
procedure TVec1Prop.SetValue(Value: TVec1);
begin
  if FProp = nil then
    FValue := Value
  else
    FProp.SetValue(Value, FIndex);
end;

The problem is happening in a lot of places which use my TVec(n)Prop types, resulting in many AV errors, but again only when I am recompiling my demo programs on 32-bit Linux (with the same compiler source). For example I have a list of TVec1Prop, when I clear the list I am getting AV errors due to some items not being finalized correctly.

What should I do?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20130709/04506e79/attachment.html>

Reply by email

Sean O'Connor
to Anthony Walter

while not being an experienced programmer myself, one thing does spring to mind. so apologies if if I am mistaken. but it does seem possible that the compiler migvht be compiling to 64 bit as opposed to 32 bit.. I hope that helps somewhat but i am sure others with greater knowledge than I will probably suggest other, equyally helpfull solutions.

Anthony Walter wrote 116 lines
Problem: Free Pascal is not properly initializing and finalizing an interface type resulting in code being invoked for a garbage pointer. The error is highlighted at the end. Previously I filed a bug report on this issue and received feedback that the bug could not be reproduced, but it's hitting me me big time when building on 32-bit Linux (I been working on 64-bit Linux withe same same compiler source without these problems). I'd like to post some code and get feedback on how to fix this problem, if it is either something I am doing or a problem with the compiler. I have an interface type, what I would call a managed type since the compiler ought to generate code to initialize and finalize its value, which is used to share 1 or more contiguous floats: IDependencyProperty = interface ['{E021AD95-9985-48AB-B29F-8D25A7BBE10E}'] function GetCount: Integer; function GetValue(Index: Integer): Float; procedure SetValue(Value: Float; Index: Integer); property Count: Integer read GetCount; end; The interface is exposed by records TVec(n)Prop where n represents the count or available floats. These properties are used to share float vectors with animations/storyboards or other objects. Here is an the listing of TVec1Prop: TVec1Prop = record private function GetValue: TVec1; procedure SetValue(Value: TVec1); function GetVec(Index: Integer): TVec1Prop; procedure SetVec(Index: Integer; const Value: TVec1Prop); public class operator Implicit(const Value: TVec1): TVec1Prop; class operator Implicit(const Value: TVec1Prop): TVec1; class operator Negative(const A: TVec1Prop): TVec1; class operator Positive(const A: TVec1Prop): TVec1; class operator Equal(const A, B: TVec1Prop) : Boolean; class operator NotEqual(const A, B: TVec1Prop): Boolean; class operator GreaterThan(const A, B: TVec1Prop): Boolean; class operator GreaterThanOrEqual(const A, B: TVec1Prop): Boolean; class operator LessThan(const A, B: TVec1Prop): Boolean; class operator LessThanOrEqual(const A, B: TVec1Prop): Boolean; class operator Add(const A, B: TVec1Prop): TVec1; class operator Subtract(const A, B: TVec1Prop): TVec1; class operator Multiply(const A, B: TVec1Prop): TVec1; class operator Divide(const A, B: TVec1Prop): TVec1; procedure Link(OnChange: TDependencyChangeNotify = nil); overload; procedure Link(Prop: IDependencyProperty; Index: LongInt); overload; procedure Unlink; function Linked: Boolean; function Equals(const A: TVec1Prop): Boolean; function Same(const A: TVec1Prop): Boolean; property X: TVec1Prop index 0 read GetVec write SetVec; property Value: TVec1 read GetValue write SetValue; property Vec[Index: Integer]: TVec1Prop read GetVec write SetVec; private FProp: IDependencyProperty; case Boolean of True: (FIndex: LongInt); False: (FValue: TVec1); end; Part of the implementation looks like this with an example of the ERROR in comments: { TVec1Prop } class operator TVec1Prop.Implicit(const Value: TVec1Prop): TVec1; begin Result := Value.Value; end; class operator TVec1Prop.Implicit(const Value: TVec1): TVec1Prop; begin // ERROR: WriteLn(UIntPtr(Result.FProp)); writes out a random memory location // ERROR: Result.FProp should be nil already but it is not Result.FProp := nil; // ERROR: Code is generated which attempts to call release on random garbage Result.FIndex := 0; Result.FValue := Value; end; function TVec1Prop.GetValue: TVec1; begin if FProp = nil then Result := FValue else Result := FProp.GetValue(FIndex); end; procedure TVec1Prop.SetValue(Value: TVec1); begin if FProp = nil then FValue := Value else FProp.SetValue(Value, FIndex); end; The problem is happening in a lot of places which use my TVec(n)Prop types, resulting in many AV errors, but again only when I am recompiling my demo programs on 32-bit Linux (with the same compiler source). For example I have a list of TVec1Prop, when I clear the list I am getting AV errors due to some items not being finalized correctly. What should I do? -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20130709/53ed3ce6/attachment-0001.html>

Reply by email

Anthony Walter
to Sean O'Connor

Thanks for the feedback Sean.

What's happening is I have a laptop with removable hard drive. I had done development for a few weeks on a drive with a 64-bit Linux OS installed using a 64-bit build of FPC. Now that I have completed my milestones I am preparing to publish updates, but before doing so I am switching hard drives to boot to a different OS and recompile/test my work. The purpose of which is to ensure my project runs the same as my original tests on 32-bit Linux, 64-Bit Windows, and 32-bit Windows.

Using the same compiler sources, and library sources, when I attempt to recompile on 32-bit Linux with FPC built and configured to use 32-bit Linux, I am seeing errors such as those I described in my original post, that is access violations caused by garbage references when using managed types.

So to answer your post, I am experiencing these issues when building 32-bit test applications on 32-bit Linux, while I had not experienced these issues on a 64-bit Linux with FPC building 64-bit test applications.

I guess what I am going to do now is take another hard drive and do a fresh 32-bit Linux install, copy my sources, and rebuild the FPC compiler with the same sources along with my libraries (the dependencies) and see if these same problems persist. I've done enough testing and attempted to fix this issue on 32-bit Linux and have run out of ways to work around this problem, though I doubt this will fix the issue :( help! -------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20130709/c083b235/attachment.html>

Reply by email

waldo kitty
to Anthony Walter
Anthony Walter wrote 9 lines
Thanks for the feedback Sean. What's happening is I have a laptop with removable hard drive. I had done development for a few weeks on a drive with a 64-bit Linux OS installed using a 64-bit build of FPC. Now that I have completed my milestones I am preparing to publish updates, but before doing so I am switching hard drives to boot to a different OS and recompile/test my work. The purpose of which is to ensure my project runs the same as my original tests on 32-bit Linux, 64-Bit Windows, and 32-bit Windows.

you are telling lazarus to clean all the old .a .o and ppu files before compiling fresh, right? i'm thinking that it is possible that some old binaries libraries are possibly left behind...

NOTE: No off-list assistance is given without prior approval. Please keep mailing list traffic on the list unless private contact is specifically requested and granted.

Reply by email

Anthony Walter
to Anthony Walter: Thanks for the feedback Sean. What's happening is I have a laptop with removable hard…

If anyone is confused as to what is happening with TVec(n)Prop here are some examples from my tests.

Camera has TVec3Props called Direction (Heading, Pitch, Roll) and Position (X, Y Z) which allows a Storyboard to hold a direct reference to those Camera properties allowing for animation of the view point and view direction:

  ZoomIn := TStoryboard.Create(World);
  ZoomIn.Add(Skybox.Camera.Direction, Vec3(0))
    .Duration(0.5)
    .Easing('easy');
  ZoomIn.Add(Skybox.Camera.Position, Vec3(0))
    .Duration(0.5)
    .Easing('easy');
  ZoomOut := TStoryboard.Create(World);
  ZoomOut.Add(Skybox.Camera.Direction, Vec3(30, 20, 0))
    .Duration(0.5)
    .Easing('easy');
  ZoomOut.Add(Skybox.Camera.Position, Vec3(-8, 8, 4))
    .Duration(0.5)
    .Easing('easy');

... pressing F2 causes the camera to play alternating Storyboards to zoom the camera in or out in animated fashion ...

  if MessageQueue.KeyDown(VK_F2) then
  begin
    if (Skybox.Camera.Direction = Vec3(0)) and (Skybox.Camera.Position =
Vec3(0)) then
      ZoomOut.Play
    else
      ZoomIn.Play;
  end

... Another example where a button is animated without a Storyboard. Two vec properties are accessed, Color: TVec4Prop (which has Count 4 and components, Red, Greem Blue, and Alpha), and Scale: TVec2Prop (which has Count 2 components, X and Y, buttons are 2d in a 3d world and thus TVec2Prop is used) ...

      Animations.Add(Button.Color.Alpha, 0.5)
        .Duration(0.2)
        .Easing('Easy');
      Animations.Add(Button.Scale, Vec2(1.2))
        .Duration(0.2)
        .Easing('Boing');

... The code above causes the button to fade the Alpha color component from it's current value to 0.5 (half transparency) in 0.2 seconds using the 'Easy' easing, and to scale the button to 1.2 it's original size in 0.2 seconds using the 'Boing' easing.

http://cache.codebot.org/snapshops/easing-modes.jpg

Older copy of the animation unit on github

https://github.com/sysrpl/Bare.Game/blob/master/source/bare.animation.pas -------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20130709/0a9489d9/attachment.html>

Reply by email

Anthony Walter
to waldo kitty: you are telling lazarus to clean all the old .a .o and ppu files before compiling fresh,…

waldo-kitty: Yes, I've deleted everything in my /lib/i386-linux folder save for my static libraries libSDL2.a (from RC3 sources) and libtess.a (from mesa glu tessellation sources), which I've compiled myself from C sources using gcc/ming-w64 for 32/64 Linux and Windows.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20130709/9fbf3df8/attachment.html>

Reply by email

Marc Santhoff
to Anthony Walter: Thanks for the feedback Sean. What's happening is I have a laptop with removable hard…
Anthony Walter wrote 6 lines
I guess what I am going to do now is take another hard drive and do a fresh 32-bit Linux install, copy my sources, and rebuild the FPC compiler with the same sources along with my libraries (the dependencies) and see if these same problems persist. I've done enough testing and attempted to fix this issue on 32-bit Linux and have run out of ways to work around this problem, though I doubt this will fix the issue :( help!

Ahem ... maybe cleaning out all binaries will suffice? Only my first thought ... I'm mostly sure you did that already.

But the reason why Im posting to this thread is something else. I'm curious why you decided to make those TVec(n)Prop records instead of classes?

I didn't even know that it is legal Object Pascal code ... embarassing. ;)

Marc Santhoff

Reply by email

Anthony Walter
to Marc Santhoff: Ahem ... maybe cleaning out all binaries will suffice? Only my first thought ... I'm…

The actual backing storage IDependencyProperty is handled by a class (an object). Records types are used to manage references back to a shared copy of the property (perhaps a Storyboard has a copy of the Alpha value of a color component being animated), or to hold an unlinked (no backing IDependencyProperty) vector.

For example, Sprite class has:

property Color: TVec4Prop read FColor write SetColor;
property Origin: TVec2Prop read FOrigin write SetOrigin;
property Size: TVec2Prop read FSize write SetSize;
property Position: TVec3Prop read FPosition write SetPosition;
property Rotation: TVec3Prop read FRotation write SetRotation;
property Angle: TVec1Prop read GetAngle write SetAngle;

Any of these properties can be animated or if you prefer changed directly. You could say ...

var P: TVec3Prop;
...
P := PlayerSprite.Position;
P.X := 100;

And PlayerSprite moves to the X location 100, assuming it is not overwritten by a currently running Animation or Storyboard. You could say Animations.Remove(P.X) or Animations.Remove(PlayerSprite.Position.X) to remove any animations of the property.

{ Animate PlayerSprite Y position to a target of 50, delay the start by 1 second,
complete in 0.5 seconds, infinitely loop the animation reversing the values,
and use the Extend easing when moving between the start value and target value }
Animations.Add(P.Y, 50)
.Start(1)
.Duration(0.5)
.Looping(loopReverse, loopInfinite)

  .Easing(Easings.Extend);

Alternately you could write:

Animations.Add(P, Vec2(100, 50))
.Duration(0.25)
.Easing('Extend')

Where Animations.Add (and Storyboard as well, they share a common inherited class) is overloaded to look like this:

{ Store a vec1 property animation returning customization arguments }
function Add(const Prop: TVec1Prop; const Target: TVec1): TAnimationArgs;
overload;
{ Store a vec2 property animation returning customization arguments }
function Add(const Prop: TVec2Prop; const Target: TVec2): TAnimationArgs;
overload;
{ Store a vec3 property animation returning customization arguments }
function Add(const Prop: TVec3Prop; const Target: TVec3): TAnimationArgs;
overload;
{ Store a vec4 property animation returning customization arguments }
function Add(const Prop: TVec4Prop; const Target: TVec4): TAnimationArgs;
overload;

Animations can only occur if a TVec(n)Prop is backed by an IDepenencyProperty.

P := Vec3(100, 50, 0);

Would overwrite the local TVec3Prop variable so that it no longer references a the IDepenencyProperty held by PlayerSprite.Position. To maintain the reference to the PlayerSprite.Position you could say:

P.Value := Vec3(100, 50, 0);

Which is what the property setter SetPosition does through an implicit conversion from a TVec3 (Vec3 is a 3 component record and is used to create geometry, surface normals, rotation matricies, 3d stuff) to a TVec3Prop (TVec(n)Prop allow an array of floating point values to be shared by reference, and thus are good for storage for animation).

So using assigning Vec3(100, 50, 0) to a property causes an implicit conversion to an unlinked (a TVec(n)Prop where the IDepenencyProperty is nil) property.

class operator TVec3Prop.Implicit(const Value: TVec3): TVec3Prop;
begin
  Result.FProp := nil;
  Result.FIndex := 0;
  Result.FValue := Value;
end;

And this is the type of situation where I get access violations, because coming into the methods like the ones above Result.FProp is not nil and the compiler tries to call release on a garbage value.

Regarding deleting object files, yes I done that and the same errors occur.

Unfortunately I do not have a simple example where I can reproduce these errors as they typically happen randomly, but often enough that the my test programs crash right away. And again I don't have this problem with the same compiler sources and same library on 64-bit Linux. -------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20130709/b1193b6e/attachment-0001.html>

Reply by email

waldo kitty
to Mattias Gaertner: On Tue, 9 Jul 2013 16:19:58 -0400 Have you tried Run / Clean up build files? Mattias
Mattias Gaertner wrote 9 lines
On Tue, 9 Jul 2013 16:19:58 -0400 Anthony Walter wrote: > waldo-kitty: Yes, I've deleted everything in my /lib/i386-linux folder save > for my static libraries libSDL2.a (from RC3 sources) and libtess.a (from > mesa glu tessellation sources), which I've compiled myself from C sources > using gcc/ming-w64 for 32/64 Linux and Windows. Have you tried Run / Clean up build files?

that's what i was trying to say... i just couldn't get the right terms out :?

NOTE: No off-list assistance is given without prior approval. Please keep mailing list traffic on the list unless private contact is specifically requested and granted.

Reply by email

Paul Ishenin
to Anthony Walter: Problem: Free Pascal is not properly initializing and finalizing an interface type…

10.07.13, 1:55, Anthony Walter ?????:

Previously I filed a bug report on this issue and received feedback that the bug could not be reproduced, but it's hitting me me big time when building on 32-bit Linux (I been working on 64-bit Linux withe same same compiler source without these problems).

Please give a link to your bug report.

Best regards,
Paul Ishenin

Reply by email

Paul Ishenin
to Anthony Walter

10.07.13, 12:38, Anthony Walter ?????:

Thanks. I remember I debugged that report and found that RTL initialized the record variable.

Do you still have the problem with given (in the bug report) example or should I test your bigger example from first mail of this thread?

At the same time can you check (using the debugger) whether fpc_initialize routine is called for the method where you see an error and what happens in this routine (whether it calls recordrtti or not, whether recordrtti routine calls rttiproc(), whether in the later call you see PPchar(Data)^ := Nil is executed for the interface field or not).

Best regards,
Paul Ishenin

Reply by email

Anthony Walter
to Paul Ishenin

http://cache.codebot.org/snapshops/managederror.jpg

I put a few lines to test what's going on before the error occurs and a screenshot is provided above. WriteLn is writing out a random integer value. I don't see any code in the assembler window to initialize the result. In that screenshot the test is getting ready to encounter an access violation on assignment of nil to FProp.

Result.FProp := nil; // ERROR: will occur here

The problem is this sort of thing is happening for me right now in a lot of places, for example when I resize a dynamic array holding a TVec(N)Prop types, or when I have a local variable.

You can actually see in that screenshot what I had tried to fix this problem previously. That is, putting code like this as the first line in some methods:

UIntPtr(Result.FProp) := 0;

This works somewhat, but in other places this isn't possible or is very impractical:

procedure TList<TItem>.DeleteItem(var Item: ItemType); { virtual }
begin
  Item := default(TItem); // BOOM when TItem is TVec(n)Prop
end;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20130710/ca8517cb/attachment.html>

Reply by email

Paul Ishenin
to Anthony Walter

10.07.13, 20:52, Anthony Walter ?????:

Show quoted text 17 lines
http://cache.codebot.org/snapshops/managederror.jpg I put a few lines to test what's going on before the error occurs and a screenshot is provided above. WriteLn is writing out a random integer value. I don't see any code in the assembler window to initialize the result. In that screenshot the test is getting ready to encounter an access violation on assignment of nil to FProp. Result.FProp := nil; // ERROR: will occur here The problem is this sort of thing is happening for me right now in a lot of places, for example when I resize a dynamic array holding a TVec(N)Prop types, or when I have a local variable. You can actually see in that screenshot what I had tried to fix this problem previously. That is, putting code like this as the first line in some methods:

Can you add a complete example to the bug report you had? The previous example attached to it worked for me. Maybe the extended example with class operators will not work properly?

Best regards,
Paul Ishenin

Reply by email