Recent

Author Topic: Thread Question  (Read 577 times)

superc

  • New member
  • *
  • Posts: 15
Thread Question
« on: November 13, 2018, 12:14:37 pm »
Goog morning,
I've a doubt about thread in lazarus: I create a simple thread for blinking a tlabel in my main form:
Code: Pascal  [Select]
  1.   TBlinker = class(TThread)
  2.   private
  3.     ftLabel: TLabel;
  4.     procedure setComponentLabel(AValue: TLabel);
  5.  
  6.   protected
  7.      procedure Execute; override;
  8.      procedure reverseBlink;
  9.   public
  10.      ctrVar: Boolean;
  11.      threadRun: Boolean;
  12.      constructor Create(CreateSuspended: boolean);
  13.      property setLabel: TLabel read ftLabel write setComponentLabel;
  14.      procedure setVisibilityOff;
  15.   end;    
  16.  
  17. procedure TBlinker.setComponentLabel(AValue: TLabel);
  18. begin
  19.   if ftLabel=AValue then Exit;
  20.   ftLabel:=AValue;
  21. end;
  22.  
  23. procedure TBlinker.Execute;
  24. var
  25.   i: Integer;
  26. begin
  27.   while not Terminated and (ctrVar = true) do
  28.   begin
  29.     Synchronize(@reverseBlink);
  30.     Sleep(200);
  31.   end;
  32.   setVisibilityOff;
  33.   threadRun := false;
  34. end;
  35.  
  36. procedure TBlinker.reverseBlink;
  37. begin
  38.   ftLabel.Visible:= not ftLabel.Visible;
  39. end;
  40.  
  41. constructor TBlinker.Create(CreateSuspended: boolean);
  42. begin
  43.   FreeOnTerminate := True;
  44.  
  45.   threadRun := true;
  46.  
  47.   inherited Create(CreateSuspended);
  48.     ctrVar:=true;
  49.  
  50. end;
  51.  
  52. procedure TBlinker.setVisibilityOff;
  53. begin
  54.   ftLabel.Visible:= False;
  55. end;    
  56.  

Well, now i create two variable on TForm1 as
Code: Pascal  [Select]
  1.     bp: TBlinker; pnt_bp : ^TBlinker;    

and two Metohd for start and stop thread:
Code: Pascal  [Select]
  1. function TForm1.createBlinker: Boolean;
  2. //var
  3. //  bp : TBlinker;
  4. begin
  5.   try
  6.     bp := TBlinker.Create(true);
  7.     bp.setComponentLabel(Label1);
  8.     bp.FreeOnTerminate:= true;
  9.     bp.Start;
  10.     pnt_bp := Addr(bp);
  11.   finally
  12.   end;
  13. end;  
  14.  
  15.  
  16. function TForm1.stopBlinker: Boolean;
  17. begin
  18.   try
  19.     if (bp=nil) then
  20.       Exit;
  21.  
  22.     bp.ctrVar:=false;
  23.     bp.Terminate;
  24.     bp := nil;
  25.   except
  26.     on e: Exception do
  27.       ShowMessage(e.Message);
  28.   end;
  29. end;  
  30.  
And works fine, but;

if I set a local variable instead a public variable bp and I add the code ( as in the comment)

Code: Pascal  [Select]
  1.      
  2. pnt_bp := Addr(bp);  
  3.  
on createBlinker

and i stop Thread with command   

Code: Pascal  [Select]
  1. TBlinker(pnt_bp^).ctrVar:= false;

I receive SIGSEV error:
why my pointer don't point correctly?







Pascal

  • Hero Member
  • *****
  • Posts: 783
Re: Thread Question
« Reply #1 on: November 13, 2018, 12:28:17 pm »
Addr() delivers the address of the local bp which gets destroyed at the end of createBlinker();
laz trunk - fpc trunk 32bit - Windows 10 Pro x64 (1803)

superc

  • New member
  • *
  • Posts: 15
Re: Thread Question
« Reply #2 on: November 13, 2018, 12:34:42 pm »
excuse me but I don't understand:

  address of local variable bp is pointed from pnt_bp until end of thread, right?



Pascal

  • Hero Member
  • *****
  • Posts: 783
Re: Thread Question
« Reply #3 on: November 13, 2018, 12:47:01 pm »
Code: Pascal  [Select]
  1. function TForm1.createBlinker: Boolean;
  2. var
  3.   bp : TBlinker;
  4. begin
  5.   // bp (the space to store the pointer to the TBlinker) is created here (on the stack)
  6.   try
  7.     bp := TBlinker.Create(true);
  8.     bp.setComponentLabel(Label1);
  9.     bp.FreeOnTerminate:= true;
  10.     bp.Start;
  11.     pnt_bp := Addr(bp); // pnt_bp points to the storage (bp) which points to the just created TBlinker instance and not to the instance of TBlinker directly
  12.   finally
  13.   end;
  14.   // bp (the space to store the pointer to the TBlinker) is destroyed here so pnt_bp points to something invalid from now on
  15. end;  

You should do:
Code: Pascal  [Select]
  1.     global_bp := bp;  // instread of pnt_bp := Addr(bp);
where global_bp is TBlinker.
« Last Edit: November 13, 2018, 01:11:44 pm by Pascal »
laz trunk - fpc trunk 32bit - Windows 10 Pro x64 (1803)

HeavyUser

  • Full Member
  • ***
  • Posts: 206
Re: Thread Question
« Reply #4 on: November 13, 2018, 12:53:22 pm »
excuse me but I don't understand:

  address of local variable bp is pointed from pnt_bp until end of thread, right?
You can not use local variables outside the procedure/method that defined them. Local variables are on the stack not on the heap. They get created when the method/procedure is called and destroyed when the method/procedure code finishes and returned to the caller and each call has its own space in the stack.

To summarize your pointer points correctly to a released address which is not accessible from your code any longer.

Keep in mind that classes in pascal are equivalent to pointer to objects in C eg
Code: Pascal  [Select]
  1.   bp: TBlinker;
  2.   pnt_bp : ^TBlinker;    
  3.  
will be in C
Code: C  [Select]
  1.   Tblinker* bp;
  2.   TBlinker** pnt_Bp;
  3.  
that in short means you save a pointer to the local variable instead of the address of the class in the heap and that (as pascaldragon said) is invalid after the method finished executing.

superc

  • New member
  • *
  • Posts: 15
Re: Thread Question
« Reply #5 on: November 13, 2018, 02:44:00 pm »
ok now it's clear, thanks.