Board index » cppbuilder » TThread & VK_ESCAPE hangs the form...

TThread & VK_ESCAPE hangs the form...


2005-12-23 03:24:03 AM
cppbuilder42
Hi,
I have a problem with a thread...
void __fastcall TAlphaThread::Execute()
{
while( !Terminated )
{
for ( BlendValue=0; BlendValue<256; BlendValue+=1 )
Synchronize( SyncAlphaBlend );
Terminate();
}
}
...when I try to *close* the blending form in the *middle* of it's
process (while still alpha-blending...):
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
if ( Key == VK_ESCAPE )
Form2->Close(); // the form which is blended
}
void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
if ( pAlphaThread )
{
pAlphaThread->Terminate();
delete pAlphaThread;
pAlphaThread = NULL;
}
}
Then the blended form (Form2) hangs (and I can say the whole OS),
but sometimes it succeeds to finish maybe 10 seconds after ESCAPE
was pressed (?!), and then everything appear to be OK...
If I add this code to the thread:
void __fastcall TAlphaThread::DoTerminate()
{
IsRunning = false;
}
void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
if ( pAlphaThread->IsRunning )
return;
// ... same as above
}
void __fastcall TAlphaThread::Execute()
{
IsRunning = true;
// ...
}
... that *almost* solved the problem. The thread could be safely breaked,
but still if I start the thread and *fast* after that press ESCAPE, the
thread is hanged again... When I say fast, I realy mean fast. I don't think
that any user can even succed that by normal operationss but that still is a
bug.
Why is that and what can I also try?
--
Best regards,
Vladimir Stefanovic
 
 

Re:TThread & VK_ESCAPE hangs the form...

Correction:
if ( ( pAlphaThread ) && ( pAlphaThread->IsRunning ) )
return;
... but not relevant for now.
--
Best regards,
Vladimir Stefanovic
 

Re:TThread & VK_ESCAPE hangs the form...

"Vladimir Stefanovic" < XXXX@XXXXX.COM >writes:
Quote
Hi,

I have a problem with a thread...

void __fastcall TAlphaThread::Execute()
{
while( !Terminated )
{
for ( BlendValue=0; BlendValue<256; BlendValue+=1 )
Synchronize( SyncAlphaBlend );
Terminate();
}
}
If SyncAlphaBlend is running in the main thread (due to Synchronize),
then the only time the GUI would be responsive is in between calls to
Synchronize, unless SyncAlphaBlend runs the event loop periodically to
avoid starvation. If that's the case however, that may be your
problem, because it can delete the object before the code running in
that object has completed.
Quote
...when I try to *close* the blending form in the *middle* of it's
process (while still alpha-blending...):

void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
if ( Key == VK_ESCAPE )
Form2->Close(); // the form which is blended
}

void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
if ( pAlphaThread )
{
pAlphaThread->Terminate();
delete pAlphaThread;
pAlphaThread = NULL;
}
}
The main form should wait for the thread to complete before deleting
it. If the thread is running a nested event loop in a Synchronize
call, it must check the Terminated property immediately after
returning from the call to Synchronize, and properly break immediately
if it's true.
Quote
... that *almost* solved the problem. The thread could be safely breaked,
but still if I start the thread and *fast* after that press ESCAPE, the
thread is hanged again... When I say fast, I realy mean fast. I don't think
that any user can even succed that by normal operationss but that still is a
bug.


Why is that and what can I also try?
I can't say with certainty what is causing your problem since your
exmaple is incomplete.
However, be sure to always start the thread suspended, to prevent
issues of the thread being spawned before the TThread object's
constructor has completed, and other similar problems. Then set the
Suspended property to "false" when you want it to start running. This
makes it impossible to run into that problem (which may not be yours,
but I simply don't know.)
But in a nutshell, if every time you call Synchronize you check the
Terminated property and immediately stop doing more calculations, your
thread should exit quickly. Then it's safe for the main thread to do
a WaitFor on the thread object that it asked to terminate.
--
Chris (TeamB);
 

{smallsort}

Re:TThread & VK_ESCAPE hangs the form...

Quote
But in a nutshell, if every time you call Synchronize you check
the Terminated property and immediately stop doing more
calculations, your thread should exit quickly.
I'll try this. It seems reasonable...
Quote
Then it's safe for the main thread to do a WaitFor on the thread
object that it asked to terminate.
This is what I never tried before. Yes, I saw WaitFor...()
pretty often in threads but I never tried that myself.
--
Best regards,
Vladimir Stefanovic
 

Re:TThread & VK_ESCAPE hangs the form...

"Vladimir Stefanovic" < XXXX@XXXXX.COM >wrote in message
Quote
I have a problem with a thread...
That is because you are not managing the thread properly.
Quote
void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
if ( pAlphaThread )
{
pAlphaThread->Terminate();
delete pAlphaThread;
pAlphaThread = NULL;
}
}
You are deadlocking the thread. The thread's destructor calls the thread's
WaitFor() method, which becomes deadlocked because the thread is waiting for
Synchronize() to exit, but Synchronize() is waiting for the form to close,
but the form is waiting for the thread to terminate.
Quote
If I add this code to the thread:
That code is completely useless, as it doesn't actually do anything
meaningful.
Quote
what can I also try?
The best thing to do is get rid of the thread altogether. Use a timer in
the main thread instead.
If you must use a thread, then do not free it in the form's OnClose event.
Delay the destruction until after the OnClose event exits. That will allow
Synchronize() to exit normally without deadlocking everything. For example
(untested):
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
if( (Key == VK_ESCAPE) && (Form2) )
Form2->Close();
}
void __fastcall TForm1::Edit1Change(TObject *Sender)
{
if( !Form2 )
Form2 = new TForm2(this);
if( !Form2->Visible )
{
Form2->PositionAndShow();
Edit1->SetFocus();
}
Form2->Caption = Edit1->Text;
}
class TAlphaThread : public TThread
{
private:
void __fastcall SyncAlphaBlend();
int BlendValue;
TForm *FormToBlend;
protected:
void __fastcall Execute();
public:
__fastcall TAlphaThread(TForm *Form);
};
__fastcall TAlphaThread::TAlphaThread(TForm *Form)
: TThread(true)
{
FreeOnTerminate = true;
FormToBlend = Form;
FormToBlend->AlphaBlend = true;
FormToBlend->AlphaBlendValue = 0;
}
void __fastcall TAlphaThread::SyncAlphaBlend()
{
FormToBlend->AlphaBlendValue = BlendValue;
FormToBlend->Update();
}
void __fastcall TAlphaThread::Execute()
{
while( !Terminated )
{
for(BlendValue = 0; BlendValue < 256; ++BlendValue)
Synchronize(SyncAlphaBlend);
}
}
class TForm2 : public TForm
{
private:
TAlphaThread *pAlphaThread;
void __fastcall AlphaBlendTerminated(TObject *Sender);
//...
public:
__fastcall TForm2(TComponent *Owner);
__fastcall ~TForm2();
//...
void __fastcall PositionAndShow();
};
__fastcall TForm2::TForm2(TComponent *Owner)
: TForm(Owner)
{
}
__fastcall TForm2::~TForm2()
{
if( pAlphaThread )
{
pAlphaThread->OnTerminate = NULL;
pAlphaThread->Terminate();
}
}
void __fastcall TForm2::AlphaBlendTerminated(TObject *Sender)
{
pAlphaThread = NULL;
}
void __fastcall TForm2::PositionAndShow()
{
Left = Form1->Left;
Width = Form1->Width;
Top = Form1->Top + Form1->Height + 32;
Height = Form1->Height;
Show();
}
void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
if( pAlphaThread )
pAlphaiThread->Terminate();
}
void __fastcall TForm2::FormShow(TObject *Sender)
{
if( pAlphaThread )
{
pAlphaThread->OnTerminate = NULL;
pAlphaThread->Terminate();
}
pAlphaThread = new TAlphaThread(this);
pAlphaThread->OnTerminate = AlphaThreadTerminated;
pAlphaThread->Resume();
}
Gambit
 

Re:TThread & VK_ESCAPE hangs the form...

Quote
The thread's destructor calls the thread's
WaitFor() method
Really? I always call WaitFor explicitally (BCB5)...
pThread->Terminate();
pThread->WaitFor();
delete pThread
Are you saying I could avoid that call or is it related to
BCB6+?
Thanks,
Steve.
 

Re:TThread & VK_ESCAPE hangs the form...

"Steve Aletto" < XXXX@XXXXX.COM >wrote in message
Quote
Really?
Yes, really:
destructor TThread.Destroy;
begin
if not FFinished and not Suspended then
begin
Terminate;
WaitFor; // <-- here
end;
if FHandle <>0 then CloseHandle(FHandle);
inherited Destroy;
RemoveThread;
end;
Quote
Are you saying I could avoid that call
I do not recommend it. There are potential problems with letting TThread's
destructor call WaitFor(). It is always best to call it yourself.
Quote
or is it related to BCB6+?
TThread's destructor has always called WaitFor() in every version of BCB.
Gambit
 

Re:TThread & VK_ESCAPE hangs the form...

Quote
You are deadlocking the thread. The thread's destructor calls
the thread's WaitFor() method, which becomes deadlocked
because the thread is waiting for Synchronize() to exit, but
Synchronize() is waiting for the form to close, but the form is
waiting for the thread to terminate.
This explanes everything.
I haven't tried the new thread code, but I will as soon as possible...
Thank you.
--
Best regards,
Vladimir Stefanovic
 

Re:TThread & VK_ESCAPE hangs the form...

Quote
If you must use a thread, then do not free it in the form's OnClose event.
Delay the destruction until after the OnClose event exits. That will
allow
Synchronize() to exit normally without deadlocking everything. For
example
(untested):
[code]
Works perfect, without deadlocks!!!
Thank you.
--
Best regards,
Vladimir Stefanovic
 

Re:TThread & VK_ESCAPE hangs the form...

Just one more Q:
Is there any memory leak in the shown code, because after:
pAlphaThread = new TAlphaThread(this);
I never see:
delete pAlphaThread;
The CG reports nothing. Is that because '(this)'?
--
Best regards,
Vladimir Stefanovic
 

Re:TThread & VK_ESCAPE hangs the form...

At 21:31:53, 25.12.2005, Vladimir Stefanovic wrote:
Quote
Just one more Q:

Is there any memory leak in the shown code, because after:

pAlphaThread = new TAlphaThread(this);

The CG reports nothing. Is that because '(this)'?
Yes. the argument to the constructor is the owner of the class. The owner
is responsible for the deletion of the class, in due time.
--
Rudy.Velthuis {TeamB} velthuis.homepage.t-online.de/
"RAM /abr./: Rarely Adequate Memory." -- From the Jargon File
 

Re:TThread & VK_ESCAPE hangs the form...

"Vladimir Stefanovic" < XXXX@XXXXX.COM >wrote:
Quote

Is there any memory leak in the shown code, because after:

pAlphaThread = new TAlphaThread(this);

I never see:

delete pAlphaThread;

The CG reports nothing. Is that because '(this)'?
Your original sample included a delete statement and passing
'this' into the ctor has nothing to do with your observations
because TThread's default ctor uses a bool (CreateSuspended),
not a TComponent (Owner) so unless you want to write your own
thread class that descends from TComponent, it will never have
an Owner and must be explicitly deleted.
~ JD
 

Re:TThread & VK_ESCAPE hangs the form...

"JD" < XXXX@XXXXX.COM >wrote:
Quote

[...] and must be explicitly deleted.
Unless of course you set FreeOnTerminate to true.
~ JD
 

Re:TThread & VK_ESCAPE hangs the form...

"Vladimir Stefanovic" < XXXX@XXXXX.COM >wrote in message
Quote
Is there any memory leak in the shown code
No.
Quote
because after:

pAlphaThread = new TAlphaThread(this);

I never see:

delete pAlphaThread;
If you look more closely at the code I gave you, I am using the thread's
FreeOnTerminate property. The thread automatically frees its memory when it
finishes running.
Quote
Is that because '(this)'?
No. TThread does not derive from TComponent, and thus does not have any
Owner like components do.
Gambit
 

Re:TThread & VK_ESCAPE hangs the form...

"Rudy Velthuis [TeamB]" < XXXX@XXXXX.COM >wrote in message
Quote
Yes.
No. 'this' is being passed to a TThread constructor. TThread has no Owner
mechanism since it is not a TComponent descendant.
Gambit