Recent

Author Topic: Multithreaded Application  (Read 962 times)

systemgvp

  • New Member
  • *
  • Posts: 28
Multithreaded Application
« on: June 28, 2019, 04:58:28 pm »
I created an application that opens a text file and uses a timer, every second, reads a couple of lines and sends them to the Server using another Thread.

The problem is that after 15 or 30 seconds the application stops working with the error "External: SIGSEGV".

What am I doing wrong?

Code: Pascal  [Select]
  1. unit MenuGE;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
  9.   //per la comunicazione col semaforo
  10.   fphttpclient, unix, process;
  11.  
  12. type
  13.  
  14.   { TMenuGEN }
  15.  
  16.   TMenuGEN = class(TForm)
  17.     AvviaSimulatore_BTN: TButton;
  18.     ApriTraiettorie_BTN: TButton;
  19.     FermaSimulatore_BTN: TButton;
  20.     Memo1: TMemo;
  21.     Panel1: TPanel;
  22.     Risultati_TXT: TMemo;
  23.     DialogoApri: TOpenDialog;
  24.     Timer1: TTimer;
  25.     procedure ApriTraiettorie_BTNClick(Sender: TObject);
  26.     procedure AvviaSimulatore_BTNClick(Sender: TObject);
  27.     procedure FermaSimulatore_BTNClick(Sender: TObject);
  28.     procedure Timer1Timer(Sender: TObject);
  29.   private
  30.  
  31.   public
  32.  
  33.   end;
  34.  
  35.   Type
  36.     TInvioPosizioni = class(TThread)
  37.       utente, latitudine, longitudine, direzione, velocita, istanteGNSS : string;
  38.     private
  39.       fStatusText : string;
  40.       procedure ShowStatus;
  41.     protected
  42.       procedure Execute; override;
  43.     public
  44.       Constructor Create(CreateSuspended : boolean);
  45.     end;
  46.  
  47. var
  48.   MenuGEN: TMenuGEN;
  49.  
  50.   riga : integer;
  51.   istante : string;
  52.   FileTraiettorie : TStringList;
  53.  
  54. implementation
  55.  
  56. {$R *.lfm}
  57.  
  58. { TMenuGEN }
  59.  
  60.   constructor TInvioPosizioni.Create(CreateSuspended : boolean);
  61.   begin
  62.     inherited Create(CreateSuspended);
  63.     FreeOnTerminate := True;
  64.   end;
  65.   procedure TInvioPosizioni.ShowStatus;
  66.   // this method is executed by the mainthread and can therefore access all GUI elements.
  67.   begin
  68.     //Form1.Caption := fStatusText;
  69.   end;
  70.   procedure TInvioPosizioni.Execute;
  71.   var FPHTTPClient: TFPHTTPClient; Parametri : TStringList; Risposta, sito: String;
  72.   begin
  73.  
  74.     MenuGEN.Risultati_TXT.Lines.Append('Server');
  75.  
  76.     Free;
  77.   end;
  78.  
  79. procedure TMenuGEN.ApriTraiettorie_BTNClick(Sender: TObject);
  80. begin
  81.   if DialogoApri.Execute then
  82.   begin
  83.     //crea il contenitore
  84.     FileTraiettorie := TStringList.Create;
  85.     //carica il file
  86.     FileTraiettorie.Clear;
  87.     FileTraiettorie.LoadFromFile(DialogoApri.FileName);
  88.   end;
  89. end;
  90.  
  91. procedure TMenuGEN.AvviaSimulatore_BTNClick(Sender: TObject);
  92. begin
  93.   Risultati_TXT.Lines.Clear;
  94.   //azzera il tempo
  95.   istante := '0';
  96.   riga    := 3;
  97.   //avvia il tempo
  98.   Timer1.Enabled := true;
  99. end;
  100.  
  101. procedure TMenuGEN.FermaSimulatore_BTNClick(Sender: TObject);
  102. begin
  103.   Timer1.Enabled := false;
  104. end;
  105.  
  106. procedure TMenuGEN.Timer1Timer(Sender: TObject);
  107. var i : integer; elementi : TStringList; InvioPosizioni : TInvioPosizioni;
  108.     //utente, latitudine, longitudine, direzione, velocita, istanteGNSS : string;
  109. begin
  110.   Risultati_TXT.Lines.Append('istante: '+istante+#9+' Tempo: '+TimeToStr(Time));
  111.   elementi := TStringList.Create;
  112.   //gira il file delle traiettorie
  113.   for i := riga to FileTraiettorie.Count -1 do
  114.   begin
  115.     //scompone la riga
  116.     elementi.Clear;
  117.     elementi.Delimiter       := #9;
  118.     elementi.StrictDelimiter := True; //risolve i problemi con il carattere spazio
  119.     elementi.DelimitedText   := FileTraiettorie[i];
  120.  
  121.     //se l'istante e' uguale
  122.     if (elementi[0].Equals(istante)) then
  123.     begin
  124.       //recupera i dati
  125.       InvioPosizioni := TInvioPosizioni.Create(True); // This way it doesn't start automatically
  126.       InvioPosizioni.utente      := IntToStr(elementi[1].ToInteger + 1);
  127.       InvioPosizioni.latitudine  := elementi[10];
  128.       InvioPosizioni.longitudine := elementi[9];
  129.       InvioPosizioni.direzione   := elementi[12];
  130.       InvioPosizioni.velocita    := elementi[4];
  131.       InvioPosizioni.istanteGNSS := elementi[0];
  132.  
  133.  
  134.       memo1.Lines.Append(IntToStr(elementi[1].ToInteger + 1)+'|'+elementi[10]+'|'+elementi[9]+'|'+elementi[12]+'|'+elementi[4]+'|'+elementi[0]);
  135.  
  136.  
  137.       InvioPosizioni.Start;
  138.     end
  139.     //termina il ciclo
  140.     else
  141.     begin
  142.       //aggiorna i riferimenti
  143.       istante := elementi[0];
  144.       riga    := i;
  145.       //ferma la ricerca
  146.       break;
  147.     end;
  148.  
  149.   end;
  150.   Risultati_TXT.Lines.Append('FINE funzione');
  151. end;
  152.  
  153. end.
  154.  

ASerge

  • Hero Member
  • *****
  • Posts: 1423
Re: Multithreaded Application
« Reply #1 on: June 28, 2019, 06:21:28 pm »
Method "TInvioPosizioni.Execute" accesses GUI elements directly, this can cause problems.

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: Multithreaded Application
« Reply #2 on: June 28, 2019, 07:02:00 pm »
And "procedure TInvioPosizioni.Execute;" frees the thread.
And the thread is created with "FreeOnTerminate := True;"
That is like probably freeing the thread twice.

Edit:
Freeing the thread inside Execute. Nice one.
« Last Edit: June 28, 2019, 07:08:14 pm by engkin »

avra

  • Hero Member
  • *****
  • Posts: 1739
    • Additional info
Re: Multithreaded Application
« Reply #3 on: June 28, 2019, 09:52:00 pm »
application stops working with the error "External: SIGSEGV"
There are several ways to update GUI elements from the thread in a safe way. QueueAsyncCall() is one of them. Here is a nice example how to safely transfer a whole record to the main thread with it:
https://forum.lazarus.freepascal.org/index.php/topic,38851.msg265181.html#msg265181

Good thing is that QueueAsyncCall() is non-blocking and it does very useful serialization so there is no need for syncing at all. I used it when adding TMemoChannel to MultiLog where many threads could update memo on a form and also all write to the same log file without any problem.
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

Thaddy

  • Hero Member
  • *****
  • Posts: 9309
Re: Multithreaded Application
« Reply #4 on: June 29, 2019, 07:28:53 am »
@avra
Example for QueueAsyncCall  there is neat, but you should have used PtrUint or UintPtr and not PtrInt. Pointers are never negative!
also related to equus asinus.

totya

  • Hero Member
  • *****
  • Posts: 577
Re: Multithreaded Application
« Reply #5 on: June 29, 2019, 11:20:17 am »
@avra
Example for QueueAsyncCall  there is neat, but you should have used PtrUint or UintPtr and not PtrInt. Pointers are never negative!

Hi, NativeUInt sounds me better, because from the built in help:
Quote
NativeUInt is defined for Delphi compatibility. It is an unsigned integer with the size of a pointer, so 32-bit on 32-bit platforms, 64-bit on 64-bit platforms.

While the PtrUint built in  help:
Quote
Declaration
Source position: systemh.inc line 351
type PtrUInt = DWord;
But this info isn't correct, because in systemh.inc certainly:

Code: Pascal  [Select]
  1. {$ifdef CPU64}
  2. ...
  3.   PtrInt = Int64;
  4.   PtrUInt = QWord;
  5. ...
  6. {$endif CPU64}
  7.  

So, you are right, but the Lazarus built in help needs to update. (Otherwise the QWord is an  interesting type, sometimes same as Int64 which is signed type. I hope this type works always unsigned as pointer.).

Edit: the wiki doesn't work as usual, so I can see only the Lazarus built-in help.
« Last Edit: June 29, 2019, 11:29:57 am by totya »

Thaddy

  • Hero Member
  • *****
  • Posts: 9309
Re: Multithreaded Application
« Reply #6 on: June 29, 2019, 11:28:27 am »
Yes the unsigned types work over all platforms and with the correct size.
AFAIK all types - signed or unsigned - are declared or aliased correctlyfor all platforms  since 3.0.4 and also the Delphi compatibility aliases.
« Last Edit: June 29, 2019, 11:37:03 am by Thaddy »
also related to equus asinus.

avra

  • Hero Member
  • *****
  • Posts: 1739
    • Additional info
Re: Multithreaded Application
« Reply #7 on: June 29, 2019, 03:04:37 pm »
Example for QueueAsyncCall  there is neat, but you should have used PtrUint or UintPtr and not PtrInt. Pointers are never negative!
Thanks Thaddy for the review. You are fully right. Linked example is corrected now.

NativeUInt sounds me better
Although NativeUInt is just an alias for PtrUInt, it does sound a little better.
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

witenite

  • New Member
  • *
  • Posts: 41
Re: Multithreaded Application
« Reply #8 on: November 17, 2019, 05:00:49 am »
Example for QueueAsyncCall  there is neat, but you should have used PtrUint or UintPtr and not PtrInt. Pointers are never negative!
Thanks Thaddy for the review. You are fully right. Linked example is corrected now.

NativeUInt sounds me better
Although NativeUInt is just an alias for PtrUInt, it does sound a little better.

Thanks for the confirmation on PtrInt vs PtrUint. I reviewed the necessary documentation found here a few days ago, and read this:
https://lazarus-ccr.sourceforge.io/docs/lcl/forms/tapplication.queueasynccall.html
Note it still refers to PtrInt. If you click on the PtrInt hyperlink it does tell you this was a mistake, but I wasn't sure what the impact would be for using QueueAsyncCall using anything other than PtrInt. You appear to have clarified this for me. Perhaps the mentioned documentation needs to be updated accordingly, but I'm not sure who takes care of that.

Thanks Again.

Thaddy

  • Hero Member
  • *****
  • Posts: 9309
Re: Multithreaded Application
« Reply #9 on: November 17, 2019, 09:32:05 am »
Enter a bug report against documentation and Michael can Canneyt will take care of it. (usually very quickly)
also related to equus asinus.

PascalDragon

  • Hero Member
  • *****
  • Posts: 751
  • Compiler Developer
Re: Multithreaded Application
« Reply #10 on: November 17, 2019, 10:42:38 am »
@avra
Example for QueueAsyncCall  there is neat, but you should have used PtrUint or UintPtr and not PtrInt. Pointers are never negative!

Hi, NativeUInt sounds me better, because from the built in help:
Quote
NativeUInt is defined for Delphi compatibility. It is an unsigned integer with the size of a pointer, so 32-bit on 32-bit platforms, 64-bit on 64-bit platforms.

While the PtrUint built in  help:
Quote
Declaration
Source position: systemh.inc line 351
type PtrUInt = DWord;

Both PtrInt/PtrUInt and NativeInt/NativeUInt have the same meaning. FPC introduced Ptr*Int before Delphi introduced Native*Int and we simply added the latter for Delphi compatibility.

But this info isn't correct, because in systemh.inc certainly:

Code: Pascal  [Select]
  1. {$ifdef CPU64}
  2. ...
  3.   PtrInt = Int64;
  4.   PtrUInt = QWord;
  5. ...
  6. {$endif CPU64}
  7.  

That is because the documentation is generated with a specific platform setting (i386-linux AFAIK) and thus the CPU32 define is active.