Boost logo

Boost :

From: William E. Kempf (wekempf_at_[hidden])
Date: 2003-02-10 16:59:46


> From: David Abrahams <dave_at_[hidden]>
> "Peter Dimov" <pdimov_at_[hidden]> writes:

> > It's a tool that allows high-level interfaces to be built. Whether
> > people will want/need to build their own high-level interfaces is
> > another story.
>
> I think it's a valuable question to ask whether /everyone/ will want
> to create /the same/ high-level interface ;-). In other words, as
> long as we have a bunch of low-level thread primitives, I prefer to
> reduce interface complexity and increase encapsulation unless we can
> find a specific use for a medium-level interface.

How about this compromise:

template <typename R>
class async_call
{
public:
    template <typename F>
    explicit async_call(const F& f)
        : m_func(f)
    {
    }

    void operator()()
    {
        mutex::scoped_lock lock(m_mutex);
        if (m_result)
            throw "can't call multiple times";
        lock.unlock();
        R temp(m_func());
        lock.lock();
        m_result.reset(temp);
        m_cond.notify_all();
    }

    R result() const
    {
        boost::mutex::scoped_lock lock(m_mutex);
        while (!m_result)
            m_cond.wait(lock);
        return *m_result.get();
    }

private:
    boost::function0<R> m_func;
    optional<R> m_result;
    mutable mutex m_mutex;
    mutable condition m_cond;
};

template <typename R>
class future
{
public:
    template <typename F>
    explicit future(const F& f)
        : m_pimpl(new async_call<R>(f))
    {
    }

    future(const future<R>& other)
    {
        mutex::scoped_lock lock(m_mutex);
        m_pimpl = other.m_pimpl;
    }

    future<R>& operator=(const future<R>& other)
    {
        mutex::scoped_lock lock(m_mutex);
        m_pimpl = other.m_pimpl;
    }

    void operator()()
    {
        (*get())();
    }

    R result() const
    {
        return get()->result();
    }

private:
    shared_ptr<async_call<R> > get() const
    {
        mutex::scoped_lock lock(m_mutex);
        return m_pimpl;
    }

    shared_ptr<async_call<R> > m_pimpl;
    mutable mutex m_mutex;
};

The async_result gives us the low level interface with a minimum of overhead, while the future gives us a higher level interface for ease of use. This higher level interface should even allow the syntax suggested elsewhere in this thread:

template <typename R, typename F, typename E>
future<R> execute(F function, E executor)
{
   future<R> res(function);
   executor(res);
   return res;
}

template <typename F>
void thread_executor(F function)
{
   thread thrd(function);
}

future<int> res = execute(foo, &thread_executor);
double d = res.result();

(And yes, I would offer these interfaces as well.)

Thoughts?

William E. Kempf
wekempf_at_[hidden]


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