| 259 | //! Base class for heap-allocatable shared state for `split` and `ensure_started`. |
| 260 | template <class _Env, class _Variant> |
| 261 | struct __shared_state_base : __local_state_base |
| 262 | { |
| 263 | using __waiters_list_t = __intrusive_slist<&__local_state_base::__next_>; |
| 264 | |
| 265 | constexpr explicit __shared_state_base(_Env __env) |
| 266 | : __env_(__env::__join(prop{get_stop_token, __stop_source_.get_token()}, |
| 267 | static_cast<_Env&&>(__env))) |
| 268 | { |
| 269 | __results_.template emplace<__tuple<set_stopped_t>>(); |
| 270 | } |
| 271 | |
| 272 | virtual ~__shared_state_base() = 0; |
| 273 | |
| 274 | /// @brief This is called when the shared async operation completes. |
| 275 | /// @post __waiters_ is set to a known "tombstone" value. |
| 276 | template <class _Tag, class... _As> |
| 277 | void __complete(_Tag, _As&&... __as) noexcept |
| 278 | { |
| 279 | STDEXEC_TRY |
| 280 | { |
| 281 | using __tuple_t = __decayed_tuple<_Tag, _As...>; |
| 282 | __results_.template emplace<__tuple_t>(_Tag(), static_cast<_As&&>(__as)...); |
| 283 | } |
| 284 | STDEXEC_CATCH_ALL |
| 285 | { |
| 286 | if constexpr (!__nothrow_decay_copyable<_As...>) |
| 287 | { |
| 288 | using __tuple_t = __decayed_tuple<set_error_t, std::exception_ptr>; |
| 289 | __results_.template emplace<__tuple_t>(set_error, std::current_exception()); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | __notify_waiters(); |
| 294 | } |
| 295 | |
| 296 | /// @brief This is called when the shared async operation completes. |
| 297 | /// @post __waiters_ is set to a known "tombstone" value. |
| 298 | void __notify_waiters() noexcept |
| 299 | { |
| 300 | __waiters_list_t __waiters_copy{this}; |
| 301 | |
| 302 | // Set the waiters list to a known "tombstone" value that we can check later. |
| 303 | { |
| 304 | std::lock_guard __lock{this->__mutex_}; |
| 305 | this->__waiters_.swap(__waiters_copy); |
| 306 | } |
| 307 | |
| 308 | STDEXEC_ASSERT(__waiters_copy.front() != this); |
| 309 | for (auto __itr = __waiters_copy.begin(); __itr != __waiters_copy.end();) |
| 310 | { |
| 311 | __local_state_base* __item = *__itr; |
| 312 | |
| 313 | // We must increment the iterator before calling notify, since notify may end up |
| 314 | // triggering *__item to be destructed on another thread, and the intrusive slist's |
| 315 | // iterator increment relies on __item. |
| 316 | ++__itr; |
| 317 | __item->__notify(); |
| 318 | } |