Lazarus

Free Pascal => Beginners => Topic started by: jan741 on December 07, 2022, 02:22:51 pm

Title: Howto send Record to thread via Pointer?
Post by: jan741 on December 07, 2022, 02:22:51 pm
I need send a record(containing numeral and string) to multiple threads. How to?

Tried different things, this doesn't work:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, dbugintf, LazLoggerBase, pingsend;
  10.  
  11. const
  12.   threadcount = 100;
  13.   stringlen = 10000;
  14.  
  15. type
  16.  
  17.   { TForm1 }
  18.  
  19.   TForm1 = class(TForm)
  20.     Button1: TButton;
  21.     Button2: TButton;
  22.     procedure Button1Click(Sender: TObject);
  23.     procedure Button2Click(Sender: TObject);
  24.   private
  25.     { private declarations }
  26.   public
  27.     { public declarations }
  28.   end;
  29.  
  30.   myrec = record
  31.     i : longint;
  32.     s : string;
  33.   end;
  34.  
  35.   Pmyrec = ^myrec;
  36.  
  37. var
  38.   Form1: TForm1;
  39.   finished : longint;
  40.  
  41. threadvar
  42.          thri : ptrint;
  43.  
  44. implementation
  45. {$R *.lfm}
  46.  
  47. { TForm1 }
  48.  
  49. function f(p : Pointer) : ptrint;
  50.  
  51. var
  52.   s : ansistring;
  53.   thisrec : myrec;
  54. begin
  55.  
  56.   Writeln('thread ', longint(p^.i), 'started');
  57.   thri:=0;
  58.   while (thri<stringlen) do
  59.     begin
  60.     s:=s+'1';
  61.     inc(thri);
  62.     end;
  63.   Writeln('thread ',p^.i,' finished with',p^.s);
  64.   InterLockedIncrement(finished);
  65.   f:=0;
  66. end;
  67.  
  68. procedure TForm1.Button1Click(Sender: TObject);
  69. begin
  70.  
  71. end;
  72.  
  73. procedure TForm1.Button2Click(Sender: TObject);
  74. var
  75.   r : myrec;
  76.   x : longint;
  77. begin
  78.    finished:=0;
  79.    r.s := ' test ';
  80.    for x :=1 to threadcount do
  81.      r.i := x;
  82.      BeginThread(@f,@r);
  83.    while finished<threadcount do ;
  84.    Writeln(finished);
  85. end;
  86.  
  87.  
  88. end.
Title: Re: Howto send Record to thread via Pointer?
Post by: Zvoni on December 07, 2022, 02:51:08 pm
errr.... is there a Begin/End missing in the For-loop?
The For-Loop as it stands only executes line 81 for 100 times
BeginThread is only executed once (with r.i=100) because it's outside the For-Loop
Title: Re: Howto send Record to thread via Pointer?
Post by: jan741 on December 07, 2022, 03:05:57 pm
Yes thanks.
That was also a problem. I'm more used to {} or inclinations(python)  8-)
Title: Re: Howto send Record to thread via Pointer?
Post by: jan741 on December 07, 2022, 03:16:07 pm
Now I got it working. But thirsrec^ in every thread is referencing to the same object (on the programstack I think) So every thread writes "100" as output. So, I should have a local copy(on the thread-stack if that exists) for every thread of r:myrec. Is that possible?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, dbugintf, LazLoggerBase, pingsend;
  10.  
  11. const
  12.   threadcount = 100;
  13.   stringlen = 10000;
  14.  
  15. type
  16.  
  17.   { TForm1 }
  18.  
  19.   TForm1 = class(TForm)
  20.     Button1: TButton;
  21.     Button2: TButton;
  22.     procedure Button1Click(Sender: TObject);
  23.     procedure Button2Click(Sender: TObject);
  24.   private
  25.     { private declarations }
  26.   public
  27.     { public declarations }
  28.   end;
  29.  
  30.   myrec = record
  31.     i : longint;
  32.     s : string;
  33.   end;
  34.  
  35.   Pmyrec = ^myrec;
  36.  
  37. var
  38.   Form1: TForm1;
  39.   finished : longint;
  40.  
  41. threadvar
  42.          thri : ptrint;
  43.  
  44. implementation
  45. {$R *.lfm}
  46.  
  47. { TForm1 }
  48.  
  49. function f(p : Pointer) : ptrint;
  50.  
  51. var
  52.   s : ansistring;
  53.   thisrec : ^myrec;
  54. begin
  55.   thisrec := Pmyrec(p);
  56.   Writeln('thread ', longint(thisrec^.i), ' started');
  57.   thri:=0;
  58.   while (thri<stringlen) do
  59.     begin
  60.     s:=s+'1';
  61.     inc(thri);
  62.     end;
  63.   Writeln('thread ',thisrec^.i,' finished with',thisrec^.s);
  64.   InterLockedIncrement(finished);
  65.   f:=0;
  66.   writeln('end of function');
  67. end;
  68.  
  69. procedure TForm1.Button1Click(Sender: TObject);
  70. begin
  71.  
  72. end;
  73.  
  74. procedure TForm1.Button2Click(Sender: TObject);
  75. var
  76.   r : myrec;
  77.   x : longint;
  78. begin
  79.    finished:=0;
  80.    r.s := ' test ';
  81.    for x :=1 to threadcount do
  82.      begin
  83.        r.i := x;
  84.        BeginThread(@f,@r);
  85.        writeln('ready for nex t');
  86.      end;
  87.    while finished<threadcount do ;
  88.    Writeln('goopend ' + IntToStr(finished));
  89. end;
  90. end.
Title: Re: Howto send Record to thread via Pointer?
Post by: mika on December 07, 2022, 04:44:32 pm
Code: Pascal  [Select][+][-]
  1. program Unit1;
  2. {$apptype console}
  3. {$mode objfpc}{$H+}
  4.  
  5.  
  6. uses
  7. {$ifdef UNIX}  cthreads, {$endif}
  8.    Classes, SysUtils;
  9.  
  10. const
  11.       threadcount = 100;
  12.       stringlen = 10000;
  13.  
  14. type
  15.   myrec = record
  16.     i : longint;
  17.     s : string;
  18.   end;
  19.  
  20.   Pmyrec = ^myrec;
  21.  
  22. var
  23.   finished : longint;
  24.  
  25. threadvar
  26.          thri : ptrint;
  27.  
  28.  
  29. function f(p : Pointer) : ptrint;
  30.  
  31. var
  32.   s : ansistring;
  33.   thisrec : ^myrec;
  34. begin
  35.   thisrec := Pmyrec(p);
  36.   Writeln('thread ', longint(thisrec^.i), ' started');
  37.   thri:=0;
  38.   while (thri<stringlen) do
  39.     begin
  40.     s:=s+'1';
  41.     inc(thri);
  42.     end;
  43.   Writeln('thread ',thisrec^.i,' finished with',thisrec^.s);
  44.   InterLockedIncrement(finished);
  45.   f:=0;
  46.   writeln('end of function');
  47. end;
  48.  
  49. procedure button;
  50. var
  51.   r : array [1..threadcount] of myrec;
  52.   x : longint;
  53. begin
  54.    finished:=0;
  55.  
  56.    for x :=1 to threadcount do
  57.      begin
  58.        r[x].s := ' test ';
  59.        r[x].i := x;
  60.        BeginThread(@f,@r[x]);
  61.        writeln('ready for nex t');
  62.      end;
  63.    while finished<threadcount do ;
  64.    Writeln('goopend ' + IntToStr(finished));
  65. end;
  66.  
  67. begin
  68.      button;
  69. end.
In way you are using record in thread, you have to have an original record in main thread. I made array of record and passed new unique record to new thread.
Title: Re: Howto send Record to thread via Pointer?
Post by: avra on December 08, 2022, 08:34:39 am
I need send a record(containing numeral and string) to multiple threads. How to?
Since you are using forms, the simplest way is to use QueueAsyncCall:
https://forum.lazarus.freepascal.org/index.php/topic,61391.msg461751.html#msg461751
Title: Re: Howto send Record to thread via Pointer?
Post by: MarkMLl on December 08, 2022, 09:52:41 am
In any event, you need to interlock the data transfer so that the sender knows when the data has been accepted, and enforce some form of critical section mechanism on any part of that which can be accessed by more than one thread.

MarkMLl
Title: Re: Howto send Record to thread via Pointer?
Post by: Zvoni on December 08, 2022, 09:58:04 am
Now I got it working. But thirsrec^ in every thread is referencing to the same object (on the programstack I think) So every thread writes "100" as output. So, I should have a local copy(on the thread-stack if that exists) for every thread of r:myrec. Is that possible?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, dbugintf, LazLoggerBase, pingsend;
  10.  
  11. const
  12.   threadcount = 100;
  13.   stringlen = 10000;
  14.  
  15. type
  16.  
  17.   { TForm1 }
  18.  
  19.   TForm1 = class(TForm)
  20.     Button1: TButton;
  21.     Button2: TButton;
  22.     procedure Button1Click(Sender: TObject);
  23.     procedure Button2Click(Sender: TObject);
  24.   private
  25.     { private declarations }
  26.   public
  27.     { public declarations }
  28.   end;
  29.  
  30.   myrec = record
  31.     i : longint;
  32.     s : string;
  33.   end;
  34.  
  35.   Pmyrec = ^myrec;
  36.  
  37. var
  38.   Form1: TForm1;
  39.   finished : longint;
  40.  
  41. threadvar
  42.          thri : ptrint;
  43.  
  44. implementation
  45. {$R *.lfm}
  46.  
  47. { TForm1 }
  48.  
  49. function f(p : Pointer) : ptrint;
  50.  
  51. var
  52.   s : ansistring;
  53.   thisrec : ^myrec;
  54. begin
  55.   thisrec := Pmyrec(p);
  56.   Writeln('thread ', longint(thisrec^.i), ' started');
  57.   thri:=0;
  58.   while (thri<stringlen) do
  59.     begin
  60.     s:=s+'1';
  61.     inc(thri);
  62.     end;
  63.   Writeln('thread ',thisrec^.i,' finished with',thisrec^.s);
  64.   InterLockedIncrement(finished);
  65.   f:=0;
  66.   writeln('end of function');
  67. end;
  68.  
  69. procedure TForm1.Button1Click(Sender: TObject);
  70. begin
  71.  
  72. end;
  73.  
  74. procedure TForm1.Button2Click(Sender: TObject);
  75. var
  76.   r : myrec;
  77.   x : longint;
  78. begin
  79.    finished:=0;
  80.    r.s := ' test ';
  81.    for x :=1 to threadcount do
  82.      begin
  83.        r.i := x;
  84.        BeginThread(@f,@r);
  85.        writeln('ready for nex t');
  86.      end;
  87.    while finished<threadcount do ;
  88.    Writeln('goopend ' + IntToStr(finished));
  89. end;
  90. end.
Adding to avra and Mark, but concentrating on your initial question:
Line 53 should not be a Pointer-Type, but the Record-Type directly! Only then you would get a local copy
And Line 55 would then read
 thisrec := Pmyrec(p)^;
and later inside the f-function you wouldn't need to dereference
Title: Re: Howto send Record to thread via Pointer?
Post by: jan741 on December 08, 2022, 05:32:26 pm
I tested it, but doesn't work. Reason; you don't have control when the local assignment happens of every threadversion of thisrec. So, what you got as output are no aligned numbers less than 100 but mostly you get 100. The main-thread loop keeps running before most treads even start initializing their version of thisrec. When they start in most cases r.i = 100
My solution was working with the "new" keyword and making copy's on the heap, passing the pointer to -and disposing them in- each tread, a similar solution of mika instead he works with a stack-array, what creates less overhead.
Title: Re: Howto send Record to thread via Pointer?
Post by: MarkMLl on December 08, 2022, 07:18:13 pm
My solution was working with the "new" keyword and making copy's on the heap, passing the pointer to -and disposing them in- each tread, a similar solution of mika instead he works with a stack-array, what creates less overhead.

What makes you confident that the memory manager is thread-safe in the way you're using it?

MarkMLl
Title: Re: Howto send Record to thread via Pointer?
Post by: alpine on December 09, 2022, 12:11:16 am
Now I got it working. But thirsrec^ in every thread is referencing to the same object (on the programstack I think) So every thread writes "100" as output. So, I should have a local copy(on the thread-stack if that exists) for every thread of r:myrec. Is that possible?

Consider the following:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. const
  11.   threadcount = 100;
  12.   stringlen = 10000;
  13.  
  14. type
  15.  
  16.   { TForm1 }
  17.  
  18.   TForm1 = class(TForm)
  19.     Button1: TButton;
  20.     Button2: TButton;
  21.     procedure Button1Click(Sender: TObject);
  22.     procedure Button2Click(Sender: TObject);
  23.   private
  24.  
  25.   public
  26.  
  27.   end;
  28.  
  29.   myrec = record
  30.     i : longint;
  31.     s : string;
  32.   end;
  33.  
  34.   Pmyrec = ^myrec;
  35.  
  36. var
  37.   Form1: TForm1;
  38.   finished : longint;
  39.  
  40. threadvar
  41.   thri : ptrint;
  42.  
  43. implementation
  44.  
  45. {$R *.lfm}
  46.  
  47. uses
  48.   Windows; // for AllocConsole
  49.  
  50. type
  51.  
  52.   { TMyThread }
  53.  
  54.   TMyThread = class(TThread)
  55.   private
  56.     fr: myrec; // Private copy of myrec
  57.   public
  58.     constructor Create(var r: myrec);
  59.     procedure Execute; override;
  60.   end;
  61.  
  62. { TForm1 }
  63.  
  64. function f(p : Pointer) : ptrint;
  65. var
  66.   s : ansistring;
  67.   thisrec : ^myrec;
  68. begin
  69.   thisrec := Pmyrec(p);
  70.   Writeln('thread ', longint(thisrec^.i), ' started');
  71.   thri:=0;
  72.   while (thri<stringlen) do
  73.     begin
  74.     s:=s+'1';
  75.     inc(thri);
  76.     end;
  77.   Writeln('thread ',thisrec^.i,' finished with',thisrec^.s);
  78.   InterLockedIncrement(finished);
  79.   f:=0;
  80.   writeln('end of function');
  81. end;
  82.  
  83. { TMyThread }
  84.  
  85. constructor TMyThread.Create(var r: myrec);
  86. begin
  87.   fr := r; // Preserve into the private copy
  88.   FreeOnTerminate := True; // Free when terminated
  89.   inherited Create(False); // Create and run
  90. end;
  91.  
  92. procedure TMyThread.Execute;
  93. begin
  94.   f(@fr);  // Call f() with a pointer to the private copy
  95. end;
  96.  
  97. procedure TForm1.Button1Click(Sender: TObject);
  98. var
  99.   r : myrec;
  100.   x : longint;
  101. begin
  102.    finished:=0;
  103.    r.s := ' test ';
  104.    for x :=1 to threadcount do
  105.      begin
  106.        r.i := x;
  107.        BeginThread(@f,@r);
  108.        writeln('ready for nex t');
  109.      end;
  110.    while finished<threadcount do ;
  111.    Writeln('goopend ' + IntToStr(finished));
  112. end;
  113.  
  114. procedure TForm1.Button2Click(Sender: TObject);
  115. var
  116.   r : myrec;
  117.   x : longint;
  118. begin
  119.    finished:=0;
  120.    r.s := ' test ';
  121.    for x :=1 to threadcount do
  122.      begin
  123.        r.i := x;
  124.        TMyThread.Create(r);
  125.        writeln('ready for nex t');
  126.      end;
  127.    while finished<threadcount do ;
  128.    Writeln('goopend ' + IntToStr(finished));
  129.  
  130. end;
  131.  
  132. // Following lines are to make WriteLn work on Windows GUI App
  133. initialization
  134.   // Allocate console in Windows
  135.   AllocConsole;      // in Windows unit
  136.   IsConsole:=True;   // in System unit
  137.   SysInitStdIO;      // in System unit
  138. end.
  139.  

Here TMyThread constructor is used to make a private copy of the myrec before actually starting the thread (line 89). Then Execute calls your f() function with a pointer to the private copy (line 94). TMyThread instances were automatically freed because of line 88.
Title: Re: Howto send Record to thread via Pointer?
Post by: alpine on December 09, 2022, 12:16:29 pm
@jan741
The post is in the "Beginners" section, so I'm assuming you're coming with a different background (Python was mentioned). As multi-threading is known to be a bit of a tricky issue, if you have any questions about FPC MT support (including TThread), ask them and there are enough supportive members to answer you.

I suspect there are some misunderstandings about FPC memory/variable allocation, too.
Title: Re: Howto send Record to thread via Pointer?
Post by: MarkMLl on December 09, 2022, 12:25:40 pm
As multi-threading is known to be a bit of a tricky issue, if you have any questions about FPC MT support (including TThread), ask them and there are enough supportive members to answer you.

I suspect there are some misunderstandings about FPC memory/variable allocation, too.

Yes, agreed. I remember though discussing threaded memory allocation on the mailing list with Jonas, and from memory the gist was that the RTL tries to simulate per-thread heaps... but that there were potential problems if trying to do too much clever cross-thread stuff.

MarkMLl
Title: Re: Howto send Record to thread via Pointer?
Post by: jan741 on December 09, 2022, 05:15:03 pm
Ok thanks
Title: Re: Howto send Record to thread via Pointer?
Post by: jan741 on December 10, 2022, 11:27:44 pm
Initialization of thread fails. See code for more details. Program ends in endless loop(assembly code not concerning of code bellow, but compiler made stuff)

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
  9.   StdCtrls, dbugintf, LazLoggerBase, pingsend;
  10.  
  11. const
  12.   threadcount = 50;
  13.   stringlen = 10000;
  14.  
  15. type
  16.  
  17.   { TForm1 }
  18.  
  19.   TForm1 = class(TForm)
  20.     Button1: TButton;
  21.     Button2: TButton;
  22.     procedure Button1Click(Sender: TObject);
  23.     procedure Button2Click(Sender: TObject);
  24.   private
  25.     { private declarations }
  26.     procedure deproc(input_: PtrInt);
  27.   public
  28.     { public declarations }
  29.   end;
  30.  
  31.   myrec = record
  32.     i : longint;
  33.     s : string;
  34.   end;
  35.  
  36.  TThread1 = class(TThread)
  37.  public
  38.    procedure Execute; override;        
  39.    constructor create(inp:myrec);    
  40.  private
  41.    inv:myrec;
  42.  end;
  43.  
  44.  Pmyrec = ^myrec;
  45.  ps = ^ansistring;
  46. var
  47.   Form1: TForm1;
  48.   Thread1:TThread1;
  49.   finished : longint;
  50.   y : longint;
  51.   nr : integer;
  52. threadvar
  53.   thri : ptrint;
  54.  
  55. implementation
  56. {$R *.lfm}
  57.  
  58.  
  59. function f(p : Pointer) : ptrint;
  60.  
  61. var
  62.   s : ansistring;
  63.   pta:ps;
  64.   thisrec : myrec;
  65. begin
  66.   thisrec := Pmyrec(p)^;
  67.   Writeln('thread ', thisrec.i, ' started');
  68.   thri:=0;
  69.   new(pta);
  70.   pta^ :='zodiac';
  71.   //sleep(100);
  72.   while (thri<stringlen) do
  73.     begin
  74.  
  75.     inc(thri);
  76.     //if (thisrec.i = 1) then Application.QueueAsyncCall(@Form1.deproc, PtrInt(pta));
  77.  
  78.     end;
  79.   Writeln('thread ',thisrec.i,' finished with',thisrec.s);
  80.   InterLockedIncrement(finished);
  81.   f:=0;
  82.  
  83. end;
  84.  
  85. { TForm1 }
  86. procedure TForm1.Button1Click(Sender: TObject);
  87. begin
  88.   y:= 1;
  89. end;
  90.  
  91. procedure TForm1.Button2Click(Sender: TObject);
  92. var
  93.   r: myrec;
  94.   pr: ^myrec;
  95.   x : longint;
  96. begin
  97.    finished:=0;
  98.    r.s := ' test ';
  99.    for x :=1 to 2 do
  100.      begin
  101.        r.i:=x;
  102.        //BeginThread(@f,@r[x]);
  103.        Thread1.create(r);                    // <<< after loop, program hungs
  104.      end;
  105.    while finished<threadcount do ;
  106. end;
  107.  
  108. procedure TForm1.deproc(input_:PtrInt);
  109. var
  110.   s: ansistring;
  111.  
  112. begin
  113.    s := ps(input_)^;
  114.    write(s);
  115.    dispose(ps(input_));
  116. end;
  117.  
  118. { Thread1 }
  119.  
  120. constructor TThread1.create(inp:myrec);     // <<----fails: in assembly, code between begin and end is never executed(when opening assembler-window and step into.
  121. begin
  122.    inv := inp;
  123.    writeln('init tread', inv.i);
  124.    inherited Create(False);  
  125.    writeln('ready for next');
  126. end;
  127.  
  128. procedure TThread1.Execute;
  129. begin
  130.    f(@inv);
  131. end;
  132.  
  133. end.
Title: Re: Howto send Record to thread via Pointer?
Post by: alpine on December 11, 2022, 12:07:05 am
Line 103 should be:
Code: Pascal  [Select][+][-]
  1.   TThread1.create(r);
Note the additional T in front. Without it you just call the create() with Self equal to the variable Thread1, which is Nil since all global variables are initialized with zeros at start.

Instances of a given class are created by calling a constructor prefixed by the class name (TThread1 in this case). The full statement is:
Code: Pascal  [Select][+][-]
  1.   Thread1 := TThread1.create(r);
but since in my example the instances were automatically freed, I don't assign them to a variable.

Title: Re: Howto send Record to thread via Pointer?
Post by: jan741 on December 14, 2022, 05:06:54 pm
What is the difference between

TMyThread.Create(r);

and using the new keyword? Pascal classes are made in the heap, I suppose, so after .create(r) you get a reference, I think to the initiated object on the heap, correct?
But when do you use the new keyword? And can you use it in this specific case? In C++ the constructor is called when you initiate with the new keyword not with calling the constructor directly.
A bit confusing.  %)

Thx
Title: Re: Howto send Record to thread via Pointer?
Post by: alpine on December 14, 2022, 07:32:47 pm
What is the difference between

TMyThread.Create(r);

and using the new keyword? Pascal classes are made in the heap, I suppose, so after .create(r) you get a reference, I think to the initiated object on the heap, correct?
Consider the following code:
Code: Pascal  [Select][+][-]
  1. type
  2.   TMyClass = class(TObject)
  3.   public
  4.     constructor Create;
  5.   end;
  6.  
  7. constructor TMyClass.Create;
  8. begin
  9.   inherited Create;
  10. end;
  11.  
  12. var
  13.   O: TMyClass;
  14. begin
  15.   O := TMyClass.Create;
  16.   O := O.Create;
  17. end.
  18.  

The line 15 (constructor with the class at the front) is compiled as:
Code: ASM  [Select][+][-]
  1. # [22] O := TMyClass.Create;
  2.         movl    $1,%edx
  3.         movl    $VMT_$P$UNIT1_$$_TMYCLASS,%eax
  4.         call    P$UNIT1$_$TMYCLASS_$__$$_CREATE$$TMYCLASS
  5.         movl    %eax,U_$P$UNIT1_$$_O
The edx is loaded with 1 and the eax with the VMT pointer. The thing is that when edx is preloaded with 1, then the generated Create calls the NewInstance entry from the given VMT, usually TObject.NewInstance unless overriden. See lines 15,16:
Code: ASM  [Select][+][-]
  1. P$PROGRAM$_$TMYCLASS_$__$$_CREATE$$TMYCLASS:
  2. # [unit1.pas]
  3. # [8] begin
  4.         pushl   %ebp
  5.         movl    %esp,%ebp
  6.         leal    -12(%esp),%esp
  7.         pushl   %ebx
  8.         pushl   %esi
  9.         pushl   %edi
  10. # Var $vmt located at ebp-4, size=OS_32
  11. # Var $self located at ebp-8, size=OS_32
  12. # Var $vmt_afterconstruction_local located at ebp-12, size=OS_S32
  13.         movl    %eax,-8(%ebp)
  14.         movl    %edx,-4(%ebp)
  15.         cmpl    $1,%edx
  16.         jne     .Lj6
  17.         movl    -8(%ebp),%eax
  18.         movl    -8(%ebp),%edx
  19.         call    *52(%edx) ;<---------------------- NewInstance into VMT
  20.         movl    %eax,-8(%ebp)
  21.         .balign 4,0x90
  22. .Lj6:
  23.         cmpl    $0,-8(%ebp)
  24.  

NewInstance allocates needed memory from heap via GetMem, calls InitInstance, then returns it into eax register (which is actually the Self).

At the other hand, if called with a  variable instead of a class in the front (line 16):
Code: ASM  [Select][+][-]
  1.         movl    %eax,U_$P$PROGRAM_$$_O
  2. # [16] O := O.Create;
  3.         movl    $-1,%edx
  4.         call    P$PROGRAM$_$TMYCLASS_$__$$_CREATE$$TMYCLASS
  5.         movl    %eax,U_$P$PROGRAM_$$_O
  6. # [17] end.
  7.  
Here the edx is preloaded with -1 (not 1) and the eax with the value of the variable O, i.e. the Self implicit argument. With the edx not equal to 1, the call to NewInstance gets skipped, assuming eax is already pointing to the instance memory.

That way:


The former is the usual way the instances were created in Object Pascal, the latter (or variation of it) can be used if you want some special allocation scheme, say to reuse an instance once allocated and to skip FreeMem/GetMem pair.

But when do you use the new keyword? And can you use it in this specific case? In C++ the constructor is called when you initiate with the new keyword not with calling the constructor directly.
A bit confusing.  %)

The C++ objects are constructed implicitly and if you want them on the heap you must use the new/delete operators. In contrast the FPC objects are actually just references and if you want them constructed you must do that explicitly.

In FPC new/dispose aren't used for that purpose. They're remnants from the original Pascal and not very much used nowadays.
Title: Re: Howto send Record to thread via Pointer?
Post by: jan741 on December 15, 2022, 10:49:30 am
Ok thanks.

How did you get the assembly code? I would also like to see the memory-layout on given address. I tried to use windbg, but can't connect because the debugger within lazarus is already connected to the process.

Thanks
Title: Re: Howto send Record to thread via Pointer?
Post by: alpine on December 15, 2022, 11:07:11 am
Ok thanks.

How did you get the assembly code? I would also like to see the memory-layout on given address. I tried to use windbg, but can't connect because the debugger within lazarus is already connected to the process.

Thanks
Project Options --> Compiler Options --> Custom Options --> press the button [All options ...] --> check -al option.
Then generated *.s file(s) will be left into the output directory.

For the memory dump at address - I haven't found a feasible way yet. Will be happy to know.
Title: Re: Howto send Record to thread via Pointer?
Post by: MarkMLl on December 15, 2022, 01:54:01 pm
I pointed out several days ago that doing this has to be approached carefully, using critical sections etc.  I've also said that I'm extremely dubious about trusting the heap manager for this, it's really not what it's designed for.

If there's one producer and multiple consumers, then either some sort of reference counting needs to be done, or each consumer needs a separate buffer.

Attached is something I've been tinkering with over the last few days which might serve as a useful example: a thread-safe extensible circular buffer. While this is byte-based, it is trivially extensible to any fixed-size data structure and could probably be redone using generics.

MarkMLl
TinyPortal © 2005-2018