TThread & VK_ESCAPE hangs the form...

TThread & VK_ESCAPE hangs the form...

Post by Vladimir S » Sat, 24 Dec 2005 04:24:03


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
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Vladimir S » Sat, 24 Dec 2005 04:27:15

Correction:

if ( ( pAlphaThread ) && ( pAlphaThread->IsRunning ) )
return;

... but not relevant for now.



--
Best regards,
Vladimir Stefanovic

 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Chris Uzda » Sat, 24 Dec 2005 04:42:17

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


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.


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.


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);
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Vladimir S » Sat, 24 Dec 2005 04:51:26

> But in a nutshell, if every time you call Synchronize you check

I'll try this. It seems reasonable...


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
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Team » Sat, 24 Dec 2005 07:12:32

"Vladimir Stefanovic" < XXXX@XXXXX.COM > wrote in message
news: XXXX@XXXXX.COM ...


That is because you are not managing the thread properly.


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.


That code is completely useless, as it doesn't actually do anything
meaningful.


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->T
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Steve Alet » Sat, 24 Dec 2005 17:06:39

> The thread's destructor calls the thread's

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.
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Team » Sat, 24 Dec 2005 20:19:35


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;


I do not recommend it. There are potential problems with letting TThread's
destructor call WaitFor(). It is always best to call it yourself.


TThread's destructor has always called WaitFor() in every version of BCB.


Gambit
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Vladimir S » Sat, 24 Dec 2005 23:42:20

> You are deadlocking the thread. The thread's destructor calls

This explanes everything.

I haven't tried the new thread code, but I will as soon as possible...

Thank you.


--
Best regards,
Vladimir Stefanovic
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Vladimir S » Mon, 26 Dec 2005 23:14:46

> If you must use a thread, then do not free it in the form's OnClose event.

Works perfect, without deadlocks!!!

Thank you.





--
Best regards,
Vladimir Stefanovic
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Vladimir S » Tue, 27 Dec 2005 05:31:53

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
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Rudy Velth » Tue, 27 Dec 2005 06:26:14


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} http://www.yqcomputer.com/

"RAM /abr./: Rarely Adequate Memory." -- From the Jargon File
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by JD » Tue, 27 Dec 2005 18:06:14


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
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by JD » Tue, 27 Dec 2005 18:07:40


Unless of course you set FreeOnTerminate to true.

~ JD
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Team » Thu, 29 Dec 2005 18:49:23


No.


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.


No. TThread does not derive from TComponent, and thus does not have any
Owner like components do.


Gambit
 
 
 

TThread & VK_ESCAPE hangs the form...

Post by Team » Thu, 29 Dec 2005 18:50:07


No. 'this' is being passed to a TThread constructor. TThread has no Owner
mechanism since it is not a TComponent descendant.


Gambit