Aborting threads before quit my application using Application.Exit

G

Guest

Hi there!

I have an application that needs to monitor PoweOFF events. I do this using
a code I found in the Internet and everything is ok, except for one thing:

When I start to listen events on ppc, I start a Thread. When I quit my
application I use this.Close(), this.Dispose() and Application.Exit(). If I
do this on debug in Visual Studio, I clearly see that Application.Exit does
not quit the application. Seem something is locked in the memory.

I did some tests, and saw the problem is my thread, so I think I need to
quit it before Application.Exit.

But it didn't work....

Can you help me?
 
D

Daniel Moth

Your thread has to change a global boolean indicating whether it should shut
down or not. For an example of this see here:
http://www.danielmoth.com/Blog/2004/11/dont-poll.html

If you post the code of how you use the thread we can have more specific
suggestions. BTW, ApplicationExit should not be required; closing the main
form after stopping other threads is all it takes.

Cheers
Daniel
 
G

Guest

Hi Daniel,

I'm using this solution for the poweroff thing:
http://www.opennetcf.org/forums/topic.asp?TOPIC_ID=3557

When I open a connection, I start the thread. When I quit the program,
something stays in the memory. With some tests, I saw it was m_th Thread (
m_th = new Thread(new ThreadStart(WaitThreadProc));)

I redeclared the thread using ThreadEx from OpenNETCF to gain acess to
..Abort method, but it didn't worked. :(

private void mnitmSair_Click(object sender, System.EventArgs e)
{
try
{
finalizaComunicacao(); // Closes the port
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
try
{
m_th.Abort();
}
catch{}
this.Close();
this.Dispose();
Application.Exit();
}
}

Method finalizaComunicacao is like this:
private bool finalizaComunicacao()
{
try
{
if (port.Enabled)
{
//Para o evento que monitora o pressionamento do botão Power
EventModify(m_hEvtQuit, EVENT_SET);
portClosedWarningFlag=true;
port.Enabled = false;
}
return true;
}
catch
{
return false;
}
}
I use the dll from Franson (http://franson.biz) to control serial
communication. In the Closing event of the form, I dispose the object port.

Can you help me ?
 
D

Daniel Moth

First get rid of the Dispose and Application.Exit. Assuming that is your
main form, simply closing it is enough. Whether you want to use ThreadEx or
not is your call.

You are checking for port Enabled rather than IsOpen. If that is what your
3rd party library is exposing that is fine, if not then your EventModify
will not run.

Assuming your EventModify runs (check this e.g. with a breakpoint), you
should be setting the flag that indicates you are now shutting down *before*
signalling the event. In the WaitThreadProc method, the first line in the
while loop should check the flag you set in your closing method. If it is
set (i.e. the app is shutting down), just return (thus exiting the thread).

The stuff above together with the example on my blog should be enough for
you to make it work. Obviously you should not just copy paste code from the
web and use it without understanding it. You said you use the code in the
forum but then said you are using a 3rd party serial class so I will not go
into details of cleaning up and leave that to you (e.g. closing native
handles).

Cheers
Daniel
 
S

Sergey Bogdanov

ThreadEx.Abort does not work well, try to avoid calling it at all.

To help to find where a thread is hanging run you program in Debug Mode
(F5) and invoke a close event from your application. Wait for a moment
and then break all threads by using Debug->Break All button. Now you can
switch between threads in a Debug->Windows->Threads window and see
what happened with each of them.

I have not seen your thread code but suppose hanging was caused by
serial communication component that was trying to read data that is not
available. Maybe you can call serialComponent.Dispose in
finalizaComunicacao function or something...

Hope this help,
Sergey Bogdanov
http://www.sergeybogdanov.com
 
G

Guest

If the thread is blocked in an API call (like WaitForSingleObject) you can't
abort it.

-Chris
 
P

Paul G. Tobey [eMVP]

That's why, if you're using WaitForSingleObject, you need to be sure that
it's OK to set that event yourself, so that you can exit the thread. If the
event is some global or OS-set event, you don't want to be setting it just
so you can exit that thread. In that case, create your own 'exit event' and
use WaitForMultipleObjects, one of which is your exit event. When
WaitForMultipleObjects returns, check the return value to see if the exit
event was set and, if so, free anything that needs to be freed and let the
thread exit...

Paul T.
 
G

Guest

Hi Sergey, I tried to look the threads, but the only one I can see is the
main thread. The other one shows a code "The's no code avaiable for this
thread" or something.

Didn't help :(

Thanks anyway for your attention.
 
G

Guest

Hi,

Now I saw I have 3 threads lost, and I can see the location of two of them:
one is the main thread, and it's location is set to finalizaComunicacao on my
main form.
398020 <No Name> ColetorBT.frmColetor.finalizaComunicacao Unknown 0

The second I don't have any information about
1896612 <No Name> 0

The third has it's location set to:
1974788 <No Name> System.PInvoke.PAL.Threading_Event_Wait Unknown 0

Could it help you helping me? =)
 
C

Chris Tacke, eMVP

Sure sounds like one thread is waiting for an event, probably blocking on
WaitForSingleObject, which I already said cannot be aborted. You need to
architect your app to allow you to signal all created threads to stop
themselves before exiting. The most common mechanism is to simply use a
global boolean and check it iteratively in the thread. Using an event would
also work, but in any case it's up to you to implement it. Simply exiting
the app will not do it.

--
Chris Tacke
Co-founder
OpenNETCF.org
Has OpenNETCF helped you? Consider donating to support us!
http://www.opennetcf.org/donate
 
G

Guest

Hi Chris,

Thanks for your attention :)

Well, I read your suggestion, but I don't have any idea of how I can do it.
I think if I exit only this event (the thread in discussion) I'll be able to
quit the application. I don't have any problem to do it if this thread is not
running, so the other threads are finalizing ok.

I'll put the code here, please help me giving one light if you can ...

// Listener thread
void WaitThreadProc()
{
// Open our message queue for reading
MSGQUEUEOPTIONS opt = new MSGQUEUEOPTIONS();
opt.bReadAccess = 1;
IntPtr hQueue = CreateMsgQueue( "PowerEventWaitQueue", opt);

// Start waiting for Message Queue to fill up, while checking for a
termination request
while ( WaitForSingleObject( m_hEvtQuit, 0 ) != WAIT_OBJECT_0 )
{
// Did queue get some data?
if ( WaitForSingleObject(hQueue, 100) == WAIT_OBJECT_0 )
{
// Yes, let's read it
// 1024 byte buffer should be well enough.
POWER_BROADCAST pb = new POWER_BROADCAST(1024);
int nRead, Flags;
ReadMsgQueue(hQueue, pb.Data, pb.Data.Length, out nRead, 0, out Flags);

// Is this an event we wanted?
if ((pb.Message & PowerEventType.PBT_TRANSITION) ==
PowerEventType.PBT_TRANSITION)
{
if (( pb.Flags & PowerState.POWER_STATE_ON ) ==
PowerState.POWER_STATE_ON )
{
//PDA has awaken from suspend mode
//Check if port is closed
if (port.Enabled == false)
{
try
{
Cursor.Current=Cursors.WaitCursor;
//Wait 5 seconds before we open the port
//If you don't you will get CreateFile error 55.
//I am guess that the bluetooth port has not initialized by
//the PDA
Thread.Sleep(5000);

//Set warning flag to tell port is not closed
this.portClosedWarningFlag=false;
port.Enabled = true;

//Close and reopen the port
this.portClosedWarningFlag=true;
port.Enabled = false;

//Wait another 5 seconds before opening the port
//This is done to make sure that the port has
//sufficient time to close.
Thread.Sleep(5000);
this.portClosedWarningFlag=false;
port.Enabled = true;

}
catch(Exception ex)
{
//MessageBox.Show("WaitThread reopening port: " +ex.ToString());
}
finally
{
Cursor.Current=Cursors.Default;
}
}

}
else if (( pb.Flags & PowerState.POWER_STATE_SUSPEND )
== PowerState.POWER_STATE_SUSPEND )
{
//PDA is going into Suspend mode
try
{

//Check if the port is open.
if (port.Enabled)
{
//Set the close port warning flag
this.portClosedWarningFlag=true;
//Close and dispose of the port
port.Enabled = false;
port.Dispose();

}
}
catch(Exception ex)
{
MessageBox.Show("WaitThread reopening port: " +ex.ToString());
}
}

}

}
}
CloseHandle(hQueue);
CloseHandle(m_hEvtQuit);

}
 
S

Sergey Bogdanov

Also to see the whole picture I suggest you to write small class that
will dump strings to file (log file) and then insert debug lines for
every step in this method. It will help you to see where the problem is
and what is hanging your system.

Best regards,
Sergey Bogdanov
http://www.sergeybogdanov.com
 
G

Guest

Hi Sergey,

I'm trying your last suggestion right now.

And I used to comment every line of this code to make debug, but when I
prepared this for going to clients, I erased those lines. But I'll put it
back.

Hope your first suggestion works. I'll see and return here what happened.

Thanks
 
G

Guest

Hi Sergey

Well, as I expected... it didn't work.

I use this sleeps just for wait a little, because the port need some seconds
to answer to the command.

I don't have more ideas :(
 
G

Guest

If it can help...

This is what I do when I open the communication port and start all threads.

{
(...)
port.BaudRate = 115200;
port.ComPort = nomePorta;
port.Timeout = 5000;

#region Referente ao PowerOFF
// Create event to stop the thread
m_hEvtQuit = CreateEvent(0, true, false, null);

// Create listener thread
//m_th = new Thread(new ThreadStart(WaitThreadProc));
m_th = new OpenNETCF.Threading.ThreadEx(new ThreadStart(WaitThreadProc));

try
{
// Create message queue for power broadcasts
m_hQueue = CreateMsgQueue( "PowerEventWaitQueue", new MSGQUEUEOPTIONS() );
port.Enabled = true;
}
catch (MissingMethodException)
{
// If we got here, it means the target device does not have CE.NET. This
API is not available in Windows CE 3.0
//MessageBox.Show("This application uses API not available on current
platform. Supported platforms are Windows CE.NET 4.1 devices, such as Windows
Mobile 2003", "Error");
//Close();
//return;
}

// Start listening
m_th.Start();

// Register for power notifications.
IntPtr hNotif = RequestPowerNotifications(m_hQueue,
PowerEventType.PBT_POWERSTATUSCHANGE|PowerEventType.PBT_RESUME|PowerEventType.PBT_TRANSITION);

(...)
}

Well, I have a event to stop the thread. And when i quit the program, I do
"EventModify(m_hEvtQuit, EVENT_SET);" - Event_SET is a constant, it's value
is 3. But why it didn't work?

Thanks.
 
C

Chris Tacke, eMVP

Alright, so I looked closer at your code. Distilled you have this:

while ( WaitForSingleObject( m_hEvtQuit, 0 ) != WAIT_OBJECT_0 )
{
// do some processing
}

CloseHandle(hQueue);
CloseHandle(m_hEvtQuit);

So when the quit event comes in, you exit this thread, but you don't close
and dispose the serial port. The port has a listener thread that's blocking
waiting for data and it's still alive one the app ends. It should look like
this:

while ( WaitForSingleObject( m_hEvtQuit, 0 ) != WAIT_OBJECT_0 )
{
// do some processing
}

port.Enabled = false;
port.Dispose();
CloseHandle(hQueue);
CloseHandle(m_hEvtQuit);

Also, no idea why you don't just use a managed event for signalling to save
the potential leak of not closing the event handle.
--
Chris Tacke
Co-founder
OpenNETCF.org
Has OpenNETCF helped you? Consider donating to support us!
http://www.opennetcf.org/donate
 
S

Sergey Bogdanov

Chris, as far as I know there are no memory leaks if you are not closing
an event handle. Here is the quote from MSDN:
"The system closes the handle automatically when the process terminates.
The event object is destroyed when its last handle has been closed."


Best regards,
Sergey Bogdanov
http://www.sergeybogdanov.com
 
C

Chris Tacke, eMVP

Leaving any handle open without explicit closing is bad, bad, bad, bad, bad,
bad form. Sure, if it's in a process and the process dies, it closes. But
you start getting lazy, then you write a DLL that creates an even on attach,
or you write a driver, or a multitude of other scenarios where you could be
creating and not destroying them and you'll end up with bugs that are not
fun to chase down.

You can make the argument that most things get cleaned up when a process
exits. That's still no excuse for poor coding practices. Heck, I put in
clean up code even after infinite loops that will *never* get called because
you never know who will be there after you and make modifications.


--
Chris Tacke
Co-founder
OpenNETCF.org
Has OpenNETCF helped you? Consider donating to support us!
http://www.opennetcf.org/donate
 
G

Guest

Hi Chris.

As I thought before, the problem is not the port. The problem is my thread.
If I don't start the thread, I can open and close the port, quitting the
program with no problems.

But if I start the thread (m_th.Start()) my problems start...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top