Archive

Archive for March, 2010

Propagating exceptions between different threads in C++0x

March 29, 2010 Leave a comment

Yesterday I finally decided to make use of all available C++0x features that are supported by gcc-4.4 in Untropy (a very specialized tool for a limited audience that is not of interest here). Whats interesting about this, is that what finally made the important difference for me is not one of the prominent features like Rvalue References, Auto Typed Variables or Initializer Lists. Instead it’s some seemingly unspectacular type called std::exception_ptr and a few related functions. This rather minimal, but very useful API has been designed to allow transporting exceptions between threads. Here is how this can be done with boost::thread (includes and namespaces are omitted for clarity – you can grab the full source here):

   /**
     * A callable implementation that allows exception propagation between 
     * threads for boost::tread.
     */
    class Callable
    {
        public:

        /**
         * Default copy constructor.
         *
         * @attention
         *  Should not be invoked from different threads with the same objects.
         */
        Callable(const Callable&) = default;

        /**
         * Default assignment operator.
         *
         * @attention
         *  Should not be invoked from diffenrent threads with the same objects.
         */
        Callable& operator=(const Callable&) = default;

        /**
         * To be called by boost::thread.
         */
        void operator()() const;

        /**
         * Rethrows pending exceptions.
         */
        void rethrowIfPending() const;

        /**
         * Returns true iff an exception is pending.
         */
        bool isExceptionPending() const;

        virtual ~Callable() = default;

        protected:

        Callable();

        /**
         * Does the actual work.
         *
         * @attention
         *  Exceptions thrown from this method will be catched in operator()(),
         *  and can be rethrown by rethrowIfPending() const.
         */
        virtual void doWork() const = 0;

        private:
        
        struct ExceptionContainer
        {
            std::exception_ptr exceptionPtr;
        };

        std::tr1::shared_ptr<boost::recursive_mutex> m_exceptionMutex;
        std::tr1::shared_ptr<ExceptionContainer> m_exception;
    };

Note that the only reason we explicitly declared a copy constructor and an assignment operator is for the comments above them. Luckily the C++0x feature Defaulted and Deleted Functions allows us to omit some boilerplate code here. But lets move to the more interesting, and less documented private part of the declaration above: Because boost::thread invokes a copy of our Callable, we cannot store pending exceptions in the class directly if we want them to be visible from outside. To solve this problem, we actually store them in a tr1::shared_ptr to a helper struct. Last but not least, we use a boost::recursive_mutex to guard the stored exception. Here is the implementation (the full source is here):

    void Callable::operator()() const
    {
       try
       {
           doWork();
       }
       catch(...)
       {
           boost::recursive_mutex::scoped_lock lock(*m_exceptionMutex);
           m_exception->exceptionPtr = current_exception();
       }
    }

    void Callable::rethrowIfPending() const
    {
        boost::recursive_mutex::scoped_lock lock(*m_exceptionMutex);
        if(isExceptionPending())
            rethrow_exception(m_exception->exceptionPtr);
    }

    bool Callable::isExceptionPending() const
    {
        boost::recursive_mutex::scoped_lock lock(*m_exceptionMutex);
        return (exception_ptr() != m_exception->exceptionPtr);
    }

    Callable::Callable()
        : m_exceptionMutex(new boost::recursive_mutex()),
          m_exception(new ExceptionContainer)
    {

    }

Using the C++0x features std::current_exception() in line 10 and std::rethrow_exception in line 18, thats all that needs to be done. The careful reader might notice, that Callable isn’t entirely thread save itself: Coping or assigning Callable instances that share internal state from within multiple threads will most likely result in undefined behavior, because tr1::shared_ptr is not thread save. In theory, C++0x contains APIs for accessing shared_ptr instances atomically from within multiple threads, but they are not available with gcc-4.4. Of course, I could have implemented a thread save reference counter in the Callable class directly, but that doesn’t seem worth the effort, as manipulating identical Callable instances from multiple threads is an error anyway. Last but not least: Here is a unit test for the code above, that also illustrates how it is meant to be used.

Advertisements
Categories: Programming Tags: , ,