Fork us on GitHub Follow us on Facebook Follow us on Twitter

Changeset 6e97265 in mainline


Ignore:
Timestamp:
2019-06-30T14:37:40Z (16 months ago)
Author:
Jaroslav Jindrak <dzejrou@…>
Branches:
master
Children:
3a29607
Parents:
d86c00f0
Message:

cpp: refactor future to avoid code duplication, fix wait_for and wait_until, synchronization and compilation

Location:
uspace/lib/cpp/include/__bits/thread
Files:
1 added
2 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/cpp/include/__bits/thread/future.hpp

    rd86c00f0 r6e97265  
    3030#define LIBCPP_BITS_THREAD_FUTURE
    3131
     32#include <__bits/thread/future_common.hpp>
     33#include <__bits/thread/shared_state.hpp>
     34#include <__bits/utility/forward_move.hpp>
    3235#include <cassert>
    33 #include <memory>
    34 #include <system_error>
    35 #include <thread>
    36 #include <tuple>
    37 #include <type_traits>
    38 #include <utility>
    3936
    4037namespace std
    4138{
    4239    /**
    43      * 30.6, futures:
    44      */
    45 
    46     enum class future_errc
    47     { // The 5001 start is to not collide with system_error's codes.
    48         broken_promise = 5001,
    49         future_already_retrieved,
    50         promise_already_satisfied,
    51         no_state
    52     };
    53 
    54     enum class future_status
    55     {
    56         ready,
    57         timeout,
    58         deferred
    59     };
    60 
    61     /**
    62      * 30.6.2, error handling:
    63      */
    64 
    65     template<>
    66     struct is_error_code_enum<future_errc>: true_type
    67     { /* DUMMY BODY */ };
    68 
    69     error_code make_error_code(future_errc) noexcept;
    70     error_condition make_error_condition(future_errc) noexcept;
    71 
    72     const error_category& future_category() noexcept;
    73 
    74     /**
    75      * 30.6.3, class future_error:
    76      */
    77 
    78     class future_error: public logic_error
    79     {
    80         public:
    81             future_error(error_code ec);
    82 
    83             const error_code& code() const noexcept;
    84             const char* what() const noexcept;
    85 
    86         private:
    87             error_code code_;
    88     };
    89 
    90     /**
    9140     * 30.6.6, class template future:
    9241     */
    9342
     43    namespace aux
     44    {
     45        template<class R>
     46        class future_base
     47        {
     48            public:
     49                future_base() noexcept
     50                    : state_{nullptr}
     51                { /* DUMMY BODY */ }
     52
     53                future_base(const future_base&) = delete;
     54
     55                future_base(future_base&& rhs) noexcept
     56                    : state_{std::move(rhs.state_)}
     57                {
     58                    rhs.state_ = nullptr;
     59                }
     60
     61                future_base(aux::shared_state<R>* state)
     62                    : state_{state}
     63                {
     64                    /**
     65                     * Note: This is a custom non-standard constructor that allows
     66                     *       us to create a future directly from a shared state. This
     67                     *       should never be a problem as aux::shared_state is a private
     68                     *       type and future has no constructor templates.
     69                     */
     70                }
     71
     72                virtual ~future_base()
     73                {
     74                    release_state_();
     75                }
     76
     77                future_base& operator=(const future_base&) = delete;
     78
     79                future_base& operator=(future_base&& rhs) noexcept
     80                {
     81                    release_state_();
     82                    state_ = std::move(rhs.state_);
     83                    rhs.state_ = nullptr;
     84                }
     85
     86                bool valid() const noexcept
     87                {
     88                    return state_ != nullptr;
     89                }
     90
     91                void wait() const noexcept
     92                {
     93                    assert(state_);
     94
     95                    state_->wait();
     96                }
     97
     98                template<class Rep, class Period>
     99                future_status
     100                wait_for(const chrono::duration<Rep, Period>& rel_time) const
     101                {
     102                    assert(state_);
     103
     104                    return state_->wait_for(rel_time);
     105                }
     106
     107                template<class Clock, class Duration>
     108                future_status
     109                wait_until(
     110                    const chrono::time_point<Clock, Duration>& abs_time
     111                ) const
     112                {
     113                    assert(state_);
     114
     115                    return state_->wait_until(abs_time);
     116                }
     117
     118            protected:
     119                void release_state_()
     120                {
     121                    if (!state_)
     122                        return;
     123
     124                    /**
     125                     * Note: This is the 'release' move described in
     126                     *       30.6.4 (5).
     127                     * Last reference to state -> destroy state.
     128                     * Decrement refcount of state otherwise.
     129                     * Will not block, unless all following hold:
     130                     *  1) State was created by call to std::async.
     131                     *  2) State is not yet ready.
     132                     *  3) This was the last reference to the shared state.
     133                     */
     134                    if (state_->decrement())
     135                    {
     136                        /**
     137                         * The destroy call handles the special case
     138                         * when 1) - 3) hold.
     139                         */
     140                        state_->destroy();
     141                        delete state_;
     142                        state_ = nullptr;
     143                    }
     144                }
     145
     146                aux::shared_state<R>* state_;
     147        };
     148    }
     149
    94150    template<class R>
    95151    class shared_future;
    96152
    97153    template<class R>
    98     class future
     154    class future: public aux::future_base<R>
    99155    {
    100156        public:
    101157            future() noexcept
    102                 : state_{nullptr}
     158                : aux::future_base<R>{}
    103159            { /* DUMMY BODY */ }
    104160
     
    106162
    107163            future(future&& rhs) noexcept
    108                 : state_{std::move(rhs.state_)}
    109             {
    110                 rhs.state_ = nullptr;
    111             }
     164                : aux::future_base<R>{std::move(rhs.state_)}
     165            { /* DUMMY BODY */ }
    112166
    113167            future(aux::shared_state<R>* state)
    114                 : state_{state}
    115             {
    116                 /**
    117                  * Note: This is a custom non-standard constructor that allows
    118                  *       us to create a future directly from a shared state. This
    119                  *       should never be a problem as aux::shared_state is a private
    120                  *       type and future has no constructor templates.
    121                  */
    122             }
    123 
    124             ~future()
    125             {
    126                 release_state_();
    127             }
    128 
    129             future& operator=(const future) = delete;
     168                : aux::future_base<R>{state}
     169            { /* DUMMY BODY */ }
     170
     171            future& operator=(const future&) = delete;
    130172
    131173            future& operator=(future&& rhs) noexcept
    132174            {
    133                 release_state_();
    134                 state_ = std::move(rhs.state_);
    135                 rhs.state_ = nullptr;
     175                return aux::future_base<R>::operator=(std::move(rhs));
    136176            }
    137177
     
    143183            R get()
    144184            {
    145                 assert(state_);
    146 
    147                 wait();
    148 
    149                 if (state_->has_exception())
    150                     state_->throw_stored_exception();
    151                 auto res = std::move(state_->get());
    152 
    153                 release_state_();
     185                assert(this->state_);
     186
     187                this->wait();
     188
     189                if (this->state_->has_exception())
     190                    this->state_->throw_stored_exception();
     191                auto res = std::move(this->state_->get());
     192
     193                this->release_state_();
    154194
    155195                return res;
    156196            }
    157 
    158             bool valid() const noexcept
    159             {
    160                 return state_ != nullptr;
    161             }
    162 
    163             void wait() const noexcept
    164             {
    165                 assert(state_);
    166 
    167                 state_->wait();
    168             }
    169 
    170             template<class Rep, class Period>
    171             future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const
    172             {
    173                 assert(state_);
    174 
    175                 return state_->wait_for(rel_time);
    176             }
    177 
    178             template<class Clock, class Duration>
    179             future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const
    180             {
    181                 assert(state_);
    182 
    183                 return state_->wait_until(abs_time);
    184             }
    185 
    186         private:
    187             void release_state_()
    188             {
    189                 if (!state_)
    190                     return;
    191 
    192                 /**
    193                  * Note: This is the 'release' move described in
    194                  *       30.6.4 (5).
    195                  * Last reference to state -> destroy state.
    196                  * Decrement refcount of state otherwise.
    197                  * Will not block, unless all following hold:
    198                  *  1) State was created by call to std::async.
    199                  *  2) State is not yet ready.
    200                  *  3) This was the last reference to the shared state.
    201                  */
    202                 if (state_->decrement())
    203                 {
    204                     /**
    205                      * The destroy call handles the special case
    206                      * when 1) - 3) hold.
    207                      */
    208                     state_->destroy();
    209                     delete state_;
    210                     state_ = nullptr;
    211                 }
    212             }
    213 
    214             aux::shared_state<R>* state_;
    215197    };
    216198
  • uspace/lib/cpp/include/__bits/thread/shared_state.hpp

    rd86c00f0 r6e97265  
    3434 */
    3535
     36#include <__bits/exception.hpp>
    3637#include <__bits/functional/function.hpp>
    3738#include <__bits/functional/invoke.hpp>
    3839#include <__bits/refcount_obj.hpp>
     40#include <__bits/thread/future_common.hpp>
    3941#include <__bits/thread/threading.hpp>
     42#include <cerrno>
     43#include <thread>
     44#include <tuple>
     45
     46namespace std
     47{
     48    enum class future_status;
     49}
    4050
    4151namespace std::aux
     
    137147
    138148            template<class Rep, class Period>
    139             virtual future_status
     149            future_status
    140150            wait_for(const chrono::duration<Rep, Period>& rel_time)
    141151            {
    142152                aux::threading::mutex::lock(mutex_);
    143                 auto res = aux::threading::condvar::wait_for(
    144                     condvar_, mutex_,
     153                auto res = timed_wait_(
    145154                    aux::threading::time::convert(rel_time)
    146155                );
    147156                aux::threading::mutex::unlock(mutex_);
    148157
     158                return res;
     159            }
     160
     161            template<class Clock, class Duration>
     162            future_status
     163            wait_until(const chrono::time_point<Clock, Duration>& abs_time)
     164            {
     165                aux::threading::mutex::lock(mutex_);
     166                auto res = timed_wait_(
     167                    aux::threading::time(convert(abs_time - Clock::now()))
     168                );
     169                aux::threading::mutex::unlock(mutex_);
     170
     171                return res;
     172            }
     173
     174            ~shared_state() override = default;
     175
     176        protected:
     177            aux::mutex_t mutex_;
     178            aux::condvar_t condvar_;
     179
     180            R value_;
     181            bool value_set_;
     182
     183            exception_ptr exception_;
     184            bool has_exception_;
     185
     186            /**
     187             * Note: wait_for and wait_until are templates and as such
     188             *       cannot be virtual and overriden by the deferred_ and
     189             *       async_ children. However, we are using aux::time_unit_t
     190             *       in the end anyway, so we can work around that
     191             *       by using the 'template method' design pattern
     192             *       (i.e. by providing a virtual function called by these
     193             *       templates and then overriding that function in the
     194             *       children).
     195             */
     196            virtual future_status timed_wait_(aux::time_unit_t time)
     197            {
     198                auto res = aux::threading::condvar::wait_for(
     199                    condvar_, mutex_, time
     200                );
     201
    149202                return res == ETIMEOUT ? future_status::timeout
    150203                                       : future_status::ready;
    151204            }
    152 
    153             template<class Clock, class Duration>
    154             virtual future_status
    155             wait_until(const chrono::time_point<Clock, Duration>& abs_time)
    156             {
    157                 aux::threading::mutex::lock(mutex_);
    158                 auto res = aux::threading::condvar::wait_for(
    159                     condvar_, mutex_,
    160                     aux::threading::time::convert(abs_time - Clock::now())
    161                 );
    162                 aux::threading::mutex::unlock(mutex_);
    163 
    164                 return res == ETIMEOUT ? future_status::timeout
    165                                        : future_status::ready;
    166             }
    167 
    168             ~shared_state() override = default;
    169 
    170         private:
    171             aux::mutex_t mutex_;
    172             aux::condvar_t condvar_;
    173 
    174             R value_;
    175             bool value_set_;
    176 
    177             exception_ptr exception_;
    178             bool has_exception_;
    179205    };
    180206
     
    224250            }
    225251
    226             template<class Rep, class Period>
    227             future_status
    228             wait_for(const chrono::duration<Rep, Period>&) override
     252            ~async_shared_state() override
     253            {
     254                destroy();
     255            }
     256
     257        protected:
     258            future_status timed_wait_(aux::time_unit_t) override
    229259            {
    230260                // TODO: have to sleep and check
    231                 return future_status::ready;
    232             }
    233 
    234             template<class Clock, class Duration>
    235             future_status
    236             wait_until(const chrono::time_point<Clock, Duration>&) override
    237             {
    238                 // TODO: have to sleep and check
    239                 return future_status::ready;
    240             }
    241 
    242             ~async_shared_state() override
    243             {
    244                 destroy();
     261                return future_status::timeout;
    245262            }
    246263
     
    261278            void destroy() override
    262279            {
    263                 aux::threading::mutex::lock(mutex_);
     280                aux::threading::mutex::lock(this->mutex_);
    264281                if (!this->is_set())
    265282                    invoke_(make_index_sequence<sizeof...(Args)>{});
    266                 aux::threading::mutex::unlock(mutex_);
     283                aux::threading::mutex::unlock(this->mutex_);
    267284            }
    268285
    269286            void wait() override
    270287            {
    271                 aux::threading::mutex::lock(mutex_);
     288                /**
     289                 * Note: Synchronization done in invoke_ -> set_value.
     290                 */
    272291                if (!this->is_set())
    273292                    invoke_(make_index_sequence<sizeof...(Args)>{});
    274                 aux::threading::mutex::unlock(mutex_);
    275             }
    276 
    277             template<class Rep, class Period>
    278             future_status
    279             wait_for(const chrono::duration<Rep, Period>&) override
    280             {
    281                 /**
    282                  * Note: Neither of the wait_ functions has any effect
    283                  *       for deferred functions spawned by async (which
    284                  *       are the only users of this state type).
    285                  */
    286                 return future_status::deferred;
    287             }
    288 
    289             template<class Clock, class Duration>
    290             future_status
    291             wait_until(const chrono::time_point<Clock, Duration>&) override
    292             {
    293                 return future_status::deferred;
    294293            }
    295294
     
    299298            }
    300299
    301         private:
     300        protected:
    302301            function<R(decay_t<Args>...)> func_;
    303302            tuple<decay_t<Args>...> args_;
     
    315314                }
    316315            }
     316
     317            future_status timed_wait_(aux::time_unit_t) override
     318            {
     319                /**
     320                 * Note: Neither of the wait_ functions has any effect
     321                 *       for deferred functions spawned by async (which
     322                 *       are the only users of this state type).
     323                 */
     324                return future_status::deferred;
     325            }
    317326    };
    318327}
Note: See TracChangeset for help on using the changeset viewer.