#ifndef FORK_HPP_INCLUDED
#define FORK_HPP_INCLUDED

// Copyright (c) 2007 Peter Dimov
//
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt

#include "future.hpp"
#include "thread.hpp"
#include <boost/utility/result_of.hpp>
#include <boost/function.hpp>

class fork_canceled: public std::exception
{
public:

    char const * what() const throw()
    {
        return "std::fork_canceled";
    }
};

template<class R, class Fn> class task
{
private:

    Fn fn_;
    future<R> ft_;

public:

    task( Fn fn, future<R> const & ft ): fn_( fn ), ft_( ft )
    {
    }

    void operator()() // throw()
    {
        try
        {
            ft_.set_value( fn_() );
        }
        catch( std::thread_cancel const & )
        {
            ft_.set_exception( copy_exception( fork_canceled() ) );
        }
        catch( ... )
        {
            ft_.set_exception( current_exception() );
        }
    }
};

template<class Fn> class task< void, Fn >
{
private:

    Fn fn_;
    future<void> ft_;

public:

    task( Fn fn, future<void> const & ft ): fn_( fn ), ft_( ft )
    {
    }

    void operator()() // throw()
    {
        try
        {
            fn_();
            ft_.set_value();
        }
        catch( std::thread_cancel const & )
        {
            ft_.set_exception( copy_exception( fork_canceled() ) );
        }
        catch( ... )
        {
            ft_.set_exception( current_exception() );
        }
    }
};

void fork_impl( boost::function< void() > const & fn );

template< class Fn > future< typename boost::result_of<Fn()>::type > fork( Fn fn )
{
    typedef typename boost::result_of<Fn()>::type R;

    future<R> ft;
    task<R, Fn> fw( fn, ft );

    fork_impl( fw );

    return ft;
}

template< class Fn, class A1 > future< typename boost::result_of<Fn(A1)>::type > fork( Fn fn, A1 a1 )
{
    return fork( boost::bind( fn, a1 ) );
}

template< class Fn, class A1, class A2 > future< typename boost::result_of<Fn(A1)>::type > fork( Fn fn, A1 a1, A2 a2 )
{
    return fork( boost::bind( fn, a1, a2 ) );
}

template< class Fn, class A1, class A2, class A3 > future< typename boost::result_of<Fn(A1)>::type > fork( Fn fn, A1 a1, A2 a2, A3 a3 )
{
    return fork( boost::bind( fn, a1, a2, a3 ) );
}

#endif // #ifndef FORK_HPP_INCLUDED

