Re: MT Design Question
"Scott Meyers" <NeverRead@aristeia.com> wrote in message
news:i512pm$sut$1@news.albasani.net...
This is a threading-related design question assuming that C++0x is the
implementation language.
[...]
Here is a pseudo-code sketch of a much more conventional C++0x solution that
should meet the requirements of the problem. Keep in mind that I do not have
access to a C++0x threading library...
_____________________________________________________________
struct entry
{
bool compare() const;
};
struct array
{
entry m_data[DEPTH];
};
struct complete
{
std::condition_vairable m_cond;
std::mutex m_mutex;
};
struct search_thread
{
array& m_array;
unsigned m_key;
complete& m_complete;
std::promise<entry*> m_promise;
std::atomic<bool> m_cancel;
std::thread m_thread;
search_thread(array& a,
unsigned key,
complete& c,
std::promise<entry*>& p)
: m_array(a),
m_key(key),
m_complete(c),
m_promise(p),
m_cancel(false),
m_thread(&search_thread::entry, this)
{
}
~search_thread()
{
m_thread.join();
}
void entry() // the search thread entry
{
for (unsigned i = 0;
i < DEPTH && ! m_cancel.load(mb_relaxed);
++i)
{
if (m_array.m_data[i].compare(m_key))
{
m_complete.m_mutex.lock();
m_promise.set_value(&m_array.m_data[i]);
m_complete.m_mutex.unlock();
m_complete.m_cond.notify_one();
return;
}
}
m_complete.m_mutex.lock();
m_promise.set_value(NULL);
m_complete.m_mutex.unlock();
m_complete.m_cond.notify_one();
}
};
// the main search function
static entry* search(array& a, unsigned key)
{
// create our completion structure
complete c;
// create a promise and future for each search
std::promise<entry*> p_1;
std::promise<entry*> p_2;
std::future<entry*> f_1 = p_1.get_future();
std::future<entry*> f_2 = p_2.get_future();
// create our wait state
std::future<entry*>* waiting[2] = { &f_1, &f_2 };
// create a thread for each promise
search_thread t_1(a, key, c, p_1);
search_thread t_2(a, key, c, p_2);
// wait for any results
entry* result = NULL;
unsigned exceptions = 0;
c.m_mutex.lock();
for (;;)
{
for (unsigned i = 0; i < 2; ++i)
{
if (waiting[i] && waiting[i]->is_ready())
{
// okay, a result is actually ready from a future.
try
{
result = waiting[i]->get();
goto bailout;
}
catch (...)
{
// well, the future has an exception!
}
waiting[i] = NULL;
if (++exceptions == 2)
{
// damn... Both of the futures have exceptions!
goto bailout;
}
}
}
c.m_cond.wait(c.m_mutex);
}
bailout:
c.m_mutex.unlock();
// cancel all of the search threads
t_1.m_cancel.store(true, mb_relaxed);
t_2.m_cancel.store(true, mb_relaxed);
// that's all folks! ;^D
return entry;
}
_____________________________________________________________
Please examine the code sketch Scott. I use a simple condvar and mutex to
avoid spin-polling for the futures state. The wait logic allows for graceful
handling of futures with exceptions. It should do all you want.
Now... An eventcount would get around the need to use condvar+mutex on the
fast-path. It would be much more efficient. Perhaps I will post an
eventcount version when I get some time.