Sign in Join

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

Mailing lists / fpc-pascal / 2022-04

Correct way for using TThread.ForceQueue?

8 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

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

Thomas Kurz

Hello,

my question is about using the TThread.Synchronize, Queue and ForceQueue methods. Unfortunately, the parameter they take is of type TThreadMethods which allows calling only methods having no assume this is done to simplify the implementation on client-side but causes headaches on my side :)

Delphi solves the issue with having anonymous methods which aren't yet available in FPC yet. So unfortunately I cannot do something like TThread.Synchronize(NIL, @procedure(a,b,c)).

As far as I have seen so far, the common solution is to use a variable inside the class to cache the parameters, then to use a DoCallSync procedure which takes no parameters but can use the cached values inside, and finally to call TThread.Synchronize(NIL, @DoCallSync). I have stick to this concept when using Synchronize, too.

But what is the correct way to deal with ForceQueue? I think I cannot use a global variable for this case because its value could be overwritten between the call to ForceQueue and the moment the queued method is actually being run. Do I have to use a FIFO-buffer for this purpose? And if yes, would it be safe (in the sense of getting no deadlock situations) to use a TThreadList for that?

Kind regards,
Thomas

Reply by email

Mattias Gaertner
to Thomas Kurz
Show quoted text 7 lines
[...] As far as I have seen so far, the common solution is to use a variable inside the class to cache the parameters, then to use a DoCallSync procedure which takes no parameters but can use the cached values inside, and finally to call TThread.Synchronize(NIL, @DoCallSync). I have stick to this concept when using Synchronize, too.

Or some small class:

type
  TMyData = class
    a: whatever;
    procedure Run;
  end;
MyData:=TMyData.Create;
MyData.a:=...;
TThread.Synchronize(NIL, @MyData.Run).

Free MyData when done and/or in the destructor.

[...]

Mattias

Reply by email

Thomas Kurz
to Mattias Gaertner

This is a very elegant solution but it can only be used for Synchronize, right? Because with ForceQueue I cannot know when it's done and freeing within Run (i.e. "Self.Free") would fail.

Using an advanced record would be fine, but it doesn't fit the declaration "TThreadMethod = procedure of object".

Earlier message 35 lines
----- Original Message ----- From: Mattias Gaertner via fpc-pascal To: Sent: Monday, April 25, 2022, 16:12:40 Subject: [fpc-pascal] Correct way for using TThread.ForceQueue? On Mon, 25 Apr 2022 15:47:57 +0200 Thomas Kurz via fpc-pascal wrote: >[...] > As far as I have seen so far, the common solution is to use a > variable inside the class to cache the parameters, then to use a > DoCallSync procedure which takes no parameters but can use the cached > values inside, and finally to call TThread.Synchronize(NIL, > @DoCallSync). I have stick to this concept when using Synchronize, > too. Or some small class: type TMyData = class a: whatever; procedure Run; end; MyData:=TMyData.Create; MyData.a:=...; TThread.Synchronize(NIL, @MyData.Run). Free MyData when done and/or in the destructor. >[...] Mattias

Reply by email

Michael Van Canneyt
to Thomas Kurz
Thomas Kurz wrote
This is a very elegant solution but it can only be used for Synchronize, right? Because with ForceQueue I cannot know when it's done and freeing within Run (i.e. "Self.Free") would fail.

Why would this fail according to you ?

I have used this approach on multiple occasions.

Michael.

Reply by email

Thomas Kurz
to Michael Van Canneyt

I cannot remember why, but a long time ago when learning Delphi 5 or 6, I have learned not to call the destructor from within any method of the class, only from outside. If this is no longer true nowadays (or maybe, has never been true before), I'd like to use this solution.

Just to be sure we're talking about the same thing -- you think this code should work:

type
  TMyData = class
    method: TMethodWithParameters;
    a: whatever;
    procedure Run;
  end;
 procedure TMyData.Run;
   begin
     method (a);
     Self.Free;
   end;

...

MyData := TMyData.Create;
MyData.a := ...;
MyData.method := @SomeMethodWithParameters;
TThread.Synchronize(NIL, @MyData.Run)
Earlier message 19 lines
----- Original Message ----- From: Michael Van Canneyt via fpc-pascal To: Thomas Kurz via fpc-pascal Sent: Monday, April 25, 2022, 16:58:26 Subject: [fpc-pascal] Correct way for using TThread.ForceQueue? On Mon, 25 Apr 2022, Thomas Kurz via fpc-pascal wrote: > This is a very elegant solution but it can only be used for Synchronize, > right? Because with ForceQueue I cannot know when it's done and freeing > within Run (i.e. "Self.Free") would fail. Why would this fail according to you ? I have used this approach on multiple occasions. Michael.

Reply by email

Thomas Kurz

EDIT: The code should be "TThread.ForceQueue", not "TThread.Synchronize"; I have corrected that.

I cannot remember why, but a long time ago when learning Delphi 5 or 6, I have learned not to call the destructor from within any method of the class, only from outside. If this is no longer true nowadays (or maybe, has never been true before), I'd like to use this solution.

Just to be sure we're talking about the same thing -- you think this code should work:

type
  TMyData = class
    method: TMethodWithParameters;
    a: whatever;
    procedure Run;
  end;
 procedure TMyData.Run;
   begin
     method (a);
     Self.Free;
   end;

...

MyData := TMyData.Create;
MyData.a := ...;
MyData.method := @SomeMethodWithParameters;
TThread.ForceQueue (NIL, @MyData.Run)
Earlier message 19 lines
----- Original Message ----- From: Michael Van Canneyt via fpc-pascal To: Thomas Kurz via fpc-pascal Sent: Monday, April 25, 2022, 16:58:26 Subject: [fpc-pascal] Correct way for using TThread.ForceQueue? On Mon, 25 Apr 2022, Thomas Kurz via fpc-pascal wrote: > This is a very elegant solution but it can only be used for Synchronize, > right? Because with ForceQueue I cannot know when it's done and freeing > within Run (i.e. "Self.Free") would fail. Why would this fail according to you ? I have used this approach on multiple occasions. Michael.

Reply by email

Michael Van Canneyt
to Thomas Kurz: I cannot remember why, but a long time ago when learning Delphi 5 or 6, I have learned…
Thomas Kurz wrote
I cannot remember why, but a long time ago when learning Delphi 5 or 6, I have learned not to call the destructor from within any method of the class, only from outside. If this is no longer true nowadays (or maybe, has never been true before), I'd like to use this solution.

If it is the last thing you do in the method, there is no problem.

Note that "Free" itself is a method that calls Destroy (the actual destructor), so the advice you got was strange to say the least :-)

Show quoted text 22 lines
Just to be sure we're talking about the same thing -- you think this code should work: type TMyData = class method: TMethodWithParameters; a: whatever; procedure Run; end; procedure TMyData.Run; begin method (a); Self.Free; end; ... MyData := TMyData.Create; MyData.a := ...; MyData.method := @SomeMethodWithParameters; TThread.Synchronize(NIL, @MyData.Run)

That should work.

Michael.

Reply by email

Sven Barth
to Michael Van Canneyt
Michael Van Canneyt wrote 14 lines
On Mon, 25 Apr 2022, Thomas Kurz via fpc-pascal wrote: > I cannot remember why, but a long time ago when learning Delphi 5 or > 6, I > have learned not to call the destructor from within any method of the > class, only from outside.  If this is no longer true nowadays (or maybe, > has never been true before), I'd like to use this solution. If it is the last thing you do in the method, there is no problem. Note that "Free" itself is a method that calls Destroy (the actual destructor), so the advice you got was strange to say the least :-)

To be precise: as long as you don't access Self after a Free (this includes calling virtual methods, but not non-virtual ones) this is no problem. This has been this way in both FPC and Delphi since the beginning of TObject.

Regards,
Sven

Reply by email