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

Changeset 04c0fc5 in mainline


Ignore:
Timestamp:
2019-06-27T15:55:09Z (16 months ago)
Author:
Jaroslav Jindrak <dzejrou@…>
Branches:
master
Children:
9eea0b7
Parents:
8add15e0
Message:

cpp: add basic implementations of the promise and future templates that support simple value retrieval in single threaded context in the least

Location:
uspace/lib/cpp
Files:
2 edited

Legend:

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

    r8add15e0 r04c0fc5  
    3030#define LIBCPP_BITS_THREAD_FUTURE
    3131
     32#include <__bits/refcount_obj.hpp>
     33#include <__bits/thread/threading.hpp>
    3234#include <cassert>
    3335#include <memory>
    3436#include <system_error>
    3537#include <type_traits>
     38#include <utility>
    3639
    3740namespace std
     
    8588
    8689            const error_code& code() const noexcept;
     90            const char* what() const noexcept;
    8791
    8892        private:
     
    9498     */
    9599
     100    namespace aux
     101    {
     102        template<class R>
     103        class shared_state: public aux::refcount_obj
     104        {
     105            public:
     106                const bool is_deferred_function;
     107
     108                shared_state(bool is_deferred = false)
     109                    : is_deferred_function{is_deferred}, mutex_{},
     110                      condvar_{}, value_{}, value_set_{false},
     111                      exception_{}, has_exception_{false}
     112                {
     113                    threading::mutex::init(mutex_);
     114                    threading::condvar::init(condvar_);
     115                }
     116
     117                void destroy() override
     118                {
     119                    if (this->refs() < 1)
     120                    {
     121                        // TODO: what to destroy? just this?
     122                    }
     123                }
     124
     125                void set_value(const R& val, bool set)
     126                {
     127                    value_ = val;
     128                    value_set_ = set;
     129                }
     130
     131                void set_value(R&& val, bool set)
     132                {
     133                    aux::threading::mutex::lock(mutex_);
     134                    value_ = std::move(val);
     135                    value_set_ = set;
     136                    aux::threading::mutex::unlock(mutex_);
     137
     138                    aux::threading::condvar::broadcast(condvar_);
     139                }
     140
     141                void set_set(bool set = true) noexcept
     142                {
     143                    value_set_ = set;
     144                }
     145
     146                bool is_set() const noexcept
     147                {
     148                    return value_set_;
     149                }
     150
     151                R& get()
     152                {
     153                    return value_;
     154                }
     155
     156                void set_exception(exception_ptr ptr)
     157                {
     158                    exception_ = ptr;
     159                    has_exception_ = true;
     160                }
     161
     162                bool has_exception() const noexcept
     163                {
     164                    return has_exception_;
     165                }
     166
     167                void throw_stored_exception() const
     168                {
     169                    // TODO: implement
     170                }
     171
     172                /**
     173                 * TODO: This member function is supposed to be marked
     174                 *       as 'const'. In such a case, however, we cannot
     175                 *       use the underlying fibril API because these
     176                 *       references get converted to pointers and the API
     177                 *       does not accept e.g. 'const fibril_condvar_t*'.
     178                 *
     179                 *       The same applies to the wait_for and wait_until
     180                 *       functions.
     181                 */
     182                void wait()
     183                {
     184                    aux::threading::mutex::lock(mutex_);
     185                    while (!value_set_)
     186                        aux::threading::condvar::wait(condvar_, mutex_);
     187                    aux::threading::mutex::unlock(mutex_);
     188                }
     189
     190                template<class Rep, class Period>
     191                bool wait_for(const chrono::duration<Rep, Period>& rel_time)
     192                {
     193                    aux::threading::mutex::lock(mutex_);
     194                    aux::threading::condvar::wait_for(
     195                        condvar_, mutex_,
     196                        aux::threading::time::convert(rel_time)
     197                    );
     198                    aux::threading::mutex::unlock(mutex_);
     199
     200                    return value_set_;
     201                }
     202
     203                template<class Clock, class Duration>
     204                bool wait_until(const chrono::time_point<Clock, Duration>& abs_time)
     205                {
     206                    aux::threading::mutex::lock(mutex_);
     207                    aux::threading::condvar::wait_for(
     208                        condvar_, mutex_,
     209                        aux::threading::time::convert(abs_time - Clock::now())
     210                    );
     211                    aux::threading::mutex::unlock(mutex_);
     212
     213                    return value_set_;
     214                }
     215
     216                ~shared_state()
     217                {
     218                    // TODO: just destroy?
     219                }
     220
     221            private:
     222                aux::mutex_t mutex_;
     223                aux::condvar_t condvar_;
     224
     225                R value_;
     226                bool value_set_;
     227
     228                exception_ptr exception_;
     229                bool has_exception_;
     230        };
     231    }
     232
     233    template<class R>
     234    class future;
     235
    96236    template<class R>
    97237    class promise
    98238    {
     239        public:
     240            promise()
     241                : state_{new aux::shared_state<R>{}}
     242            { /* DUMMY BODY */ }
     243
     244            template<class Allocator>
     245            promise(allocator_arg_t, const Allocator& a)
     246                : promise{}
     247            {
     248                // TODO: Use the allocator.
     249            }
     250
     251            promise(promise&& rhs) noexcept
     252                : state_{}
     253            {
     254                state_ = rhs.state_;
     255                rhs.state_ = nullptr;
     256            }
     257
     258            promise(const promise&) = delete;
     259
     260            ~promise()
     261            {
     262                abandon_state_();
     263            }
     264
     265            promise& operator=(promise&& rhs) noexcept
     266            {
     267                abandon_state_();
     268                promise{std::move(rhs)}.swap(*this);
     269            }
     270
     271            promise& operator=(const promise&) = delete;
     272
     273            void swap(promise& other) noexcept
     274            {
     275                std::swap(state_, other.state_);
     276            }
     277
     278            future<R> get_future()
     279            {
     280                return future<R>{state_};
     281            }
     282
     283            void set_value(const R& val)
     284            {
     285                if (!state_)
     286                    throw future_error{make_error_code(future_errc::no_state)};
     287                if (state_->is_set())
     288                {
     289                    throw future_error{
     290                        make_error_code(future_errc::promise_already_satisfied)
     291                    };
     292                }
     293
     294                state_->set_value(val, true);
     295            }
     296
     297            void set_value(R&& val)
     298            {
     299                if (!state_)
     300                    throw future_error{make_error_code(future_errc::no_state)};
     301                if (state_->is_set())
     302                {
     303                    throw future_error{
     304                        make_error_code(future_errc::promise_already_satisfied)
     305                    };
     306                }
     307
     308                state_->set_value(std::forward<R>(val), true);
     309            }
     310
     311            void set_exception(exception_ptr ptr)
     312            {
     313                assert(state_);
     314
     315                state_->set_exception(ptr);
     316            }
     317
     318            void set_value_at_thread_exit(const R& val)
     319            {
     320                if (!state_)
     321                    throw future_error{make_error_code(future_errc::no_state)};
     322                if (state_->is_set())
     323                {
     324                    throw future_error{
     325                        make_error_code(future_errc::promise_already_satisfied)
     326                    };
     327                }
     328
     329                state_->set_value(val, false);
     330                // TODO: schedule it to be set as ready when thread exits
     331            }
     332
     333            void set_value_at_thread_exit(R&& val)
     334            {
     335                if (!state_)
     336                    throw future_error{make_error_code(future_errc::no_state)};
     337                if (state_->is_set())
     338                {
     339                    throw future_error{
     340                        make_error_code(future_errc::promise_already_satisfied)
     341                    };
     342                }
     343
     344                state_->set_value(std::forward<R>(val), false);
     345                // TODO: schedule it to be set as ready when thread exits
     346            }
     347
     348            void set_exception_at_thread_exit(exception_ptr)
     349            {
     350                // TODO: No exception handling, no-op at this time.
     351            }
     352
     353        private:
     354            void abandon_state_()
     355            {
     356                /**
     357                 * 1) If state is not ready:
     358                 *   a) Store exception of type future_error with
     359                 *      error condition broken_promise.
     360                 *   b) Mark state as ready.
     361                 * 2) Rekease the state.
     362                 */
     363            }
     364
     365            aux::shared_state<R>* state_;
    99366    };
    100367
     
    102369    class promise<R&>
    103370    {
     371        // TODO: Copy & modify once promise is done.
    104372    };
    105373
     
    107375    class promise<void>
    108376    {
     377        // TODO: Copy & modify once promise is done.
    109378    };
    110379
     
    120389
    121390    template<class R>
     391    class shared_future;
     392
     393    template<class R>
    122394    class future
    123395    {
     396        public:
     397            future() noexcept
     398                : state_{nullptr}
     399            { /* DUMMY BODY */ }
     400
     401            future(const future&) = delete;
     402
     403            future(future&& rhs) noexcept
     404                : state_{std::move(rhs.state_)}
     405            {
     406                rhs.state_ = nullptr;
     407            }
     408
     409            future(aux::shared_state<R>* state)
     410                : state_{state}
     411            {
     412                /**
     413                 * Note: This is a custom non-standard constructor that allows
     414                 *       us to create a future directly from a shared state. This
     415                 *       should never be a problem as aux::shared_state is a private
     416                 *       type and future has no constructor templates.
     417                 */
     418            }
     419
     420            ~future()
     421            {
     422                release_state_();
     423            }
     424
     425            future& operator=(const future) = delete;
     426
     427            future& operator=(future&& rhs) noexcept
     428            {
     429                release_state_();
     430                state_ = std::move(rhs.state_);
     431                rhs.state_ = nullptr;
     432            }
     433
     434            shared_future<R> share()
     435            {
     436                return shared_future<R>(std::move(*this));
     437            }
     438
     439            R get()
     440            {
     441                assert(state_);
     442
     443                wait();
     444
     445                auto state = state_;
     446                state_ = nullptr;
     447                if (state->has_exception())
     448                    state->throw_stored_exception();
     449
     450                return std::move(state->get());
     451            }
     452
     453            bool valid() const noexcept
     454            {
     455                return state_ != nullptr;
     456            }
     457
     458            void wait() const noexcept
     459            {
     460                assert(state_);
     461
     462                state_->wait();
     463            }
     464
     465            template<class Rep, class Period>
     466            future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const
     467            {
     468                assert(state_);
     469                if (state_->is_deffered_function)
     470                    return future_status::deferred;
     471
     472                auto res = state_->wait_for(rel_time);
     473
     474                if (res)
     475                    return future_status::ready;
     476                else
     477                    return future_status::timeout;
     478            }
     479
     480            template<class Clock, class Duration>
     481            future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const
     482            {
     483                assert(state_);
     484                if (state_->is_deffered_function)
     485                    return future_status::deferred;
     486
     487                auto res = state_->wait_until(abs_time);
     488
     489                if (res)
     490                    return future_status::ready;
     491                else
     492                    return future_status::timeout;
     493            }
     494
     495        private:
     496            void release_state_()
     497            {
     498                /**
     499                 * Last reference to state -> destroy state.
     500                 * Decrement refcount of state otherwise.
     501                 * Will not block, unless all following hold:
     502                 *  1) State was created by call to std::async.
     503                 *  2) State is not yet ready.
     504                 *  3) This was the last reference to the shared state.
     505                 */
     506            }
     507
     508            aux::shared_state<R>* state_;
    124509    };
    125510
     
    127512    class future<R&>
    128513    {
     514        // TODO: Copy & modify once future is done.
    129515    };
    130516
     
    132518    class future<void>
    133519    {
    134     };
    135 
     520        // TODO: Copy & modify once future is done.
     521    };
     522
     523    // TODO: Make sure the move constructor of shared_future
     524    //       invalidates the state (i.e. sets to nullptr).
    136525    template<class R>
    137526    class shared_future
    138527    {
     528        // TODO: Copy & modify once future is done.
    139529    };
    140530
     
    142532    class shared_future<R&>
    143533    {
     534        // TODO: Copy & modify once future is done.
    144535    };
    145536
     
    147538    class shared_future<void>
    148539    {
     540        // TODO: Copy & modify once future is done.
    149541    };
    150542
     
    155547    class packaged_task<R(Args...)>
    156548    {
     549        packaged_task() noexcept
     550        {}
     551
     552        template<class F>
     553        explicit packaged_task(F&& f)
     554        {}
     555
     556        template<class F, class Allocator>
     557        explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f)
     558        {}
     559
     560        ~packaged_task()
     561        {}
     562
     563        packaged_task(const packaged_task&) = delete;
     564        packaged_task& operator=(const packaged_task&) = delete;
     565
     566        packaged_task(packaged_task&& rhs)
     567        {}
     568
     569        packaged_task& operator=(packaged_task&& rhs)
     570        {}
     571
     572        void swap(packaged_task& other) noexcept
     573        {}
     574
     575        bool valid() const noexcept
     576        {}
     577
     578        future<R> get_future()
     579        {}
     580
     581        void operator()(Args...)
     582        {}
     583
     584        void make_ready_at_thread_exit(Args...)
     585        {}
     586
     587        void reset()
     588        {}
    157589    };
    158590
  • uspace/lib/cpp/src/future.cpp

    r8add15e0 r04c0fc5  
    8181        return code_;
    8282    }
     83
     84    const char* future_error::what() const noexcept
     85    {
     86        return code().message().c_str();
     87    }
    8388}
Note: See TracChangeset for help on using the changeset viewer.