| 1 | /*
|
|---|
| 2 | * Copyright (c) 2019 Jaroslav Jindrak
|
|---|
| 3 | * All rights reserved.
|
|---|
| 4 | *
|
|---|
| 5 | * Redistribution and use in source and binary forms, with or without
|
|---|
| 6 | * modification, are permitted provided that the following conditions
|
|---|
| 7 | * are met:
|
|---|
| 8 | *
|
|---|
| 9 | * - Redistributions of source code must retain the above copyright
|
|---|
| 10 | * notice, this list of conditions and the following disclaimer.
|
|---|
| 11 | * - Redistributions in binary form must reproduce the above copyright
|
|---|
| 12 | * notice, this list of conditions and the following disclaimer in the
|
|---|
| 13 | * documentation and/or other materials provided with the distribution.
|
|---|
| 14 | * - The name of the author may not be used to endorse or promote products
|
|---|
| 15 | * derived from this software without specific prior written permission.
|
|---|
| 16 | *
|
|---|
| 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|---|
| 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|---|
| 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|---|
| 20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|---|
| 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|---|
| 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|---|
| 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|---|
| 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|---|
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|---|
| 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|---|
| 27 | */
|
|---|
| 28 |
|
|---|
| 29 | #ifndef LIBCPP_BITS_THREAD_SHARED_STATE
|
|---|
| 30 | #define LIBCPP_BITS_THREAD_SHARED_STATE
|
|---|
| 31 |
|
|---|
| 32 | /**
|
|---|
| 33 | * 30.6.4, shared state:
|
|---|
| 34 | */
|
|---|
| 35 |
|
|---|
| 36 | #include <__bits/exception.hpp>
|
|---|
| 37 | #include <__bits/functional/function.hpp>
|
|---|
| 38 | #include <__bits/functional/invoke.hpp>
|
|---|
| 39 | #include <__bits/refcount_obj.hpp>
|
|---|
| 40 | #include <__bits/thread/future_common.hpp>
|
|---|
| 41 | #include <__bits/thread/threading.hpp>
|
|---|
| 42 | #include <cerrno>
|
|---|
| 43 | #include <thread>
|
|---|
| 44 | #include <tuple>
|
|---|
| 45 |
|
|---|
| 46 | namespace std::aux
|
|---|
| 47 | {
|
|---|
| 48 | class shared_state_base: public aux::refcount_obj
|
|---|
| 49 | {
|
|---|
| 50 | public:
|
|---|
| 51 | shared_state_base()
|
|---|
| 52 | : mutex_{}, condvar_{}, value_set_{false},
|
|---|
| 53 | exception_{}, has_exception_{false}
|
|---|
| 54 | {
|
|---|
| 55 | threading::mutex::init(mutex_);
|
|---|
| 56 | threading::condvar::init(condvar_);
|
|---|
| 57 | }
|
|---|
| 58 |
|
|---|
| 59 | void destroy() override
|
|---|
| 60 | {
|
|---|
| 61 | /**
|
|---|
| 62 | * Note: No need to act in this case, async shared
|
|---|
| 63 | * state is the object that needs to sometimes
|
|---|
| 64 | * invoke its payload.
|
|---|
| 65 | */
|
|---|
| 66 | }
|
|---|
| 67 |
|
|---|
| 68 | void mark_set(bool set = true) noexcept
|
|---|
| 69 | {
|
|---|
| 70 | value_set_ = set;
|
|---|
| 71 | }
|
|---|
| 72 |
|
|---|
| 73 | bool is_set() const noexcept
|
|---|
| 74 | {
|
|---|
| 75 | return value_set_;
|
|---|
| 76 | }
|
|---|
| 77 |
|
|---|
| 78 | void set_exception(exception_ptr ptr)
|
|---|
| 79 | {
|
|---|
| 80 | exception_ = ptr;
|
|---|
| 81 | has_exception_ = true;
|
|---|
| 82 | }
|
|---|
| 83 |
|
|---|
| 84 | bool has_exception() const noexcept
|
|---|
| 85 | {
|
|---|
| 86 | return has_exception_;
|
|---|
| 87 | }
|
|---|
| 88 |
|
|---|
| 89 | void throw_stored_exception() const
|
|---|
| 90 | {
|
|---|
| 91 | // TODO: implement
|
|---|
| 92 | }
|
|---|
| 93 |
|
|---|
| 94 | /**
|
|---|
| 95 | * TODO: This member function is supposed to be marked
|
|---|
| 96 | * as 'const'. In such a case, however, we cannot
|
|---|
| 97 | * use the underlying fibril API because these
|
|---|
| 98 | * references get converted to pointers and the API
|
|---|
| 99 | * does not accept e.g. 'const fibril_condvar_t*'.
|
|---|
| 100 | *
|
|---|
| 101 | * The same applies to the wait_for and wait_until
|
|---|
| 102 | * functions.
|
|---|
| 103 | */
|
|---|
| 104 | virtual void wait()
|
|---|
| 105 | {
|
|---|
| 106 | aux::threading::mutex::lock(mutex_);
|
|---|
| 107 | while (!value_set_)
|
|---|
| 108 | aux::threading::condvar::wait(condvar_, mutex_);
|
|---|
| 109 | aux::threading::mutex::unlock(mutex_);
|
|---|
| 110 | }
|
|---|
| 111 |
|
|---|
| 112 | template<class Rep, class Period>
|
|---|
| 113 | future_status
|
|---|
| 114 | wait_for(const chrono::duration<Rep, Period>& rel_time)
|
|---|
| 115 | {
|
|---|
| 116 | aux::threading::mutex::lock(mutex_);
|
|---|
| 117 | auto res = timed_wait_(
|
|---|
| 118 | aux::threading::time::convert(rel_time)
|
|---|
| 119 | );
|
|---|
| 120 | aux::threading::mutex::unlock(mutex_);
|
|---|
| 121 |
|
|---|
| 122 | return res;
|
|---|
| 123 | }
|
|---|
| 124 |
|
|---|
| 125 | template<class Clock, class Duration>
|
|---|
| 126 | future_status
|
|---|
| 127 | wait_until(const chrono::time_point<Clock, Duration>& abs_time)
|
|---|
| 128 | {
|
|---|
| 129 | aux::threading::mutex::lock(mutex_);
|
|---|
| 130 | auto res = timed_wait_(
|
|---|
| 131 | aux::threading::time::convert(abs_time - Clock::now())
|
|---|
| 132 | );
|
|---|
| 133 | aux::threading::mutex::unlock(mutex_);
|
|---|
| 134 |
|
|---|
| 135 | return res;
|
|---|
| 136 | }
|
|---|
| 137 |
|
|---|
| 138 | ~shared_state_base() override = default;
|
|---|
| 139 |
|
|---|
| 140 | protected:
|
|---|
| 141 | aux::mutex_t mutex_;
|
|---|
| 142 | aux::condvar_t condvar_;
|
|---|
| 143 |
|
|---|
| 144 | bool value_set_;
|
|---|
| 145 |
|
|---|
| 146 | exception_ptr exception_;
|
|---|
| 147 | bool has_exception_;
|
|---|
| 148 |
|
|---|
| 149 | /**
|
|---|
| 150 | * Note: wait_for and wait_until are templates and as such
|
|---|
| 151 | * cannot be virtual and overriden by the deferred_ and
|
|---|
| 152 | * async_ children. However, we are using aux::time_unit_t
|
|---|
| 153 | * in the end anyway, so we can work around that
|
|---|
| 154 | * by using the 'template method' design pattern
|
|---|
| 155 | * (i.e. by providing a virtual function called by these
|
|---|
| 156 | * templates and then overriding that function in the
|
|---|
| 157 | * children).
|
|---|
| 158 | */
|
|---|
| 159 | virtual future_status timed_wait_(aux::time_unit_t time)
|
|---|
| 160 | {
|
|---|
| 161 | auto res = aux::threading::condvar::wait_for(
|
|---|
| 162 | condvar_, mutex_, time
|
|---|
| 163 | );
|
|---|
| 164 |
|
|---|
| 165 | return res == ETIMEOUT ? future_status::timeout
|
|---|
| 166 | : future_status::ready;
|
|---|
| 167 | }
|
|---|
| 168 | };
|
|---|
| 169 |
|
|---|
| 170 | template<class R>
|
|---|
| 171 | class shared_state: public shared_state_base
|
|---|
| 172 | {
|
|---|
| 173 | public:
|
|---|
| 174 | shared_state()
|
|---|
| 175 | : shared_state_base{}
|
|---|
| 176 | { /* DUMMY BODY */ }
|
|---|
| 177 |
|
|---|
| 178 | void set_value(const R& val, bool set)
|
|---|
| 179 | {
|
|---|
| 180 | /**
|
|---|
| 181 | * Note: This is the 'mark ready' move described
|
|---|
| 182 | * in 30.6.4 (6).
|
|---|
| 183 | */
|
|---|
| 184 |
|
|---|
| 185 | aux::threading::mutex::lock(mutex_);
|
|---|
| 186 | value_ = val;
|
|---|
| 187 | value_set_ = set;
|
|---|
| 188 | aux::threading::mutex::unlock(mutex_);
|
|---|
| 189 |
|
|---|
| 190 | aux::threading::condvar::broadcast(condvar_);
|
|---|
| 191 | }
|
|---|
| 192 |
|
|---|
| 193 | void set_value(R&& val, bool set = true)
|
|---|
| 194 | {
|
|---|
| 195 | aux::threading::mutex::lock(mutex_);
|
|---|
| 196 | value_ = std::move(val);
|
|---|
| 197 | value_set_ = set;
|
|---|
| 198 | aux::threading::mutex::unlock(mutex_);
|
|---|
| 199 |
|
|---|
| 200 | aux::threading::condvar::broadcast(condvar_);
|
|---|
| 201 | }
|
|---|
| 202 |
|
|---|
| 203 | R& get()
|
|---|
| 204 | {
|
|---|
| 205 | return value_;
|
|---|
| 206 | }
|
|---|
| 207 |
|
|---|
| 208 | protected:
|
|---|
| 209 | R value_;
|
|---|
| 210 | };
|
|---|
| 211 |
|
|---|
| 212 | template<>
|
|---|
| 213 | class shared_state<void>: public shared_state_base
|
|---|
| 214 | {
|
|---|
| 215 | public:
|
|---|
| 216 | shared_state()
|
|---|
| 217 | : shared_state_base{}
|
|---|
| 218 | { /* DUMMY BODY */ }
|
|---|
| 219 |
|
|---|
| 220 | void set_value()
|
|---|
| 221 | {
|
|---|
| 222 | value_set_ = true;
|
|---|
| 223 | aux::threading::condvar::broadcast(condvar_);
|
|---|
| 224 | }
|
|---|
| 225 |
|
|---|
| 226 | void get()
|
|---|
| 227 | { /* DUMMY BODY */ }
|
|---|
| 228 | };
|
|---|
| 229 |
|
|---|
| 230 | /**
|
|---|
| 231 | * We could make one state for both async and
|
|---|
| 232 | * deferred policies, but then we would be wasting
|
|---|
| 233 | * memory and the only benefit would be the ability
|
|---|
| 234 | * for additional implementation defined policies done
|
|---|
| 235 | * directly in that state (as opposed to making new
|
|---|
| 236 | * states for them).
|
|---|
| 237 | *
|
|---|
| 238 | * But since we have no plan (nor need) to make those,
|
|---|
| 239 | * this approach seems to be the best one.
|
|---|
| 240 | *
|
|---|
| 241 | * Also note that unlike the parent class shared_state,
|
|---|
| 242 | * we do not need to specialize these for void. This is because
|
|---|
| 243 | * the difference in class contents are handled by the parent
|
|---|
| 244 | * specialization and setting the value can be handled easily
|
|---|
| 245 | * by if constexpr and checking for the equivalence of the
|
|---|
| 246 | * R template parameter and void.
|
|---|
| 247 | */
|
|---|
| 248 |
|
|---|
| 249 | template<class R, class F, class... Args>
|
|---|
| 250 | class async_shared_state: public shared_state<R>
|
|---|
| 251 | {
|
|---|
| 252 | public:
|
|---|
| 253 | async_shared_state(F&& f, Args&&... args)
|
|---|
| 254 | : shared_state<R>{}, thread_{}
|
|---|
| 255 | {
|
|---|
| 256 | thread_ = thread{
|
|---|
| 257 | [=](){
|
|---|
| 258 | try
|
|---|
| 259 | {
|
|---|
| 260 | if constexpr (!is_same_v<R, void>)
|
|---|
| 261 | this->set_value(invoke(f, args...));
|
|---|
| 262 | else
|
|---|
| 263 | {
|
|---|
| 264 | invoke(f, args...);
|
|---|
| 265 | this->mark_set(true);
|
|---|
| 266 | }
|
|---|
| 267 | }
|
|---|
| 268 | catch(...) // TODO: Any exception.
|
|---|
| 269 | {
|
|---|
| 270 | // TODO: Store it.
|
|---|
| 271 | }
|
|---|
| 272 | }
|
|---|
| 273 | };
|
|---|
| 274 | }
|
|---|
| 275 |
|
|---|
| 276 | void destroy() override
|
|---|
| 277 | {
|
|---|
| 278 | if (!this->is_set())
|
|---|
| 279 | thread_.join();
|
|---|
| 280 | }
|
|---|
| 281 |
|
|---|
| 282 | void wait() override
|
|---|
| 283 | {
|
|---|
| 284 | if (!this->is_set())
|
|---|
| 285 | thread_.join();
|
|---|
| 286 | }
|
|---|
| 287 |
|
|---|
| 288 | ~async_shared_state() override
|
|---|
| 289 | {
|
|---|
| 290 | destroy();
|
|---|
| 291 | }
|
|---|
| 292 |
|
|---|
| 293 | protected:
|
|---|
| 294 | future_status timed_wait_(aux::time_unit_t) override
|
|---|
| 295 | {
|
|---|
| 296 | // TODO: have to sleep and check
|
|---|
| 297 | return future_status::timeout;
|
|---|
| 298 | }
|
|---|
| 299 |
|
|---|
| 300 | private:
|
|---|
| 301 | thread thread_;
|
|---|
| 302 | };
|
|---|
| 303 |
|
|---|
| 304 | template<class R, class F, class... Args>
|
|---|
| 305 | class deferred_shared_state: public shared_state<R>
|
|---|
| 306 | {
|
|---|
| 307 | public:
|
|---|
| 308 | template<class G>
|
|---|
| 309 | deferred_shared_state(G&& f, Args&&... args)
|
|---|
| 310 | : shared_state<R>{}, func_{forward<F>(f)},
|
|---|
| 311 | args_{forward<Args>(args)...}
|
|---|
| 312 | { /* DUMMY BODY */ }
|
|---|
| 313 |
|
|---|
| 314 | void destroy() override
|
|---|
| 315 | {
|
|---|
| 316 | aux::threading::mutex::lock(this->mutex_);
|
|---|
| 317 | if (!this->is_set())
|
|---|
| 318 | invoke_(make_index_sequence<sizeof...(Args)>{});
|
|---|
| 319 | aux::threading::mutex::unlock(this->mutex_);
|
|---|
| 320 | }
|
|---|
| 321 |
|
|---|
| 322 | void wait() override
|
|---|
| 323 | {
|
|---|
| 324 | /**
|
|---|
| 325 | * Note: Synchronization done in invoke_ -> set_value.
|
|---|
| 326 | */
|
|---|
| 327 | if (!this->is_set())
|
|---|
| 328 | invoke_(make_index_sequence<sizeof...(Args)>{});
|
|---|
| 329 | }
|
|---|
| 330 |
|
|---|
| 331 | ~deferred_shared_state() override
|
|---|
| 332 | {
|
|---|
| 333 | destroy();
|
|---|
| 334 | }
|
|---|
| 335 |
|
|---|
| 336 | protected:
|
|---|
| 337 | function<R(decay_t<Args>...)> func_;
|
|---|
| 338 | tuple<decay_t<Args>...> args_;
|
|---|
| 339 |
|
|---|
| 340 | template<size_t... Is>
|
|---|
| 341 | void invoke_(index_sequence<Is...>)
|
|---|
| 342 | {
|
|---|
| 343 | try
|
|---|
| 344 | {
|
|---|
| 345 | if constexpr (!is_same_v<R, void>)
|
|---|
| 346 | this->set_value(invoke(move(func_), get<Is>(move(args_))...));
|
|---|
| 347 | else
|
|---|
| 348 | {
|
|---|
| 349 | invoke(move(func_), get<Is>(move(args_))...);
|
|---|
| 350 | this->mark_set(true);
|
|---|
| 351 | }
|
|---|
| 352 | }
|
|---|
| 353 | catch(...)
|
|---|
| 354 | {
|
|---|
| 355 | // TODO: Store it.
|
|---|
| 356 | }
|
|---|
| 357 | }
|
|---|
| 358 |
|
|---|
| 359 | future_status timed_wait_(aux::time_unit_t) override
|
|---|
| 360 | {
|
|---|
| 361 | /**
|
|---|
| 362 | * Note: Neither of the wait_ functions has any effect
|
|---|
| 363 | * for deferred functions spawned by async (which
|
|---|
| 364 | * are the only users of this state type).
|
|---|
| 365 | */
|
|---|
| 366 | return future_status::deferred;
|
|---|
| 367 | }
|
|---|
| 368 | };
|
|---|
| 369 | }
|
|---|
| 370 |
|
|---|
| 371 | #endif
|
|---|