Boost logo

Boost :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2003-02-10 11:29:22


William E. Kempf wrote:
>> Here's some compilable code, to put things in perspective:
>
> Thanks. This helps me, at least.
>
>> #include <boost/detail/lightweight_mutex.hpp>
>> #include <boost/function.hpp>
>> #include <boost/bind.hpp>
>> #include <stdexcept>
>> #include <string>
>> #include <iostream>
>>
>> template<class R> class async_call
>> {
>> public:
>>
>> template<class F> explicit async_call(F f): f_(f), ready_(false)
>> {
>> }
>>
>> void operator()()
>> {
>> mutex_type::scoped_lock lock(mutex_);
>> new(result_) R(f_());
>> ready_ = true;
>> }
>
> Hmm... is this truly portable? Don't you have to use the same
> techniques as optional<> here... or even just use optional<> in the
> implementation? Also, though not on subject with discussion of the
> design, it's probably a bad idea to lock the mutex in this way
> (mutexes shouldn't be held for extensive periods of time), though the
> alternative implementation requires 2 copies of R and a condition for
> waiting on the result.

No, this is not portable; it's also incorrect since it doesn't execute ~R
when it should, and I didn't block copy/assignment. Using optional<> would
be a much better choice. This is only a sketch.

I've used the "illustrative" mutex to specify the synchronization semantics,
not implementation.

>> // step 2: execute an async_call
>> call();
>
> This example, and the implementation above, are just complex
> synchronous calls. I assume you really meant for either the
> constructor or this call to also take an Executor concept?

This line could be

boost::thread exec( ref(call) );

or

boost::thread_pool pool;
pool.dispatch( ref(call) );

I didn't have a prebuilt Boost.Threads library handy when I wrote the code
(rather quickly) so I used a plain call.

>> // step 3: obtain result
>> try
>> {
>> std::cout << call.result() << std::endl;
>> }
>> catch(std::exception const & x)
>> {
>> std::cout << x.what() << std::endl;
>> }
>> }
>
> The one "issue" I see with using operator() to invoke the function is
> the race conditions that can occur if the user calls this multiple
> times. I'd consider it a non-issue personally, since you'd have to
> go out of your way to use this design incorrectly, but thought I
> should at least point it out.

Since operator() is synchronized, i don't see a race... am I missing
something?

> Actually, there's another minor issue as well. The user can call
> operator() and then let the async_call go out of scope with out ever
> calling result(). Mayhem would ensue. The two options for dealing
> with this are to either block in the destructor until the call has
> completed or to simply document this as undefined behavior.

Yes, good point, I missed that.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk