| 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, bool set = true)
|
|---|
| 79 | {
|
|---|
| 80 | exception_ = ptr;
|
|---|
| 81 | has_exception_ = set;
|
|---|
| 82 | }
|
|---|
| 83 |
|
|---|
| 84 | bool has_exception() const noexcept
|
|---|
| 85 | {
|
|---|
| 86 | return has_exception_;
|
|---|
| 87 | }
|
|---|
| 88 |
|
|---|
| 89 | void throw_stored_exception() const
|
|---|
| 90 | {
|
|---|
| 91 | if (has_exception_)
|
|---|
| 92 | rethrow_exception(exception_);
|
|---|
| 93 | }
|
|---|
| 94 |
|
|---|
| 95 | virtual void wait() const
|
|---|
| 96 | {
|
|---|
| 97 | aux::threading::mutex::lock(
|
|---|
| 98 | const_cast<aux::mutex_t&>(mutex_)
|
|---|
| 99 | );
|
|---|
| 100 |
|
|---|
| 101 | while (!value_set_)
|
|---|
| 102 | {
|
|---|
| 103 | aux::threading::condvar::wait(
|
|---|
| 104 | const_cast<aux::condvar_t&>(condvar_),
|
|---|
| 105 | const_cast<aux::mutex_t&>(mutex_)
|
|---|
| 106 | );
|
|---|
| 107 | }
|
|---|
| 108 |
|
|---|
| 109 | aux::threading::mutex::unlock(
|
|---|
| 110 | const_cast<aux::mutex_t&>(mutex_)
|
|---|
| 111 | );
|
|---|
| 112 | }
|
|---|
| 113 |
|
|---|
| 114 | template<class Rep, class Period>
|
|---|
| 115 | future_status
|
|---|
| 116 | wait_for(const chrono::duration<Rep, Period>& rel_time) const
|
|---|
| 117 | {
|
|---|
| 118 | if (value_set_)
|
|---|
| 119 | return future_status::ready;
|
|---|
| 120 |
|
|---|
| 121 | aux::threading::mutex::lock(
|
|---|
| 122 | const_cast<aux::mutex_t&>(mutex_)
|
|---|
| 123 | );
|
|---|
| 124 |
|
|---|
| 125 | auto res = timed_wait_(
|
|---|
| 126 | aux::threading::time::convert(rel_time)
|
|---|
| 127 | );
|
|---|
| 128 |
|
|---|
| 129 | aux::threading::mutex::unlock(
|
|---|
| 130 | const_cast<aux::mutex_t&>(mutex_)
|
|---|
| 131 | );
|
|---|
| 132 |
|
|---|
| 133 | return res;
|
|---|
| 134 | }
|
|---|
| 135 |
|
|---|
| 136 | template<class Clock, class Duration>
|
|---|
| 137 | future_status
|
|---|
| 138 | wait_until(const chrono::time_point<Clock, Duration>& abs_time)
|
|---|
| 139 | {
|
|---|
| 140 | if (value_set_)
|
|---|
| 141 | return future_status::ready;
|
|---|
| 142 |
|
|---|
| 143 | aux::threading::mutex::lock(
|
|---|
| 144 | const_cast<aux::mutex_t&>(mutex_)
|
|---|
| 145 | );
|
|---|
| 146 |
|
|---|
| 147 | auto res = timed_wait_(
|
|---|
| 148 | aux::threading::time::convert(abs_time - Clock::now())
|
|---|
| 149 | );
|
|---|
| 150 |
|
|---|
| 151 | aux::threading::mutex::unlock(
|
|---|
| 152 | const_cast<aux::mutex_t&>(mutex_)
|
|---|
| 153 | );
|
|---|
| 154 |
|
|---|
| 155 | return res;
|
|---|
| 156 | }
|
|---|
| 157 |
|
|---|
| 158 | ~shared_state_base() override = default;
|
|---|
| 159 |
|
|---|
| 160 | protected:
|
|---|
| 161 | aux::mutex_t mutex_;
|
|---|
| 162 | aux::condvar_t condvar_;
|
|---|
| 163 |
|
|---|
| 164 | bool value_set_;
|
|---|
| 165 |
|
|---|
| 166 | exception_ptr exception_;
|
|---|
| 167 | bool has_exception_;
|
|---|
| 168 |
|
|---|
| 169 | /**
|
|---|
| 170 | * Note: wait_for and wait_until are templates and as such
|
|---|
| 171 | * cannot be virtual and overriden by the deferred_ and
|
|---|
| 172 | * async_ children. However, we are using aux::time_unit_t
|
|---|
| 173 | * in the end anyway, so we can work around that
|
|---|
| 174 | * by using the 'template method' design pattern
|
|---|
| 175 | * (i.e. by providing a virtual function called by these
|
|---|
| 176 | * templates and then overriding that function in the
|
|---|
| 177 | * children).
|
|---|
| 178 | */
|
|---|
| 179 | virtual future_status timed_wait_(aux::time_unit_t time) const
|
|---|
| 180 | {
|
|---|
| 181 | auto res = aux::threading::condvar::wait_for(
|
|---|
| 182 | const_cast<aux::condvar_t&>(condvar_),
|
|---|
| 183 | const_cast<aux::mutex_t&>(mutex_), time
|
|---|
| 184 | );
|
|---|
| 185 |
|
|---|
| 186 | return res == ETIMEOUT ? future_status::timeout
|
|---|
| 187 | : future_status::ready;
|
|---|
| 188 | }
|
|---|
| 189 | };
|
|---|
| 190 |
|
|---|
| 191 | template<class R>
|
|---|
| 192 | class shared_state: public shared_state_base
|
|---|
| 193 | {
|
|---|
| 194 | public:
|
|---|
| 195 | shared_state()
|
|---|
| 196 | : shared_state_base{}
|
|---|
| 197 | { /* DUMMY BODY */ }
|
|---|
| 198 |
|
|---|
| 199 | void set_value(const R& val, bool set)
|
|---|
| 200 | {
|
|---|
| 201 | /**
|
|---|
| 202 | * Note: This is the 'mark ready' move described
|
|---|
| 203 | * in 30.6.4 (6).
|
|---|
| 204 | */
|
|---|
| 205 |
|
|---|
| 206 | aux::threading::mutex::lock(mutex_);
|
|---|
| 207 | value_ = val;
|
|---|
| 208 | value_set_ = set;
|
|---|
| 209 | aux::threading::mutex::unlock(mutex_);
|
|---|
| 210 |
|
|---|
| 211 | if (set)
|
|---|
| 212 | aux::threading::condvar::broadcast(condvar_);
|
|---|
| 213 | }
|
|---|
| 214 |
|
|---|
| 215 | void set_value(R&& val, bool set = true)
|
|---|
| 216 | {
|
|---|
| 217 | aux::threading::mutex::lock(mutex_);
|
|---|
| 218 | value_ = std::move(val);
|
|---|
| 219 | value_set_ = set;
|
|---|
| 220 | aux::threading::mutex::unlock(mutex_);
|
|---|
| 221 |
|
|---|
| 222 | if (set)
|
|---|
| 223 | aux::threading::condvar::broadcast(condvar_);
|
|---|
| 224 | }
|
|---|
| 225 |
|
|---|
| 226 | R& get()
|
|---|
| 227 | {
|
|---|
| 228 | return value_;
|
|---|
| 229 | }
|
|---|
| 230 |
|
|---|
| 231 | protected:
|
|---|
| 232 | R value_;
|
|---|
| 233 | };
|
|---|
| 234 |
|
|---|
| 235 | template<>
|
|---|
| 236 | class shared_state<void>: public shared_state_base
|
|---|
| 237 | {
|
|---|
| 238 | public:
|
|---|
| 239 | shared_state()
|
|---|
| 240 | : shared_state_base{}
|
|---|
| 241 | { /* DUMMY BODY */ }
|
|---|
| 242 |
|
|---|
| 243 | void set_value()
|
|---|
| 244 | {
|
|---|
| 245 | value_set_ = true;
|
|---|
| 246 | aux::threading::condvar::broadcast(condvar_);
|
|---|
| 247 | }
|
|---|
| 248 |
|
|---|
| 249 | void get()
|
|---|
| 250 | { /* DUMMY BODY */ }
|
|---|
| 251 | };
|
|---|
| 252 |
|
|---|
| 253 | /**
|
|---|
| 254 | * We could make one state for both async and
|
|---|
| 255 | * deferred policies, but then we would be wasting
|
|---|
| 256 | * memory and the only benefit would be the ability
|
|---|
| 257 | * for additional implementation defined policies done
|
|---|
| 258 | * directly in that state (as opposed to making new
|
|---|
| 259 | * states for them).
|
|---|
| 260 | *
|
|---|
| 261 | * But since we have no plan (nor need) to make those,
|
|---|
| 262 | * this approach seems to be the best one.
|
|---|
| 263 | *
|
|---|
| 264 | * Also note that unlike the parent class shared_state,
|
|---|
| 265 | * we do not need to specialize these for void. This is because
|
|---|
| 266 | * the difference in class contents are handled by the parent
|
|---|
| 267 | * specialization and setting the value can be handled easily
|
|---|
| 268 | * by if constexpr and checking for the equivalence of the
|
|---|
| 269 | * R template parameter and void.
|
|---|
| 270 | */
|
|---|
| 271 |
|
|---|
| 272 | template<class R, class F, class... Args>
|
|---|
| 273 | class async_shared_state: public shared_state<R>
|
|---|
| 274 | {
|
|---|
| 275 | public:
|
|---|
| 276 | async_shared_state(F&& f, Args&&... args)
|
|---|
| 277 | : shared_state<R>{}, thread_{}
|
|---|
| 278 | {
|
|---|
| 279 | thread_ = thread{
|
|---|
| 280 | [=](){
|
|---|
| 281 | try
|
|---|
| 282 | {
|
|---|
| 283 | if constexpr (!is_same_v<R, void>)
|
|---|
| 284 | this->set_value(invoke(f, args...));
|
|---|
| 285 | else
|
|---|
| 286 | {
|
|---|
| 287 | invoke(f, args...);
|
|---|
| 288 | this->mark_set(true);
|
|---|
| 289 | }
|
|---|
| 290 | }
|
|---|
| 291 | catch(const exception& __exception)
|
|---|
| 292 | {
|
|---|
| 293 | this->set_exception(make_exception_ptr(__exception));
|
|---|
| 294 | }
|
|---|
| 295 | }
|
|---|
| 296 | };
|
|---|
| 297 | }
|
|---|
| 298 |
|
|---|
| 299 | void destroy() override
|
|---|
| 300 | {
|
|---|
| 301 | if (!this->is_set())
|
|---|
| 302 | thread_.join();
|
|---|
| 303 | }
|
|---|
| 304 |
|
|---|
| 305 | void wait() const override
|
|---|
| 306 | {
|
|---|
| 307 | if (!this->is_set())
|
|---|
| 308 | const_cast<thread&>(thread_).join();
|
|---|
| 309 | }
|
|---|
| 310 |
|
|---|
| 311 | ~async_shared_state() override
|
|---|
| 312 | {
|
|---|
| 313 | destroy();
|
|---|
| 314 | }
|
|---|
| 315 |
|
|---|
| 316 | protected:
|
|---|
| 317 | future_status timed_wait_(aux::time_unit_t time) const override
|
|---|
| 318 | {
|
|---|
| 319 | /**
|
|---|
| 320 | * Note: Currently we have no timed join, but this
|
|---|
| 321 | * behaviour should be compliant.
|
|---|
| 322 | */
|
|---|
| 323 | aux::threading::time::sleep(time);
|
|---|
| 324 | if (this->value_set_)
|
|---|
| 325 | return future_status::ready;
|
|---|
| 326 | else
|
|---|
| 327 | return future_status::timeout;
|
|---|
| 328 | }
|
|---|
| 329 |
|
|---|
| 330 | private:
|
|---|
| 331 | thread thread_;
|
|---|
| 332 | };
|
|---|
| 333 |
|
|---|
| 334 | template<class R, class F, class... Args>
|
|---|
| 335 | class deferred_shared_state: public shared_state<R>
|
|---|
| 336 | {
|
|---|
| 337 | public:
|
|---|
| 338 | template<class G>
|
|---|
| 339 | deferred_shared_state(G&& f, Args&&... args)
|
|---|
| 340 | : shared_state<R>{}, func_{forward<F>(f)},
|
|---|
| 341 | args_{forward<Args>(args)...}
|
|---|
| 342 | { /* DUMMY BODY */ }
|
|---|
| 343 |
|
|---|
| 344 | void destroy() override
|
|---|
| 345 | {
|
|---|
| 346 | aux::threading::mutex::lock(this->mutex_);
|
|---|
| 347 | if (!this->is_set())
|
|---|
| 348 | invoke_(make_index_sequence<sizeof...(Args)>{});
|
|---|
| 349 | aux::threading::mutex::unlock(this->mutex_);
|
|---|
| 350 | }
|
|---|
| 351 |
|
|---|
| 352 | void wait() const override
|
|---|
| 353 | {
|
|---|
| 354 | /**
|
|---|
| 355 | * Note: Synchronization done in invoke_ -> set_value.
|
|---|
| 356 | */
|
|---|
| 357 | if (!this->is_set())
|
|---|
| 358 | {
|
|---|
| 359 | const_cast<
|
|---|
| 360 | deferred_shared_state<R, F, Args...>*
|
|---|
| 361 | >(this)->invoke_(make_index_sequence<sizeof...(Args)>{});
|
|---|
| 362 | }
|
|---|
| 363 | }
|
|---|
| 364 |
|
|---|
| 365 | ~deferred_shared_state() override
|
|---|
| 366 | {
|
|---|
| 367 | destroy();
|
|---|
| 368 | }
|
|---|
| 369 |
|
|---|
| 370 | protected:
|
|---|
| 371 | function<R(decay_t<Args>...)> func_;
|
|---|
| 372 | tuple<decay_t<Args>...> args_;
|
|---|
| 373 |
|
|---|
| 374 | template<size_t... Is>
|
|---|
| 375 | void invoke_(index_sequence<Is...>)
|
|---|
| 376 | {
|
|---|
| 377 | try
|
|---|
| 378 | {
|
|---|
| 379 | if constexpr (!is_same_v<R, void>)
|
|---|
| 380 | this->set_value(invoke(move(func_), get<Is>(move(args_))...));
|
|---|
| 381 | else
|
|---|
| 382 | {
|
|---|
| 383 | invoke(move(func_), get<Is>(move(args_))...);
|
|---|
| 384 | this->mark_set(true);
|
|---|
| 385 | }
|
|---|
| 386 | }
|
|---|
| 387 | catch(const exception& __exception)
|
|---|
| 388 | {
|
|---|
| 389 | this->set_exception(make_exception_ptr(__exception));
|
|---|
| 390 | }
|
|---|
| 391 | }
|
|---|
| 392 |
|
|---|
| 393 | future_status timed_wait_(aux::time_unit_t) const override
|
|---|
| 394 | {
|
|---|
| 395 | /**
|
|---|
| 396 | * Note: Neither of the wait_ functions has any effect
|
|---|
| 397 | * for deferred functions spawned by async (which
|
|---|
| 398 | * are the only users of this state type).
|
|---|
| 399 | */
|
|---|
| 400 | return future_status::deferred;
|
|---|
| 401 | }
|
|---|
| 402 | };
|
|---|
| 403 |
|
|---|
| 404 | /**
|
|---|
| 405 | * Note: The following two functions should:
|
|---|
| 406 | * 1) Increment refcount.
|
|---|
| 407 | * 2) Store ptr to a vector of shared_state_base ptrs
|
|---|
| 408 | * (as those have ::mark_set member functions).
|
|---|
| 409 | * 3) If not done already, register a function
|
|---|
| 410 | * executing all these in the thread_atexit function
|
|---|
| 411 | * once that is implemented.
|
|---|
| 412 | */
|
|---|
| 413 |
|
|---|
| 414 | template<class R>
|
|---|
| 415 | void set_state_value_at_thread_exit(shared_state<R>* state)
|
|---|
| 416 | {
|
|---|
| 417 | // TODO: implement
|
|---|
| 418 | __unimplemented();
|
|---|
| 419 | }
|
|---|
| 420 |
|
|---|
| 421 | template<class R>
|
|---|
| 422 | void set_state_exception_at_thread_exit(shared_state<R>* state)
|
|---|
| 423 | {
|
|---|
| 424 | // TODO: implement
|
|---|
| 425 | __unimplemented();
|
|---|
| 426 | }
|
|---|
| 427 | }
|
|---|
| 428 |
|
|---|
| 429 | #endif
|
|---|