Threads were not terminated by the application Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Post by doublemax »

you're making it sound more complicated than it is.

If your thread checks TestDestroy() every 10 seconds, than it takes 0-10 seconds to terminate it cleanly.

For that purpose to need to know when the thread actually terminates. You can use a thread list in your main thread like i described in the link i posted above.
http://forums.wxwidgets.org/viewtopic.php?p=96574#96574

Other options are sending an event, or just write into a variable in wxThread::Exit()
Use the source, Luke!
TrV
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 630
Joined: Wed Jul 04, 2007 1:12 pm

Post by TrV »

neub wrote:I don't know how the main thread can wait the other threads to finish.
Because of this:

Code: Select all

MyFrame::~MyFrame() 
{ 
    myThread->Delete(); 
}
If you omit TestDestroy() into wxThread::Entry(), the main thread will never terminate !
neub wrote:So the last problem we have is when a thread sleep during like 10s, We need to wait at worst 10 seconds to go out the sleep and check the TestDestroy() method, then to delete object!

My question is if there is a way to cancel the this->Sleep(10000); to go check faster the TestDestroy().
My 'maybe dirty' solution allows this. I'll give you a sample of code if you'ld like to (it's very very not complicated).
neub
Knows some wx things
Knows some wx things
Posts: 28
Joined: Wed Sep 24, 2008 7:42 am

Post by neub »

Actually I'm using event and a list of threads > This works greats... Which is a pretty solution.

My code detached thread is actually like this

Code: Select all

void* Session::Entry() {

    while(!this->TestDestroy()) {
    ...
    this->Sleep(10000);
    }
}
The only problem is that i need to wait about 10 seconds... but it could be about 1 minutes (Check if network connection is still responding)

I was wondering if we can cancel this->Sleep(10000); to make the detached thread destroy faster. Which means to close the application faster!

With this the pretty solution will be still valid, and the dirty hack make the main application close faster.
neub
Knows some wx things
Knows some wx things
Posts: 28
Joined: Wed Sep 24, 2008 7:42 am

Post by neub »

Okay If you can give me your code it should be nice!
TrV
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 630
Joined: Wed Jul 04, 2007 1:12 pm

Post by TrV »

Here it is - by the way, this code can be criticize.
myThread.cpp

Code: Select all

myThread::myThread()
: wxThread(wxTHREAD_DETACHED)
{
    this->pollTime = 500;
    this->runTime = 10000; // sleeps for 10 seconds
}

void* myThread::Entry()
{
    size_t countdown = 0; // initializes countdown (ensures actions will be executed right at thread start)

    while ( !this->TestDestroy() ) { // while observation is active
        if ( countdown <= 0 ) {
            /*
              thread work
            */
            countdown = this->runTime - this->pollTime; // resets countdown
        }
        else { // if countdown has not expired (keeps sleeping until next run)
            countdown -= this->pollTime; // decrements countdown
        }
        this->Sleep(this->pollTime); // sleeps until next scan
    }
}

That's all folks.
So thread is polled every 500 milliseconds, but real work is done every 10 seconds (merely in fact, depends on /* thread work */)
neub
Knows some wx things
Knows some wx things
Posts: 28
Joined: Wed Sep 24, 2008 7:42 am

Post by neub »

Nice, i was implementing exactly the same thing ....
So nothing to criticize :)

It might be a little bit complex but it is the best way to do :)
TrV
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 630
Joined: Wed Jul 04, 2007 1:12 pm

Post by TrV »

doublemax wrote:If your thread checks TestDestroy() every 10 seconds, than it takes 0-10 seconds to terminate it cleanly.
At first, this is what i did. But a combination of different problems made me code this polltime/runtime solution.
- if application was in systray, and click quit on systray menu, application has to deiconize so relevant size and position values could be retrieved
- deiconizing the application when quitting didn't hide frame while waiting for threads to delete
neub
Knows some wx things
Knows some wx things
Posts: 28
Joined: Wed Sep 24, 2008 7:42 am

wxNapThread class

Post by neub »

Hello guy,

here is the result of our long discussion.
I've implemented a generic class derived from wxThread called the wxNapThread.

It ease the way to:
- Use pollTime and runTime with a Nap() function similar to Sleep().
- Send custom event (wxEVT_THREAD) when the wxThread is terminated.
- Connect easyly to an wxEvtHandler.
-

If you have suggestion to improve it i'm okay with this.

Code: Select all


#ifndef WXNAPTHREAD_HPP_
#define WXNAPTHREAD_HPP_

#include <wx/wx.h>
#include <wx/event.h>
#include <wx/thread.h>

//! Define the event type wxEVT_THREAD for all the thread used.
extern const wxEventType wxEVT_THREAD;

#define EVT_THREAD_TERMINATED 0


#define THREAD_RET_OK 0
#define THREAD_RET_DELETED -1
#define THREAD_RET_ERR_INIT -2


/**
 * @brief The wxNapThread is an abstract class representing a detached threads with 
 * 	additional options.
 *
 *	The wxNapThread use a two timers:
 *		 	- one for sleeping (pollTime).
 *			- one for taking a nap (napTime).
 *
 *	At each loop the thread Sleep() during pollTime to avoid consuming CPU. At each pollTime the
 *	thread looks if it need to be paused, resumed or deleted.
 *
 *	Moreover, If the user don't need to use so much the thread (i.e, each 10 seconds) the user
 *	can make the thread taking a nap (doing nothing) during napTime using the function Nap().
 *	In other words, during the napTime the function never go inside the function Loop().
 *
 *	This let the user delete the script in at worst pollTime, even if the thread is doing
 *	nothing during napTime.
 *
 * @note This class is still abstract, therefore Entry() function still need to be defined 
 * 	in a derived class
 *
 *
 *	@ingroup net
 */
class wxNapThread : public wxThread {
public:
	wxNapThread(wxEvtHandler* pParent,int PollTime=50);
	virtual ~wxNapThread();
	virtual wxThreadError RunResume();
	void Nap(unsigned long milliseconds);
	void WakeUp();

protected:
	virtual void OnExit();
	void SendEvt(const wxCommandEvent &evt);
	void Connect(wxObjectEventFunction func);

private:
	wxEvtHandler *m_pParent;	//!< Pointer on a class derived from wxEventHandler.
	int m_pollTime;		//!< pollTime set a initialization
	int m_napTime;				//!< Set while calling nap
	int m_countDown;			//!< countDown (can be reset by WakeUp())
	int m_retCode;
};

#endif /* WXNAPTHREAD_HPP_ */

Code: Select all

#include "wxNapThread.hpp"

DEFINE_EVENT_TYPE(wxEVT_THREAD)

/**
 * This constructor creates a new detached C++ thread object with nap and event functions.
 *
 * @param pParent a wxWidget object which is derived from wxEvtHandler (@ref wxWindow).
 * @param pollTime the minimum Sleep() time of this thread, if it is equal to zeros the
 * thread will consume 100% of the CPU because it will never pause.
 */
wxNapThread::wxNapThread(wxEvtHandler* pParent, int pollTime)
: wxThread(wxTHREAD_DETACHED), m_pParent(pParent), m_retCode(0),
m_pollTime(pollTime), m_napTime(pollTime), m_countDown(0)
{
	if(pollTime<0) 	{
		this->m_pollTime=10;
		this->m_napTime=10;
	}

}

/**
 * @brief Default destructor.
 */
wxNapThread::~wxNapThread() {

}

/**
 * @brief Create, Resume or Run the threads depending on the actual state of the threads.
 * @see wxThread::Create(), wxThread::Resume(), wxThread::Run().
 */
wxThreadError wxNapThread::RunResume() {
	wxThreadError err;
	//If the thread is in pause make it run again.
	if(this->IsPaused()) {
		this->WakeUp();
		return this->Resume();
	}
	//If the thread is running put it in pause.
	else if(this->IsRunning()) {
		this->WakeUp();
		return this->Pause();
	}
	//Otherwise the thread is not created (neither running nor paused)
	else {
		err = this->Create();
		if(err=!wxTHREAD_NO_ERROR) return err;
		err=this->Run();
		return err;
	}
	return wxTHREAD_NO_ERROR;
}

/**
 * @brief Pauses the thread execution for the given amount of time,
 * but let us waking up from this pause.
 * @note This function is similar to wxThread::Sleep(), however during
 * the Sleep() the thread is not usable (Delete(), Pause(), Run()).
 *
 * Using Nap() permits to let the thread doing nothing but polling
 * if an action must be done during the nap time.
 *
 * @see http://wxforum.shadonet.com/viewtopic.php?p=98722
 *
 */
void wxNapThread::Nap(unsigned long milliseconds) {

	//Set the napTime if it is greater than pollTime.
	if(m_pollTime<milliseconds) {
		m_napTime=milliseconds;
		m_countDown = m_napTime - m_pollTime;

		while(!(this->TestDestroy())) {  // while observation is active
			if ( m_countDown <= 0 ) {
					break; 	//or exit the loop!
			}
			else { // if countdown has not expired (keeps sleeping until next run)
				m_countDown -= m_pollTime;	// decrements countdown
			}
			//Truely sleep the thread (can not delete it during this time).
			this->Sleep(m_pollTime);
		}
	}
	else {
		this->Sleep(m_pollTime);
	}
}

/**
 * @brief Wake up the thread while is taking a nap but not while is sleeping.
 */
void wxNapThread::WakeUp() {
	m_countDown=0;
	m_napTime=m_pollTime;
}

/**
 * @brief Send EVT_THREAD_TERMINATED when the thread is terminated.
 * @note To know the exit code of the thread we can use evt.GetInt().
 */
void wxNapThread::OnExit() {

	wxCommandEvent evt(wxEVT_THREAD, EVT_THREAD_TERMINATED);
	evt.SetInt(m_retCode);
	this->SendEvt(evt);
}

/**
 * @brief Send an event to the Event Handler.
 * @note Don't forget to connect the event handler to this class.
 * @see Connect().
 */
void wxNapThread::SendEvt(const wxCommandEvent& evt) {
	m_pParent->AddPendingEvent((wxEvent&)evt);
}

/**
 * @brief To connect the eventHandler function to this thread we need to use this Connect function.
 *
 * In the event handler, if the function is something like this
 * @code
 * void EvtHandlerClass::OnThreadEvt(wxCommandEvent& evt) {
 * 		switch(evt.GetId()) {
 * 			case EVT_THREAD_TERMINATED:
 * 			...
 * 			break;
 * 			...
 * 			default:
 * 			...
 * 			break;
 * 		}
 * }
 * @encode
 *
 * Then, we need to call this->Connect(wxCommandEventHandler(EvtHandlerClass::OnThreadEvt));
 */
void wxNapThread::Connect(wxObjectEventFunction func) {
	m_pParent->Connect(wxEVT_THREAD,func,m_pParent);
}


TrV
Ultimate wxWidgets Guru
Ultimate wxWidgets Guru
Posts: 630
Joined: Wed Jul 04, 2007 1:12 pm

Post by TrV »

Nice job ! :)
Do you mind if i use your code for my own needs ?
neub
Knows some wx things
Knows some wx things
Posts: 28
Joined: Wed Sep 24, 2008 7:42 am

Post by neub »

Sure you can use it!

Maybe, i should put it on the wiki as a FAQ for detached thread. But I don't know how it works!
Post Reply