View Issue Details

IDProjectCategoryView StatusLast Update
0004117JEDI VCL00 JVCL Componentspublic2007-11-18 04:23
Reporteradit_boseAssigned Tojfudickar 
PrioritynormalSeveritymajorReproducibilityalways
Status resolvedResolutionfixed 
Product Version 
Target VersionFixed in Version 
Summary0004117: Bug in JvThread
DescriptionI've used JvThread in my application. While using it, I've found a bug in the ThreadTerminate method, that can be seen in the attached program. On calling JvThread.Terminate after ThreadExecute or ThreadExecuteAndWait, it does NOT terminate. It is Terminated by passing a boolean value (QuitThread).
Additional InformationThe thread is Terminated by passing a boolean value (QuitThread), but this is not the proper way to terminate the thread because, this sample program instantiates on a loop, what if there is no loop?
TagsNo tags attached.

Relationships

has duplicate 0004118 resolvedobones Bug in JvThread 

Activities

2007-04-29 00:01

 

ThreadBugSource.zip (361,759 bytes)

obones

2007-06-19 09:20

administrator   ~0013470

Can you elaborate a bit more on this? From the description I have a hard time understanding what the problem is.

AlexB

2007-06-20 22:36

reporter   ~0013506

To reporter (adit_bose):

You incorrectly treat action of a method 'Terminate'. This method doesn't 'stop' the thread but only sets 'Terminated' flags for all threads in internal list (like TThread.Terminate does it). Thread should check 'Terminated' and stop execution itself:

procedure TForm1.JvThread1Execute(Sender: TObject; Params: Pointer);
begin
// repeat until QuitThread = true;
 repeat
 {...}
 until JvThread1.Tetrminated; // conventional use
end;

Method TJvThread.Tetrminated scans all thread in internal list, finds current thread and returns it's 'Terminated' flag.

ad_bose

2007-06-21 21:35

reporter   ~0013509

Thx obones and AlexB for taking interest in the query.
What I want to assert, is that on calling Terminate, a thread should immediately stop running. But this does not happen, as shown in the zip file example. For example, if we close an application with running thread, it will raise an exception. To counter this, what should have happened, is that the thread should have terminated (stopped running) if we had called Terminate before closing the application, but unfortunately, even if Terminate is called on ApplicationClose, and the thread is running; the thread does not close, and raises exception.
To understand it better, do something like this :
1. Run a thread, and in its execute event, run an infinite loop.
2. In the close event of the application, call Thread.Terminate.
In step 2, the thread should have terminated and then the application should have closed normally, but this raises an exception.
The problem is, that Terminate call should have FORCE terminated the thread even if the loop inside the thread was running.
I agree with AlexB that Terminate sets the 'Terminated' flags in the internal list, but is it really what it was supposed to do ?
Please refer to details of TerminateThread in MSDN, the problem may be there ???

AlexB

2007-06-24 02:18

reporter   ~0013528

Last edited: 2007-06-24 02:40

Hi ad_bose!

> I agree that Terminate sets the 'Terminated' flags in the internal list, but is it really what it was supposed to do ? Please refer to details of TerminateThread in MSDN, the problem may be there ???
-----
'Terminate' is only name of method, it's particular action you can easily see in our _open_ source. TJvThread.Terminate has the same name as VCL TThread.Terminate and works similarly. I aree that name is not so good (in terms of MS 'TerminateThread') but (as you understand) it was not our choice.
Please see more information about use of VCL class TThread in help for D7/BCB6.

> The problem is, that Terminate call should have FORCE terminated the thread
-----
Ask Borland/CG why TThread.Terminate only sets flag :) If you know TThread has no method for true termination of thread at all (you still can do it directly via TThread.Handle but you should know what you do).

>1. Run a thread, and in its execute event, run an infinite loop.
2. In the close event of the application, call Thread.Terminate.
In step 2, the thread should have terminated and then the application should have closed normally, but this raises an exception.
-----
Brutal termination is not welcome. Gentle finishing of thread requires cooperation between main thread and background thread: main thread sets the flag and waits, background thread must finish its execution if flag is true; it's the conventional way and it's the way how VCL TThread class works.
Complete example of finising of application you can see in Examlpes\JvThread\BCB\fThread.cpp:

void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose)
{
  // set flag 'Terminated' for all threads and resume of all threads
  JvThread->Terminate();
  // wait for finish of all threads
  JvThread->WaitFor();
  // if you used JvThread->FreeOnTerminate=false
  if(JvThread->Count) JvThread->RemoveZombie();
}
------
Alex

ZENsan

2007-06-25 02:44

reporter   ~0013536

Here can be resolution like "Thread.TerminateHard" like in one component (i dont remember where) was. So this method must terminate and free Thread memory. Is this acceptable?

adit_bose

2007-06-30 21:53

reporter   ~0013544

Thx Alex and ZENsan for your time.
The problem still remains the same, despite all the explanations. However ZENsan, if you can provide me with the component you mentioned, or its source, it may be worth experimenting over it. Thx again, both of you.

ZENsan

2007-07-16 06:34

reporter   ~0013579

Why dont use TerminateThread for this extraordinal situations?
Call this method like TerminateHard.

TerminateThread(hThread, EXIT_CODE);

2007-07-16 06:43

 

Thread.zip (2,040 bytes)

ZENsan

2007-07-16 06:44

reporter   ~0013580

There is also primitive sample for Thread.

ZENsan

2007-07-16 06:51

reporter   ~0013581

Last edited: 2007-07-16 06:54

Idea: Thread.Terminate must look like something this:

...

QuitThread := True;
Time := 0;

repeat
  Time := Time + 10;
  Sleep(10);
until Terminated or Time >= TimeOut;

if not Terminated then
  if not TerminateThread(Handle, EXIT_CODE_HARD_TERMINATE) then
    raise JvThreadException.Create('Failed to terminate thread. Thread is blocked.');

...

adit_bose

2007-07-18 10:45

reporter   ~0013592

Last edited: 2007-07-18 12:12

Thx very much again ZENsan, for the idea and for the time.

To understand the problem better, I'll try to explain the theory now. Please let me know, where I’m going wrong, or if my perseverance is correct.

I've used TerminateThread already, but unfortunately that does not work. I've already mentioned it in this sequence of messages (plz search in this web page - "Please refer to details of TerminateThread in MSDN, the problem may be there ???")

Anyway, I'll now try to explain the scene, the problem, that is.

Suppose that our program is supposed to destroy a particular "target" window, identified by its window handle (obtained by, say, FindWindow). This target window can appear many times. Now, I run an infinite loop in a thread (made to execute at FormCreate) in which the code to destroy the target window is put. This code to destroy the window is continuously run in a loop. The target window can show up any number of times, and hence the presence of "infinite" loop. This thread is supposed to continuously run till the program ends (FormClose). Of course a timer can be a good substitute, but right now, we are taking thread.

What I’ve tried to do earlier is that on FormClose, I used TerminateThread, and/or all sorts of methods to close the thread (not loop, let's be specific), but in all cases it raised error (exception) after application closed, because the thread (ok, loop) did NOT terminate, despite any call to terminate the thread.

If we call "Halt" in our application at FormClose, still there is error (exception).

Now I solved it "conditionally" by using a local Boolean variable and that was made to toggle at FormClose, and hence triggered the closure of the loop in the thread. This way the thread was "made to terminate".

I'm giving the sample code of "HideMsg" thread, and then later discuss the outcome. Here's the code :

// on FormCreate, HideMsg.Execute is called
var ExitLoop : bool; //(declared in Private, and initialized in FormCreate as False)
procedure TForm1.HideMsgExecute(Sender: TObject; Params: Pointer);
var i : HWND;
begin
  repeat
  i := FindWindow(nil, PChar('SomeWindow'));
  if i <> 0 then
     SendMessage(i, WM_Close, 0, 0); // or WM_NCDESTROY
     until ExitLoop = true;
end;

Now, at FormClose, ExitLoop is set to True (which was otherwise False throughout the running of application), and hence the loop closed immediately and the thread terminates correctly.

What I want to assert is that is there NO OTHER WAY to close the THREAD (even if the loop is running) WITHOUT calling ExitLoop, that would NOT raise exception otherwise. There are different methods, that "look" as if it would "force close" the thread, like "TerminateThread", "HideMsg.Terminate" etc, but all are hopeless for this purpose.

What is the use of "TerminateThread", "HideMsg.Terminate" etc, when they can't actually terminate the thread. Note that in my example, I’ve used a loop. But looking at it in a broader spectrum, the same problem may exist in non-loop cases too.

Also, as mentioned earlier, if we "Halt" our application at FormClose, still there is error (exception). Now, let's close in more : Why does Windows not raise exception, if we close our application through Windows Task Manager. How can Windows close the loop/thread, without exception? Normally, on FormClose, it may raise exception... granted; but why calling "Halt" raises exception?
Perhaps we may not like to compare "Halt" with "EndProcess" in Windows Task Manager, as "Halt" is used as an internal application command, and Windows Task Manager being an entirely separate process, but I presume, even on contrasting them, "Halt" should have closed the THREAD without exception, as Windows Task Manager.

Now, that we are observing this bug under microscope, I hope I'm able to put the case better now.

If there is any provision to suppress the exception by any means, then it is ok; for example, using compiler directives, like {$I-} etc, else there is perhaps some "broken link" between Microsoft-Borland-Jedi.

I know, it is perhaps against "good" programming coding, but very often we are faced by such circumstances, where we are forced to employ codes for 'unseen' or 'unpredictable' events, else there would not have been "EndProcess" in Windows Task Manager.

obones

2007-10-12 07:11

administrator   ~0013923

So what's the result of this? Any actions to be taken?

AlexB

2007-10-14 22:26

reporter   ~0013962

IMO the resolution is "as designed".

The sense of the complaint is "Method Terminate doesn't terminate the thread but sets the flag only". But this is well-known behavior of the TThread and TJvThread is only container of the TThread's descendants. If you need to terminate the thread you should call Terminate (set the flag) and wait for thread completion; the class TThread initially does not give a way for forced end of the thread.

jfudickar

2007-10-15 02:06

developer   ~0013963

The only think we could do is a new procedure terminate_and_wait.

This could call terminate and then do a loop until all threads are finished.


Not the best solution, but maybe sometimes helpfull.

Greetings
Jens

AlexB

2007-10-15 05:58

reporter   ~0013968

Last edited: 2007-10-15 06:02

OK, if so then such method should look like FormCloseQuery in example for BCB (as was already described in one of my previous posts (0013528) AlexB 06-24-07 02:18 edited on: 06-24-07 02:40):

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose)
{
  JvThread1->Terminate();
  JvThread1->WaitFor();
  if(JvThread1->Count) JvThread1->RemoveZombie();
}
//---------------------------------------------------------------------------

jfudickar

2007-11-18 04:21

developer   ~0014031

Added to svn

TJvThread.TerminateWaitFor

Issue History

Date Modified Username Field Change
2007-04-29 00:01 adit_bose New Issue
2007-04-29 00:01 adit_bose File Added: ThreadBugSource.zip
2007-04-30 01:27 obones Relationship added has duplicate 0004118
2007-06-19 09:20 obones Note Added: 0013470
2007-06-19 09:20 obones Status new => feedback
2007-06-20 22:36 AlexB Note Added: 0013506
2007-06-21 21:35 ad_bose Note Added: 0013509
2007-06-24 02:18 AlexB Note Added: 0013528
2007-06-24 02:39 AlexB Note Edited: 0013528
2007-06-24 02:40 AlexB Note Edited: 0013528
2007-06-25 02:44 ZENsan Note Added: 0013536
2007-06-30 21:53 adit_bose Note Added: 0013544
2007-07-16 06:34 ZENsan Note Added: 0013579
2007-07-16 06:43 ZENsan File Added: Thread.zip
2007-07-16 06:44 ZENsan Note Added: 0013580
2007-07-16 06:51 ZENsan Note Added: 0013581
2007-07-16 06:54 ZENsan Note Edited: 0013581
2007-07-18 10:45 adit_bose Note Added: 0013592
2007-07-18 10:52 adit_bose Note Edited: 0013592
2007-07-18 10:56 adit_bose Note Edited: 0013592
2007-07-18 10:59 adit_bose Note Edited: 0013592
2007-07-18 11:06 adit_bose Note Edited: 0013592
2007-07-18 11:10 adit_bose Note Edited: 0013592
2007-07-18 11:10 adit_bose Note Edited: 0013592
2007-07-18 11:13 adit_bose Note Edited: 0013592
2007-07-18 11:16 adit_bose Note Edited: 0013592
2007-07-18 11:18 adit_bose Note Edited: 0013592
2007-07-18 11:27 adit_bose Note Edited: 0013592
2007-07-18 11:30 adit_bose Note Edited: 0013592
2007-07-18 11:36 adit_bose Note Edited: 0013592
2007-07-18 11:47 adit_bose Note Edited: 0013592
2007-07-18 11:58 adit_bose Note Edited: 0013592
2007-07-18 12:12 adit_bose Note Edited: 0013592
2007-10-12 07:11 obones Note Added: 0013923
2007-10-14 22:26 AlexB Note Added: 0013962
2007-10-15 02:06 jfudickar Note Added: 0013963
2007-10-15 05:58 AlexB Note Added: 0013968
2007-10-15 06:02 AlexB Note Edited: 0013968
2007-10-15 06:02 AlexB Note Edited: 0013968
2007-10-16 01:47 jfudickar Status feedback => assigned
2007-10-16 01:47 jfudickar Assigned To => jfudickar
2007-11-18 04:21 jfudickar Status assigned => resolved
2007-11-18 04:21 jfudickar Resolution open => fixed
2007-11-18 04:21 jfudickar Note Added: 0014031