|
Boost Users : |
From: Roland Schwarz (roland.schwarz_at_[hidden])
Date: 2004-12-12 12:21:45
Peter Dimov wrote:
> Yuval Ronen wrote:
>
> [...]
>
>> If we agree that threads cannot be copied,
>
>
> We don't. More details below.
>
Sorry for jumping in between. But I always was wondering what "copying a
thread"
could mean. Innocently I would expect creating a second instance of the
thread similar
to a "fork" of processes in unix. But I don't think this is what you
have in mind.
Otherwise copying the whole state of a thread is not of much sense
either (thread
needs to be scheduled.) I think noncopyable is a property of a thread,
and the
design simply refelcts this fact, but I might be wrong in this respect
and would like
to hear what I am missing.
>> it means that each thread can be
>> represented by only one thread object. Pass the address of thread
>> objects and compare them, if you really want to. It'll give you the
>> exact same result.
>
>
> The problem with your reasoning is that the thread object may have
> been destroyed. Its lifetime is not tied to the lifetime of the thread.
>
This is true. But where is the problem? Once I have deleted the thread
object (not the thread!) I am not
able to compare it to anything, since the memory is invalid, isn't it?
As I see it, the demand for thread ID's or references mainly come from
the attempt to
communicate with the thread. (Are there really other usages?) So not
having a thread ID
is no drawback at all, since this problem can be solved easily by other
means.
I append a small example showing how a thread can communicate with another,
using a control object with automatic lifetime:
Some remarks
1) from inside the thread I use a tls that holds the communication object
2) from outside I use a wrapper object
3) control of lifetime of this object is tricky, since it must last as
long as both
communicating threads have agreed to dipose it.
#include <boost/thread.hpp>
#include <boost/bind.hpp>
struct control_impl;
boost::thread_specific_ptr<control_impl*> g_control;
struct control_impl {
control_impl() : stopped(false), ref(1) {}
void stop() {
boost::mutex::scoped_lock lk(monitor);
stopped = true;
control_changed.notify_one();
}
void wait_until_stopped() {
boost::mutex::scoped_lock lk(monitor);
while(!stopped)
control_changed.wait(lk);
}
void release() {
boost::mutex::scoped_lock lk(monitor);
if (--ref == 0) {
lk.unlock();
delete this;
g_control.reset(0);
}
}
void addref() {
boost::mutex::scoped_lock lk(monitor);
++ref;
}
boost::mutex monitor;
boost::condition control_changed;
bool stopped;
int ref;
};
// the control struct to be used from outside the thread
struct control {
control() { pimpl = new control_impl; }
~control() { pimpl->release(); }
void stop() {pimpl->stop(); }
control_impl* pimpl;
};
// the functions to be used from inside the thread
void wait_until_stopped()
{
(*g_control)->wait_until_stopped();
}
void register_control(control* p)
{
p->pimpl->addref();
g_control.reset(new control_impl*(p->pimpl));
}
void release_control()
{
(*g_control)->release();
}
// the user program
void foo() {
wait_until_stopped();
}
void run(control* p) {
register_control(p);
foo();
release_control();
}
int main(int argc, char* argv[])
{
control* pc = new control;
boost::thread* pt = new boost::thread(boost::bind(run,pc));
pc->stop();
delete pc;
pt->join();
return 0;
}
In a real implementation of course the user code should not have access to
the control_impl, so it cannot mess with the reference counting.
I admit however that the real question remains: What is a thread object
then at all?
At the momnet its only purpose is to serve as an access point for join.
I think it s worth about thinking about a generalized mechansim that will
allow access of user defined structures from inside and outside a thread.
Roland
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net