Recent

Author Topic: ABort a Task by GUI Button  (Read 2477 times)

Nimral

  • Jr. Member
  • **
  • Posts: 97
ABort a Task by GUI Button
« on: February 06, 2019, 01:31:18 pm »
Hi all,

please forgive me if I ask a question which could have been asked before, where the answer may be somewhere on page xxx of some doc, or which I could probably have solved myself. I am right now not allowed to spend time using FPC by order from above, seems there isn't much trust there when a product doesn't cost $$$$$ and is backed by some well known company name. I just ran into a wicked problem using some other so-called technology (web ... Javascript), using a framework from one of those well known companies, wasted a lot of time and still haven't found a satisfying solution, and wonder whether there would be a better solution in Lazarus/FPC.

The problem is, in a event driven program, to abort a long running task from the GUI. The only option I have right now (in the programming environment I am forced to use) is that I can set a global flag variable, and spread queries all over the long running task code. As soon as the code hits into one of those many queries, I can try to abort the task code. There are two problems with this: if the long running task spends time in a sleep state, it won't abort until the current sleep has reached its limit, given I put queries to my variable right after every sleep. The second problem ist that the environment I currently am forced use doesn't fire a separate task when one hits a button, but queues the button press message in some main task event loop, where it resides until the long-running task has returned. Pressing some abort button thus doesn't have any effect, since it's handling routine won't be called until the long running task returns, and so the mnagic variable cannot be set before the task has finished anyway.

Simple question: does Lazarus/FPC better, and if yes, where can I read more about it?

Thx, Armin.
Lazarus 1.8 on Windows 10/7, VMWare Workstation 12

GetMem

  • Hero Member
  • *****
  • Posts: 3506
Re: ABort a Task by GUI Button
« Reply #1 on: February 06, 2019, 01:39:21 pm »
Run the long taks in a worker thread.

Bart

  • Hero Member
  • *****
  • Posts: 3538
    • Bart en Mariska's Webstek
Re: ABort a Task by GUI Button
« Reply #2 on: February 06, 2019, 01:43:36 pm »
The task can have a callback function, and that function can indicate wether to abort the task.
The callback function will be a method of the main thread.

Bart

Nimral

  • Jr. Member
  • **
  • Posts: 97
Re: ABort a Task by GUI Button
« Reply #3 on: February 06, 2019, 04:22:07 pm »
Thank you for your responses. Your leads brought me here ...

http://wiki.lazarus.freepascal.org/Multithreaded_Application_Tutorial#Do_you_need_multi-threading.3F

The "Terminated" flag seems to be the suggested solution. Hm, doesn't this look like the clumsy "check variable as often as you can" approach?

Armin.

P.S. What I am after is something like this (don't care about syntax ...)

var MyTask : Task = NIL

...

MyTask = new Task
MyTask.Body = ( .... the long running code thing as a closure, or a reference to a function, or inserrted via inheritance and extension of a "Body" member, whatever the language suipports  ....)

...

# if it's time for the task to be fired up
myTask.Run()

# and the GUI button handler like this:

MyButton.OnClick()
    {
    if MyTask <> NIL Then MyTask.Kill()     # Kill the thing immedately, no questions asked, probably also Kill(MyTask)
    }

« Last Edit: February 06, 2019, 04:34:44 pm by Nimral »
Lazarus 1.8 on Windows 10/7, VMWare Workstation 12

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7493
Re: ABort a Task by GUI Button
« Reply #4 on: February 06, 2019, 11:08:09 pm »
Basically there are two options:

- poll variables.
- block on signals (using waitforsingle/multiple or TEvents). IOW semaphores.

Using events allows you to sleep longer and yet be quite responsive, since you can short-circuit the pauze.

Nimral

  • Jr. Member
  • **
  • Posts: 97
Re: ABort a Task by GUI Button
« Reply #5 on: February 07, 2019, 11:41:56 am »
Marcov,

thanks for your hints.


- block on signals (using waitforsingle/multiple or TEvents). IOW semaphores.


Can you guide me to a sample/tutorial showing this approach? I am really curious how the "abort code somewhere in the middle" problem is solved using it.

Btw, I still wonder why the approach I have sketched above isn't implemented in FPC, is it? It seems like the most logical approach to me, but FPC seems not to work like that, there must be reasons, I wonder what they are. Do you happen to know?

Thanks, Armin.
« Last Edit: February 07, 2019, 11:44:19 am by Nimral »
Lazarus 1.8 on Windows 10/7, VMWare Workstation 12

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: ABort a Task by GUI Button
« Reply #6 on: February 07, 2019, 08:35:35 pm »
If you are brave enough, kill the thread using:
KillThread from the RTL or TerminateThread from Windows. But make sure you understand and don't mind the consequences of calling TerminateThread.

Another possibility is to do a calculated exit using the code provided in this thread. Here you can control how to leave the long task, and have a change to free the memory and notify the main thread, if needed.

If you don't understand it, then most likely you don't need any of this.
« Last Edit: February 07, 2019, 08:42:24 pm by engkin »

Nimral

  • Jr. Member
  • **
  • Posts: 97
Re: ABort a Task by GUI Button
« Reply #7 on: February 08, 2019, 12:03:22 pm »
KillThread from the RTL or TerminateThread from Windows. But make sure you understand and don't mind the consequences of calling TerminateThread.

Ah, there is the shotgun aproach I was after :-) I'd expect problems with memory management, especially heap corruption problems when allocating objects dynamically, and probably some similar problems with allocated resources, like open file handles. As long as the thread code isn't very complex it seems possible to deal with - if the thread code requires any of those at all. Am I seeing things too simple? If I KillThread a Thread, doesn't the Thread object destructor get called, where I could do any required cleanup work easily?

Another possibility is to do a calculated exit using the code provided in this thread. Here you can control how to leave the long task, and have a change to free the memory and notify the main thread, if needed.

I am interested ... but the thread you've linked is a 4 page thing, and a sketchy browse over the posts didn't reveal anything which would help me with my specific problem. As far as I see they discuss several differrent topics related to the usage of GOTO. Can you please point my nose to a specific post where I can find the code you wanted me to look at?

Thnx!

Armin.

BTW: don't we all at some point start as non-knowing toddlers, and learn?
« Last Edit: February 08, 2019, 12:56:54 pm by Nimral »
Lazarus 1.8 on Windows 10/7, VMWare Workstation 12

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: ABort a Task by GUI Button
« Reply #8 on: February 09, 2019, 05:00:11 am »
If I KillThread a Thread, doesn't the Thread object destructor get called, where I could do any required cleanup work easily?
No, KillThread is just cross-platform function. On Windows it calls TerminateThread or whatever function assigned to the thread manager.

the thread you've linked is a 4 page thing, and a sketchy browse over the posts didn't reveal anything which would help me with my specific problem. As far as I see they discuss several differrent topics related to the usage of GOTO. Can you please point my nose to a specific post where I can find the code you wanted me to look at?
Armin, four pages are not a lot to read. It was not about GOTO. GOTO takes a label, does not accept anything else.

Here is the idea. A typical Execute looks like this:
Code: Pascal  [Select]
  1. procedure TTestThread.Execute;
  2. var
  3.   i: int64;
  4. begin
  5.   while not Terminated do
  6.   begin
  7.     { Some slow task  like}
  8.     repeat
  9.       inc(i);
  10.     until (i>=$1122334455667788) or Terminated;
  11.   end;
  12. end;

This could be replaced with:
Code: Pascal  [Select]
  1. procedure TTestThread.Execute;
  2. label
  3.   lblLoop, lblExit;
  4. var
  5.   i: int64;
  6. begin
  7.   FLoopOrExitLbl := @lblLoop;
  8.   FExitLbl := @lblExit;
  9.   i := 0;
  10.   while not Terminated do
  11.   begin
  12.     lblLoop:
  13.     inc(i);
  14.     if i<$1122334455667788 then
  15.       NewGoto(FLoopOrExitLbl)
  16.     else
  17.       Goto lblExit;
  18.   end;
  19.   lblExit:
  20. end;

When you want to finish this thread, you call TestThread.Die:
Code: Pascal  [Select]
  1. procedure TTestThread.Die;
  2. begin
  3.   FLoopOrExitLbl := lblExit;
  4. end;

You can find how to write NewGoto in the aforementioned thread. But as you can see, it does not help enhancing the code, nor prevents you from scattering the code with more unnecessary lines.