handling GUI resource locks between event handler and boost::thread

L

Lars Uffmann

I have this wxWidgets OnButtonClick event handler, that apparently holds
a lock on all widgets in my form, but this event handler is supposed to
end a thread in the background - while that thread is supposed to update
widget content. So if I want to wait for the thread to end in the event
handler, I have 2 threads waiting on each other - with the one in the
background waiting to access the widget content.

My current workaround is to introduce a boost::mutex, have the
background thread acquire a lock on it upon start, release upon end, and
the OnButtonClick event handler will check (or try to acquire and
instantly release) the lock on that mutex to make sure the previous
background thread has ended before starting a new one.

The according code looks like this:

boost::mutex mutexChecka;
typedef boost::mutex::scoped_lock lockaChecka;

void demonstrateFrameLock()
{
cout << "child thread: acquiring lock" << endl;
lockaChecka lc (mutexChecka);
cout << "child thread: got lock" << endl;

sleep (1);
cout << "child thread: taking a nap (2 seconds)" << endl;
sleep (2);
cout << "child thread: woke up!" << endl;

cout << "child thread: mainWindow->txt1->GetValue = " <<
mainWindow->txt1->GetValue() << endl;

cout << "child thread: unlocking" << endl;
lc.unlock();
cout << "child thread: unlocked" << endl;

cout << "child thread: thread finished" << endl;
}

void debuggingGUImainFrame::OnToggle( wxCommandEvent& event )
{
cout << "main thread: acquiring lock" << endl;
lockaChecka lc (mutexChecka);
cout << "main thread: got lock, unlocking" << endl;
lc.unlock();
cout << "main thread: unlocked" << endl;
if (myThread) {
cout << "main thread: deleting old thread" << endl;
delete myThread;
myThread = 0;
}

myThread = new boost::thread(&demonstrateFrameLock);
cout << "main thread: sleeping 5 seconds" << endl;
sleep (5);
cout << "main thread: done sleeping" << endl;
// myThread.join(); // old code, never got this to work due to the lock
on form controls
cout << "main thread: thread finished" << endl;
}

This gets the job done, but it really doesn't seem very pretty to me -
so being a newbie with thread programming, I would appreciate any
comments on how to improve this code (which is, of course, incomplete
and just the sample that remained after long hours of debugging).

Thank you in advance!

Lars
 
P

Paavo Helde

I have this wxWidgets OnButtonClick event handler, that apparently
holds a lock on all widgets in my form, but this event handler is
supposed to end a thread in the background - while that thread is
supposed to update widget content. So if I want to wait for the thread
to end in the event handler, I have 2 threads waiting on each other -
with the one in the background waiting to access the widget content.

I have no experience in wxWidgets, but this seems more like a threading
problem. Maybe this discussion should move in comp.programming.threads,
but as there is boost::thread used in the code, then I argue that the
ways how to use it should be on-topic.

First, if you start a thread in an event handler and then wait until it
is finished, then why to start a thread in the first place? A background
thread is useful only if the main thread can continue to work in
parallel.

In this light the solution is simple - let the thread running and forget
about it. When it becomes ready, it will update the widget content,
either directly or by posting a message to a special message queue,
which the main thread reads and processes periodically (in idle
processing loop, I guess most UI frameworks should have such a thing).

The latter infrastructure (message queues) is more manageable in the
long term. For instance, the main thread will know by reading the
message queue that the background thread is finished. It can then clean
it up properly and reset all needed flags and pointers. It also avoids
any locking issues (more exactly pushes the locks into internals of the
message queue where they can be put in place once and relatively
easily).

I also note that in your example you have some evil global variables and
you are creating boost::thread on a free function. This is not a good
idea in long term either; instead you should define a custom functor
class with operator() and needed data content and use this functor to
create a boost::thread.

hth
Paavo
 
L

Lars Uffmann

Paavo said:
First, if you start a thread in an event handler and then wait until it
is finished, then why to start a thread in the first place? A background
thread is useful only if the main thread can continue to work in
parallel.
Of course :)
The idea is, to have a toggle button to activate/deactivate a "service"
- listening for network traffic - running in the background.
Since this service is basically downloading files, and possibly has to
reorder packets and all, it must not run more than once at a time.

So the toggle button event handler should end the thread when it's
active, and when the button is clicked again, should make sure the old
thread has ended, before starting it again.
about it. When it becomes ready, it will update the widget content,
either directly or by posting a message to a special message queue,
which the main thread reads and processes periodically (in idle
processing loop, I guess most UI frameworks should have such a thing).
oO I am starting to understand the concept of messages! Thanks a bunch
for this almost offhand remark! :)
The latter infrastructure (message queues) is more manageable in the
long term. For instance, the main thread will know by reading the
message queue that the background thread is finished. It can then clean
it up properly and reset all needed flags and pointers. It also avoids
any locking issues (more exactly pushes the locks into internals of the
message queue where they can be put in place once and relatively
easily).
Cool - even more elaborate explanations. Very well written, thank you a
lot for this. I might be able to solve basically all the issues I've
been having with this approach. Gonna look into it (hopefully get around
to it before next business trip ;)
I also note that in your example you have some evil global variables and
I sorta must have missed the point when global variables became
"evil"... From Ansi C to C++? I have always preferred to have a couple
of global variables if that meant avoiding a lot of parameters in
function calls, because nearly every function in my application was
accessing those. Is there any big difference to capsule everything in
one big class and using class properties as "global" variables?
you are creating boost::thread on a free function. This is not a good
idea in long term either; instead you should define a custom functor
class with operator() and needed data content and use this functor to
create a boost::thread.
erm... Now you lost me. My problem is, while I'm quite good at
programming any particular algorithm, I usually try to get some work
done with it, and so far never had the need for "functors" or even
overloaded operators - which means I'd have to read a bunch of
documentation without making any progress in my project.

If I am not going to programmers hell for it, I'd like to get my program
to do what I want it to, and then replace piece by piece with more
standard-conform code. And yes, I do actually update such projects every
now and then - I'm not leaving them unchanged just because they're
working :) But I need to work with it rather soon.

Thank you a lot for the inputs!

Lars
 
L

Lars Uffmann

bytebro said:
wxWidgets have a support forum at http://wxforum.shadonet.com/ and
boost have user support mailing lists at http://www.boost.org/more/mailing_lists.htm#users
HTH.

No, not at all. You told me two things that I do already know, and btw.
there is no newsgroup where boost is more on-topic than this one,
because the boost mailing list - while having a newsgroup portal -
doesn't allow posting via the latter.

I guess I'll add a disclaimer in my signature saying which resource
sites I know and which I do not :) Otherwise yes, your information would
have been partially helpful - but a direct answer was what I was looking
for.

Thanks anyways,

Lars
 
P

Paavo Helde

Paavo Helde wrote: [...]
I also note that in your example you have some evil global variables
and
I sorta must have missed the point when global variables became
"evil"... From Ansi C to C++? I have always preferred to have a couple
of global variables if that meant avoiding a lot of parameters in
function calls, because nearly every function in my application was
accessing those. Is there any big difference to capsule everything in
one big class and using class properties as "global" variables?


Nope, there should be many different classes, one big singleton class
would be the same as global variables. For example, if you create a new
thread, you almost always want to pass some data to it. This data should
be put in a struct of class which is passed directly to the thread, not
in global variables. After all, you may end up creating more thread than
one. Even if you are creating only one thread, it is good to have some
encapsulation of the data what it receives.

In case of multiple threads the global data (actually any data visible
to more than one thread) becomes especially painful because you have to
lock *each* access to *every* such variable. OK, you could argue that
different threads are accessing different data at different times by the
application logic, thus no locking needed, but this is extremely hard to
achieve in a large project and even more hard to verify that you have
done it properly. So if there is an easy way to avoid global variables
in a multithreaded application this should be done without hesitation.
erm... Now you lost me. My problem is, while I'm quite good at
programming any particular algorithm, I usually try to get some work
done with it, and so far never had the need for "functors" or even

Now that's it. Traditional threading frameworks allow to pass a void*
pointer to the started thread, where you can pass a pointer to a struct
with relevant data, which you have to cast back to the original type in
the thread functions. The boost::thread library, being a *real c++
library*, uses functors for that. So you have to learn them sooner or
later. Fortunately these are not so hard, you just define an
operator()() member function. The hardest problem technically is to
avoid the copy of the functor object (when needed), the boost::thread
library makes a copy of the thread object by default, but this can be
suppressed by a little generic wrapper I keep carrying on from one
project to another. An uncompiled example (here the objects could be
copied as well but in real life my thread objects tend to be non-
copyable):

template<typename T>
struct functor_forwarder {
T* p_;
functor_forwarder(T& p): p_(&p) {}
functor_forwarder(const functor_forwarder<T>& b): p_(b.p_) {}
void operator()() {(*p_)();}
};

class HttpServer {
unsigned short port_;
boost::thread* thread_;
public:
HttpServer(unsigned short port): port_(port) {
thread_ = new boost::thread(
functor_forwarder<HttpServer>(*this));
}
void operator()() {
// thread function
while(true) {
// accept and service connections on port_
}
}
};

int main() {

// start up some servers

HttpServer server(1234);

HttpServer another_server(4568);

// go on with other business...
}

Thank you a lot for the inputs!

You are welcome!
Paavo
 
L

Lars Uffmann

Hey Paavo,

I'll have to chew a bit on your example, doesn't seem so obvious to me -
I haven't yet learned even nearly all aspects of C++.
Put it in my "re-read" folder :)

Meanwhile, I managed to solve my locking problem with the help of input
from this thread, in the way stated below.

Best Regards & thanks to everyone involved,

Lars

solution relevant code excerpt (missing includes and wxWidgets
initialization):
/* *** */

debuggingGUImainFrame *mainWindow; // wxWidgets main window,
// controls cmd1 (wxButton) and txt1 (wxTextCtrl)

int glbSERVICING = 0; // "keepalive flag" for the background thread

// mutex to ensure alternate access to window controls and glbSERVICING
boost::mutex mutexChecka;

typedef boost::mutex::scoped_lock lockaChecka;

void demonstrateFrameLock()
{
sleep (1);
cout << "child thread: acquiring lock" << endl;
lockaChecka lc (mutexChecka);
cout << "child thread: got lock, child thread: ready for servicing"
<< endl;
mainWindow->cmd1->Enable();

int i = 0;
while (glbSERVICING) {
lc.unlock(); // unlock r/w access to keepalive flag
cout << "child thread: unlocked" << endl;
++i;
sleep (2);
cout << "child thread: servicing... (i = " << i << ")" << endl;
lc.lock(); // prepare read access to keepalive flag
cout << "child thread: got lock" << endl;
}

cout << "child thread: mainWindow->txt1->GetValue = " <<
mainWindow->txt1->GetValue() << endl;
mainWindow->cmd1->Enable();

lc.unlock();
cout << "child thread: unlocked, thread finished" << endl;
}

void debuggingGUImainFrame::OnToggle( wxCommandEvent& event )
{
cmd1->Disable();

cout << "main thread: acquiring lock" << endl;
lockaChecka lc (mutexChecka);
cout << "main thread: got lock" << endl;

if (glbSERVICING) {
glbSERVICING = 0;
mainWindow->cmd1->SetLabel (wxT ("Enable Service"));
}
else {
glbSERVICING = 1;
mainWindow->cmd1->SetLabel (wxT ("Disable Service"));
boost::thread *helperThread;
helperThread = new boost::thread(&demonstrateFrameLock);
delete helperThread;
helperThread = 0;
}

cout << "main thread: unlocking" << endl;
lc.unlock();

cout << "main thread: thread finished" << endl;
}
/* *** */
 

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

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top