Commit 07a7d5c4 authored by Kirk Shoop's avatar Kirk Shoop Committed by Facebook Github Bot

add new thread cancellation test

fbshipit-source-id: 9d12db7b6b8ea4bc2ba87de78a35046884c30e3d
parent ff57e8ff
...@@ -1330,9 +1330,9 @@ void set_stopping(S& s) noexcept(noexcept(s.stopping())) { ...@@ -1330,9 +1330,9 @@ void set_stopping(S& s) noexcept(noexcept(s.stopping())) {
s.stopping(); s.stopping();
} }
PUSHMI_TEMPLATE (class S, class Up) PUSHMI_TEMPLATE (class S, class Up)
(requires requires (std::declval<S&>().starting(std::declval<Up&>()))) (requires requires (std::declval<S&>().starting(std::declval<Up>())))
void set_starting(S& s, Up& up) noexcept(noexcept(s.starting(up))) { void set_starting(S& s, Up up) noexcept(noexcept(s.starting(std::move(up)))) {
s.starting(up); s.starting(std::move(up));
} }
PUSHMI_TEMPLATE (class SD, class Out) PUSHMI_TEMPLATE (class SD, class Out)
...@@ -1404,10 +1404,10 @@ void set_stopping(std::reference_wrapper<S> s) noexcept( ...@@ -1404,10 +1404,10 @@ void set_stopping(std::reference_wrapper<S> s) noexcept(
set_stopping(s.get()); set_stopping(s.get());
} }
PUSHMI_TEMPLATE (class S, class Up) PUSHMI_TEMPLATE (class S, class Up)
(requires requires ( set_starting(std::declval<S&>(), std::declval<Up&>()) )) (requires requires ( set_starting(std::declval<S&>(), std::declval<Up>()) ))
void set_starting(std::reference_wrapper<S> s, Up& up) noexcept( void set_starting(std::reference_wrapper<S> s, Up up) noexcept(
noexcept(set_starting(s.get(), up))) { noexcept(set_starting(s.get(), std::move(up)))) {
set_starting(s.get(), up); set_starting(s.get(), std::move(up));
} }
PUSHMI_TEMPLATE (class SD, class Out) PUSHMI_TEMPLATE (class SD, class Out)
(requires requires ( submit(std::declval<SD&>(), std::declval<Out>()) )) (requires requires ( submit(std::declval<SD&>(), std::declval<Out>()) ))
...@@ -1485,13 +1485,13 @@ struct set_stopping_fn { ...@@ -1485,13 +1485,13 @@ struct set_stopping_fn {
struct set_starting_fn { struct set_starting_fn {
PUSHMI_TEMPLATE (class S, class Up) PUSHMI_TEMPLATE (class S, class Up)
(requires requires ( (requires requires (
set_starting(std::declval<S&>(), std::declval<Up&>()), set_starting(std::declval<S&>(), std::declval<Up>()),
set_error(std::declval<S&>(), std::current_exception()) set_error(std::declval<S&>(), std::current_exception())
)) ))
void operator()(S&& s, Up& up) const void operator()(S&& s, Up up) const
noexcept(noexcept(set_starting(s, up))) { noexcept(noexcept(set_starting(s, std::move(up)))) {
try { try {
set_starting(s, up); set_starting(s, std::move(up));
} catch (...) { } catch (...) {
set_error(s, std::current_exception()); set_error(s, std::current_exception());
} }
...@@ -1961,13 +1961,13 @@ PUSHMI_CONCEPT_DEF( ...@@ -1961,13 +1961,13 @@ PUSHMI_CONCEPT_DEF(
// silent does not really make sense, but cannot test for // silent does not really make sense, but cannot test for
// None without the error type, use is_none<> to strengthen // None without the error type, use is_none<> to strengthen
// requirements // requirements
PUSHMI_CONCEPT_DEF( PUSHMI_CONCEPT_DEF(
template (class D, class... PropertyN) template (class D, class... PropertyN)
(concept Sender)(D, PropertyN...), (concept Sender)(D, PropertyN...),
SemiMovable<D> && SemiMovable<D> &&
None<D> && None<D> &&
property_query_v<D, PropertyN...> && property_query_v<D, PropertyN...> &&
is_sender_v<D> is_sender_v<D>
); );
...@@ -1999,19 +1999,19 @@ PUSHMI_CONCEPT_DEF( ...@@ -1999,19 +1999,19 @@ PUSHMI_CONCEPT_DEF(
PUSHMI_CONCEPT_DEF( PUSHMI_CONCEPT_DEF(
template ( template (
class N, class N,
class Up, class Up,
class PE = std::exception_ptr, class PE = std::exception_ptr,
class E = PE) class E = PE)
(concept FlowNoneReceiver)(N, Up, PE, E), (concept FlowNoneReceiver)(N, Up, PE, E),
requires(N& n, Up& up) ( requires(N& n, Up&& up) (
::pushmi::set_starting(n, up) ::pushmi::set_starting(n, (Up &&) up)
) && ) &&
FlowReceiver<N> && FlowReceiver<N> &&
Receiver<Up> && Receiver<Up> &&
SemiMovable<PE> && SemiMovable<PE> &&
SemiMovable<E> && SemiMovable<E> &&
NoneReceiver<Up, PE> && NoneReceiver<Up, PE> &&
NoneReceiver<N, E> NoneReceiver<N, E>
); );
...@@ -2023,7 +2023,7 @@ PUSHMI_CONCEPT_DEF( ...@@ -2023,7 +2023,7 @@ PUSHMI_CONCEPT_DEF(
class PE = std::exception_ptr, class PE = std::exception_ptr,
class E = PE) class E = PE)
(concept FlowSingleReceiver)(S, Up, T, PE, E), (concept FlowSingleReceiver)(S, Up, T, PE, E),
SingleReceiver<S, T, E> && SingleReceiver<S, T, E> &&
FlowNoneReceiver<S, Up, PE, E> FlowNoneReceiver<S, Up, PE, E>
); );
...@@ -2035,7 +2035,7 @@ PUSHMI_CONCEPT_DEF( ...@@ -2035,7 +2035,7 @@ PUSHMI_CONCEPT_DEF(
class PE = std::exception_ptr, class PE = std::exception_ptr,
class E = PE) class E = PE)
(concept FlowManyReceiver)(S, Up, T, PE, E), (concept FlowManyReceiver)(S, Up, T, PE, E),
ManyReceiver<S, T, E> && ManyReceiver<S, T, E> &&
FlowSingleReceiver<S, Up, T, PE, E> FlowSingleReceiver<S, Up, T, PE, E>
); );
...@@ -2065,9 +2065,9 @@ PUSHMI_CONCEPT_DEF( ...@@ -2065,9 +2065,9 @@ PUSHMI_CONCEPT_DEF(
::pushmi::now(d), ::pushmi::now(d),
requires_<Regular<decltype(::pushmi::now(d))>> requires_<Regular<decltype(::pushmi::now(d))>>
) && ) &&
Sender<D> && Sender<D> &&
property_query_v<D, PropertyN...> && property_query_v<D, PropertyN...> &&
Time<D> && Time<D> &&
None<D> None<D>
); );
...@@ -2077,7 +2077,7 @@ PUSHMI_CONCEPT_DEF( ...@@ -2077,7 +2077,7 @@ PUSHMI_CONCEPT_DEF(
requires(D& d, S&& s) ( requires(D& d, S&& s) (
::pushmi::submit(d, ::pushmi::now(d), (S &&) s) ::pushmi::submit(d, ::pushmi::now(d), (S &&) s)
) && ) &&
TimeSender<D> && TimeSender<D> &&
property_query_v<D, PropertyN...> && property_query_v<D, PropertyN...> &&
Receiver<S> Receiver<S>
); );
...@@ -2188,7 +2188,7 @@ struct ignoreStpF { ...@@ -2188,7 +2188,7 @@ struct ignoreStpF {
struct ignoreStrtF { struct ignoreStrtF {
template <class Up> template <class Up>
void operator()(Up&) {} void operator()(Up&&) {}
}; };
...@@ -2240,10 +2240,10 @@ struct passDStpF { ...@@ -2240,10 +2240,10 @@ struct passDStpF {
struct passDStrtF { struct passDStrtF {
PUSHMI_TEMPLATE(class Up, class Data) PUSHMI_TEMPLATE(class Up, class Data)
(requires requires ( (requires requires (
::pushmi::set_starting(std::declval<Data&>(), std::declval<Up&>()) ::pushmi::set_starting(std::declval<Data&>(), std::declval<Up>())
) && Receiver<Data>) ) && Receiver<Data>)
void operator()(Data& out, Up& up) const { void operator()(Data& out, Up up) const {
::pushmi::set_starting(out, up); ::pushmi::set_starting(out, std::move(up));
} }
}; };
...@@ -4045,13 +4045,13 @@ class flow_single<V, PE, E> { ...@@ -4045,13 +4045,13 @@ class flow_single<V, PE, E> {
static void s_error(data&, E) noexcept { std::terminate(); } static void s_error(data&, E) noexcept { std::terminate(); }
static void s_value(data&, V) {} static void s_value(data&, V) {}
static void s_stopping(data&) noexcept {} static void s_stopping(data&) noexcept {}
static void s_starting(data&, any_none<PE>&) {} static void s_starting(data&, any_none<PE>) {}
void (*op_)(data&, data*) = vtable::s_op; void (*op_)(data&, data*) = vtable::s_op;
void (*done_)(data&) = vtable::s_done; void (*done_)(data&) = vtable::s_done;
void (*error_)(data&, E) noexcept = vtable::s_error; void (*error_)(data&, E) noexcept = vtable::s_error;
void (*value_)(data&, V) = vtable::s_value; void (*value_)(data&, V) = vtable::s_value;
void (*stopping_)(data&) noexcept = vtable::s_stopping; void (*stopping_)(data&) noexcept = vtable::s_stopping;
void (*starting_)(data&, any_none<PE>&) = vtable::s_starting; void (*starting_)(data&, any_none<PE>) = vtable::s_starting;
}; };
static constexpr vtable const noop_ {}; static constexpr vtable const noop_ {};
vtable const* vptr_ = &noop_; vtable const* vptr_ = &noop_;
...@@ -4075,8 +4075,8 @@ class flow_single<V, PE, E> { ...@@ -4075,8 +4075,8 @@ class flow_single<V, PE, E> {
static void stopping(data& src) noexcept { static void stopping(data& src) noexcept {
::pushmi::set_stopping(*static_cast<Wrapped*>(src.pobj_)); ::pushmi::set_stopping(*static_cast<Wrapped*>(src.pobj_));
} }
static void starting(data& src, any_none<PE>& up) { static void starting(data& src, any_none<PE> up) {
::pushmi::set_starting(*static_cast<Wrapped*>(src.pobj_), up); ::pushmi::set_starting(*static_cast<Wrapped*>(src.pobj_), std::move(up));
} }
}; };
static const vtable vtbl{s::op, s::done, s::error, s::value, s::stopping, s::starting}; static const vtable vtbl{s::op, s::done, s::error, s::value, s::stopping, s::starting};
...@@ -4106,8 +4106,8 @@ class flow_single<V, PE, E> { ...@@ -4106,8 +4106,8 @@ class flow_single<V, PE, E> {
static void stopping(data& src) noexcept { static void stopping(data& src) noexcept {
::pushmi::set_stopping(*static_cast<Wrapped*>((void*)src.buffer_)); ::pushmi::set_stopping(*static_cast<Wrapped*>((void*)src.buffer_));
} }
static void starting(data& src, any_none<PE>& up) { static void starting(data& src, any_none<PE> up) {
::pushmi::set_starting(*static_cast<Wrapped*>((void*)src.buffer_), up); ::pushmi::set_starting(*static_cast<Wrapped*>((void*)src.buffer_), std::move(up));
} }
}; };
static const vtable vtbl{s::op, s::done, s::error, s::value, s::stopping, s::starting}; static const vtable vtbl{s::op, s::done, s::error, s::value, s::stopping, s::starting};
...@@ -4150,8 +4150,8 @@ public: ...@@ -4150,8 +4150,8 @@ public:
void stopping() noexcept { void stopping() noexcept {
vptr_->stopping_(data_); vptr_->stopping_(data_);
} }
void starting(any_none<PE>& up) { void starting(any_none<PE> up) {
vptr_->starting_(data_, up); vptr_->starting_(data_, std::move(up));
} }
}; };
...@@ -4219,9 +4219,9 @@ class flow_single<VF, EF, DF, StpF, StrtF> { ...@@ -4219,9 +4219,9 @@ class flow_single<VF, EF, DF, StpF, StrtF> {
stpf_(); stpf_();
} }
PUSHMI_TEMPLATE(class Up) PUSHMI_TEMPLATE(class Up)
(requires Receiver<Up, is_none<>> && Invocable<StrtF&, Up&>) (requires Receiver<Up, is_none<>> && Invocable<StrtF&, Up&&>)
void starting(Up& up) { void starting(Up&& up) {
strtf_(up); strtf_( (Up &&) up);
} }
}; };
...@@ -4291,9 +4291,9 @@ class flow_single<Data, DVF, DEF, DDF, DStpF, DStrtF> { ...@@ -4291,9 +4291,9 @@ class flow_single<Data, DVF, DEF, DDF, DStpF, DStrtF> {
stpf_(data_); stpf_(data_);
} }
PUSHMI_TEMPLATE (class Up) PUSHMI_TEMPLATE (class Up)
(requires Invocable<DStrtF&, Data&, Up&>) (requires Invocable<DStrtF&, Data&, Up&&>)
void starting(Up& up) { void starting(Up&& up) {
strtf_(data_, up); strtf_(data_, (Up &&) up);
} }
}; };
...@@ -4936,6 +4936,9 @@ class trampoline { ...@@ -4936,6 +4936,9 @@ class trampoline {
auto item = std::move(pending(pending_store).front()); auto item = std::move(pending(pending_store).front());
pending(pending_store).pop_front(); pending(pending_store).pop_front();
auto& when = std::get<0>(item); auto& when = std::get<0>(item);
if (when > trampoline<E>::now()) {
std::this_thread::sleep_until(when);
}
auto& what = std::get<1>(item); auto& what = std::get<1>(item);
any_time_executor_ref<error_type, time_point> anythis{that}; any_time_executor_ref<error_type, time_point> anythis{that};
::pushmi::set_value(what, anythis); ::pushmi::set_value(what, anythis);
......
...@@ -62,7 +62,7 @@ struct ignoreStpF { ...@@ -62,7 +62,7 @@ struct ignoreStpF {
struct ignoreStrtF { struct ignoreStrtF {
template <class Up> template <class Up>
void operator()(Up&) {} void operator()(Up&&) {}
}; };
...@@ -114,10 +114,10 @@ struct passDStpF { ...@@ -114,10 +114,10 @@ struct passDStpF {
struct passDStrtF { struct passDStrtF {
PUSHMI_TEMPLATE(class Up, class Data) PUSHMI_TEMPLATE(class Up, class Data)
(requires requires ( (requires requires (
::pushmi::set_starting(std::declval<Data&>(), std::declval<Up&>()) ::pushmi::set_starting(std::declval<Data&>(), std::declval<Up>())
) && Receiver<Data>) ) && Receiver<Data>)
void operator()(Data& out, Up& up) const { void operator()(Data& out, Up up) const {
::pushmi::set_starting(out, up); ::pushmi::set_starting(out, std::move(up));
} }
}; };
......
...@@ -228,13 +228,13 @@ PUSHMI_CONCEPT_DEF( ...@@ -228,13 +228,13 @@ PUSHMI_CONCEPT_DEF(
// silent does not really make sense, but cannot test for // silent does not really make sense, but cannot test for
// None without the error type, use is_none<> to strengthen // None without the error type, use is_none<> to strengthen
// requirements // requirements
PUSHMI_CONCEPT_DEF( PUSHMI_CONCEPT_DEF(
template (class D, class... PropertyN) template (class D, class... PropertyN)
(concept Sender)(D, PropertyN...), (concept Sender)(D, PropertyN...),
SemiMovable<D> && SemiMovable<D> &&
None<D> && None<D> &&
property_query_v<D, PropertyN...> && property_query_v<D, PropertyN...> &&
is_sender_v<D> is_sender_v<D>
); );
...@@ -266,19 +266,19 @@ PUSHMI_CONCEPT_DEF( ...@@ -266,19 +266,19 @@ PUSHMI_CONCEPT_DEF(
PUSHMI_CONCEPT_DEF( PUSHMI_CONCEPT_DEF(
template ( template (
class N, class N,
class Up, class Up,
class PE = std::exception_ptr, class PE = std::exception_ptr,
class E = PE) class E = PE)
(concept FlowNoneReceiver)(N, Up, PE, E), (concept FlowNoneReceiver)(N, Up, PE, E),
requires(N& n, Up& up) ( requires(N& n, Up&& up) (
::pushmi::set_starting(n, up) ::pushmi::set_starting(n, (Up &&) up)
) && ) &&
FlowReceiver<N> && FlowReceiver<N> &&
Receiver<Up> && Receiver<Up> &&
SemiMovable<PE> && SemiMovable<PE> &&
SemiMovable<E> && SemiMovable<E> &&
NoneReceiver<Up, PE> && NoneReceiver<Up, PE> &&
NoneReceiver<N, E> NoneReceiver<N, E>
); );
...@@ -290,7 +290,7 @@ PUSHMI_CONCEPT_DEF( ...@@ -290,7 +290,7 @@ PUSHMI_CONCEPT_DEF(
class PE = std::exception_ptr, class PE = std::exception_ptr,
class E = PE) class E = PE)
(concept FlowSingleReceiver)(S, Up, T, PE, E), (concept FlowSingleReceiver)(S, Up, T, PE, E),
SingleReceiver<S, T, E> && SingleReceiver<S, T, E> &&
FlowNoneReceiver<S, Up, PE, E> FlowNoneReceiver<S, Up, PE, E>
); );
...@@ -302,7 +302,7 @@ PUSHMI_CONCEPT_DEF( ...@@ -302,7 +302,7 @@ PUSHMI_CONCEPT_DEF(
class PE = std::exception_ptr, class PE = std::exception_ptr,
class E = PE) class E = PE)
(concept FlowManyReceiver)(S, Up, T, PE, E), (concept FlowManyReceiver)(S, Up, T, PE, E),
ManyReceiver<S, T, E> && ManyReceiver<S, T, E> &&
FlowSingleReceiver<S, Up, T, PE, E> FlowSingleReceiver<S, Up, T, PE, E>
); );
...@@ -332,9 +332,9 @@ PUSHMI_CONCEPT_DEF( ...@@ -332,9 +332,9 @@ PUSHMI_CONCEPT_DEF(
::pushmi::now(d), ::pushmi::now(d),
requires_<Regular<decltype(::pushmi::now(d))>> requires_<Regular<decltype(::pushmi::now(d))>>
) && ) &&
Sender<D> && Sender<D> &&
property_query_v<D, PropertyN...> && property_query_v<D, PropertyN...> &&
Time<D> && Time<D> &&
None<D> None<D>
); );
...@@ -344,7 +344,7 @@ PUSHMI_CONCEPT_DEF( ...@@ -344,7 +344,7 @@ PUSHMI_CONCEPT_DEF(
requires(D& d, S&& s) ( requires(D& d, S&& s) (
::pushmi::submit(d, ::pushmi::now(d), (S &&) s) ::pushmi::submit(d, ::pushmi::now(d), (S &&) s)
) && ) &&
TimeSender<D> && TimeSender<D> &&
property_query_v<D, PropertyN...> && property_query_v<D, PropertyN...> &&
Receiver<S> Receiver<S>
); );
......
#pragma once
// Copyright (c) 2018-present, Facebook, Inc.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#include "forwards.h"
namespace pushmi {
#if 0
template <class T, class Dual>
struct entangled {
T t;
entangled<Dual, T>* dual;
~entangled() {
if (!!dual) {
dual->dual = nullptr;
}
}
explicit entangled(T t) : t(std::move(t)), dual(nullptr) {}
entangled(entangled&& o) : t(std::move(o.t)), dual(o.dual) {
o.dual = nullptr;
if (!!dual) {
dual->dual = this;
}
}
entangled() = delete;
entangled(const entangled&) = delete;
entangled& operator=(const entangled&) = delete;
entangled& operator=(entangled&&) = delete;
Dual* lockPointerToDual() {
if (!!dual) {
return std::addressof(dual->t);
}
return nullptr;
}
void unlockPointerToDual() {
}
};
#else
// This class can be used to keep a pair of values with pointers to each other
// in sync, even when both objects are allowed to move. Ordinarily you'd do this
// with a heap-allocated, refcounted, control block (or do something else using
// external storage, like a lock table chosen by the current addresses of both
// objects).
// Another thing you could do is have locks, and a backoff strategy for dealing
// with deadlock: lock locally, trylock your dual, if the trylock fails,
// unlock yourself, wait a little while (giving a thread touching the dual a
// chance to acquire the local lock), and repeat. That's kind of ugly.
// This algorithm (which, to be clear, is untested and I haven't really even
// thought through carefully) provides the same guarantees, but without using
// external storage or backoff-based deadlock recovery.
template <class T, class Dual>
struct entangled {
// must be constructed first so that the other.lockBoth() in the move
// constructor is run before moving other.t and other.dual
std::atomic<int> stateMachine;
T t;
// In a couple places, we can save on some atomic ops by making this atomic,
// and adding a "dual == null" fast-path without locking.
entangled<Dual, T>* dual;
const static int kUnlocked = 0;
const static int kLocked = 1;
const static int kLockedAndLossAcknowledged = 2;
// Note: *not* thread-safe; it's a bug for two threads to concurrently call
// lockBoth() on the same entangled (just as it's a bug for two threads to
// concurrently move from the same object).
// However, calling lockBoth() on two entangled objects at once is
// thread-safe.
// Note also that this may wait indefinitely; it's not the usual non-blocking
// tryLock().
bool tryLockBoth() {
// Try to acquire the local lock. We have to start locally, since local
// addresses are the only ones we know are safe at first. The rule is, you
// have to hold *both* locks to write any of either entangled object's
// metadata, but need only one to read it.
int expected = kUnlocked;
if (!stateMachine.compare_exchange_weak(expected, kLocked)) {
return false;
}
// Having *either* object local-locked protects the data in both objects.
// Once we hold our lock, no control data can change, in either object.
if (dual == nullptr) {
return true;
}
expected = kUnlocked;
if (dual->stateMachine.compare_exchange_strong(expected, kLocked)) {
return true;
}
// We got here, and so hit the race; we're deadlocked if we stick to
// locking. Revert to address-ordering. Note that address-ordering would
// not be safe on its own, because of the lifetime issues involved; the
// addresses here are only stable *because* we know both sides are locked,
// and because of the invariant that you must hold both locks to modify
// either piece of data.
if ((uintptr_t)this < (uintptr_t)dual) {
// I get to win the race. I'll acquire the locks, but have to make sure
// my memory stays valid until the other thread acknowledges its loss.
while (stateMachine.load() != kLockedAndLossAcknowledged) {
// Spin.
}
stateMachine.store(kLocked);
return true;
} else {
// I lose the race, but have to coordinate with the winning thread, so
// that it knows that I'm not about to try to touch it's data
dual->stateMachine.store(kLockedAndLossAcknowledged);
return false;
}
}
void lockBoth() {
while (!tryLockBoth()) {
// Spin. But, note that all the unbounded spinning in tryLockBoth can be
// straightforwardly futex-ified. There's a potentialy starvation issue
// here, but note that it can be dealt with by adding a "priority" bit to
// the state machine (i.e. if my priority bit is set, the thread for whom
// I'm the local member of the pair gets to win the race, rather than
// using address-ordering).
}
}
void unlockBoth() {
// Note that unlocking locally and then remotely is the right order. There
// are no concurrent accesses to this object (as an API constraint -- lock
// and unlock are not thread safe!), and no other thread will touch the
// other object so long as its locked. Going in the other order could let
// another thread incorrectly think we're going down the deadlock-avoidance
// path in tryLock().
stateMachine.store(kUnlocked);
if (dual != nullptr) {
dual->stateMachine.store(kUnlocked);
}
}
entangled() = delete;
entangled(const entangled&) = delete;
entangled& operator=(const entangled&) = delete;
entangled& operator=(entangled&&) = delete;
explicit entangled(T t)
: t(std::move(t)), dual(nullptr), stateMachine(kUnlocked) {}
entangled(entangled&& other)
: stateMachine((other.lockBoth(), kLocked)),
t(std::move(other.t)),
dual(std::move(other.dual)) {
// Note that, above, we initialized stateMachine to the locked state; the
// address of this object hasn't escaped yet, and won't (until we unlock
// the dual), so it doesn't *really* matter, but it's conceptually helpful
// to maintain that invariant.
// Update our dual's data.
if (dual != nullptr) {
dual->dual = this;
}
// Update other's data.
other.dual = nullptr;
// unlock other so that its destructor can complete
other.stateMachine.store(kUnlocked);
// We locked on other, but will unlock on *this. The locking protocol
// ensured that no accesses to other will occur after lock() returns, and
// since then we updated dual's dual to be us.
unlockBoth();
}
~entangled() {
lockBoth();
if (dual != nullptr) {
dual->dual = nullptr;
}
unlockBoth();
}
// Must unlock later even if dual is nullptr. This is fixable.
Dual* lockPointerToDual() {
lockBoth();
return !!dual ? std::addressof(dual->t) : nullptr;
}
void unlockPointerToDual() {
unlockBoth();
}
};
#endif
template <class First, class Second>
auto entangle(First f, Second s)
-> std::pair<entangled<First, Second>, entangled<Second, First>> {
entangled<First, Second> ef(std::move(f));
entangled<Second, First> es(std::move(s));
ef.dual = std::addressof(es);
es.dual = std::addressof(ef);
return {std::move(ef), std::move(es)};
}
} // namespace pushmi
...@@ -33,9 +33,9 @@ void set_stopping(S& s) noexcept(noexcept(s.stopping())) { ...@@ -33,9 +33,9 @@ void set_stopping(S& s) noexcept(noexcept(s.stopping())) {
s.stopping(); s.stopping();
} }
PUSHMI_TEMPLATE (class S, class Up) PUSHMI_TEMPLATE (class S, class Up)
(requires requires (std::declval<S&>().starting(std::declval<Up&>()))) (requires requires (std::declval<S&>().starting(std::declval<Up>())))
void set_starting(S& s, Up& up) noexcept(noexcept(s.starting(up))) { void set_starting(S& s, Up up) noexcept(noexcept(s.starting(std::move(up)))) {
s.starting(up); s.starting(std::move(up));
} }
PUSHMI_TEMPLATE (class SD, class Out) PUSHMI_TEMPLATE (class SD, class Out)
...@@ -107,10 +107,10 @@ void set_stopping(std::reference_wrapper<S> s) noexcept( ...@@ -107,10 +107,10 @@ void set_stopping(std::reference_wrapper<S> s) noexcept(
set_stopping(s.get()); set_stopping(s.get());
} }
PUSHMI_TEMPLATE (class S, class Up) PUSHMI_TEMPLATE (class S, class Up)
(requires requires ( set_starting(std::declval<S&>(), std::declval<Up&>()) )) (requires requires ( set_starting(std::declval<S&>(), std::declval<Up>()) ))
void set_starting(std::reference_wrapper<S> s, Up& up) noexcept( void set_starting(std::reference_wrapper<S> s, Up up) noexcept(
noexcept(set_starting(s.get(), up))) { noexcept(set_starting(s.get(), std::move(up)))) {
set_starting(s.get(), up); set_starting(s.get(), std::move(up));
} }
PUSHMI_TEMPLATE (class SD, class Out) PUSHMI_TEMPLATE (class SD, class Out)
(requires requires ( submit(std::declval<SD&>(), std::declval<Out>()) )) (requires requires ( submit(std::declval<SD&>(), std::declval<Out>()) ))
...@@ -188,13 +188,13 @@ struct set_stopping_fn { ...@@ -188,13 +188,13 @@ struct set_stopping_fn {
struct set_starting_fn { struct set_starting_fn {
PUSHMI_TEMPLATE (class S, class Up) PUSHMI_TEMPLATE (class S, class Up)
(requires requires ( (requires requires (
set_starting(std::declval<S&>(), std::declval<Up&>()), set_starting(std::declval<S&>(), std::declval<Up>()),
set_error(std::declval<S&>(), std::current_exception()) set_error(std::declval<S&>(), std::current_exception())
)) ))
void operator()(S&& s, Up& up) const void operator()(S&& s, Up up) const
noexcept(noexcept(set_starting(s, up))) { noexcept(noexcept(set_starting(s, std::move(up)))) {
try { try {
set_starting(s, up); set_starting(s, std::move(up));
} catch (...) { } catch (...) {
set_error(s, std::current_exception()); set_error(s, std::current_exception());
} }
......
...@@ -25,13 +25,13 @@ class flow_single<V, PE, E> { ...@@ -25,13 +25,13 @@ class flow_single<V, PE, E> {
static void s_error(data&, E) noexcept { std::terminate(); } static void s_error(data&, E) noexcept { std::terminate(); }
static void s_value(data&, V) {} static void s_value(data&, V) {}
static void s_stopping(data&) noexcept {} static void s_stopping(data&) noexcept {}
static void s_starting(data&, any_none<PE>&) {} static void s_starting(data&, any_none<PE>) {}
void (*op_)(data&, data*) = vtable::s_op; void (*op_)(data&, data*) = vtable::s_op;
void (*done_)(data&) = vtable::s_done; void (*done_)(data&) = vtable::s_done;
void (*error_)(data&, E) noexcept = vtable::s_error; void (*error_)(data&, E) noexcept = vtable::s_error;
void (*value_)(data&, V) = vtable::s_value; void (*value_)(data&, V) = vtable::s_value;
void (*stopping_)(data&) noexcept = vtable::s_stopping; void (*stopping_)(data&) noexcept = vtable::s_stopping;
void (*starting_)(data&, any_none<PE>&) = vtable::s_starting; void (*starting_)(data&, any_none<PE>) = vtable::s_starting;
}; };
static constexpr vtable const noop_ {}; static constexpr vtable const noop_ {};
vtable const* vptr_ = &noop_; vtable const* vptr_ = &noop_;
...@@ -55,8 +55,8 @@ class flow_single<V, PE, E> { ...@@ -55,8 +55,8 @@ class flow_single<V, PE, E> {
static void stopping(data& src) noexcept { static void stopping(data& src) noexcept {
::pushmi::set_stopping(*static_cast<Wrapped*>(src.pobj_)); ::pushmi::set_stopping(*static_cast<Wrapped*>(src.pobj_));
} }
static void starting(data& src, any_none<PE>& up) { static void starting(data& src, any_none<PE> up) {
::pushmi::set_starting(*static_cast<Wrapped*>(src.pobj_), up); ::pushmi::set_starting(*static_cast<Wrapped*>(src.pobj_), std::move(up));
} }
}; };
static const vtable vtbl{s::op, s::done, s::error, s::value, s::stopping, s::starting}; static const vtable vtbl{s::op, s::done, s::error, s::value, s::stopping, s::starting};
...@@ -86,8 +86,8 @@ class flow_single<V, PE, E> { ...@@ -86,8 +86,8 @@ class flow_single<V, PE, E> {
static void stopping(data& src) noexcept { static void stopping(data& src) noexcept {
::pushmi::set_stopping(*static_cast<Wrapped*>((void*)src.buffer_)); ::pushmi::set_stopping(*static_cast<Wrapped*>((void*)src.buffer_));
} }
static void starting(data& src, any_none<PE>& up) { static void starting(data& src, any_none<PE> up) {
::pushmi::set_starting(*static_cast<Wrapped*>((void*)src.buffer_), up); ::pushmi::set_starting(*static_cast<Wrapped*>((void*)src.buffer_), std::move(up));
} }
}; };
static const vtable vtbl{s::op, s::done, s::error, s::value, s::stopping, s::starting}; static const vtable vtbl{s::op, s::done, s::error, s::value, s::stopping, s::starting};
...@@ -130,8 +130,8 @@ public: ...@@ -130,8 +130,8 @@ public:
void stopping() noexcept { void stopping() noexcept {
vptr_->stopping_(data_); vptr_->stopping_(data_);
} }
void starting(any_none<PE>& up) { void starting(any_none<PE> up) {
vptr_->starting_(data_, up); vptr_->starting_(data_, std::move(up));
} }
}; };
...@@ -199,9 +199,9 @@ class flow_single<VF, EF, DF, StpF, StrtF> { ...@@ -199,9 +199,9 @@ class flow_single<VF, EF, DF, StpF, StrtF> {
stpf_(); stpf_();
} }
PUSHMI_TEMPLATE(class Up) PUSHMI_TEMPLATE(class Up)
(requires Receiver<Up, is_none<>> && Invocable<StrtF&, Up&>) (requires Receiver<Up, is_none<>> && Invocable<StrtF&, Up&&>)
void starting(Up& up) { void starting(Up&& up) {
strtf_(up); strtf_( (Up &&) up);
} }
}; };
...@@ -271,9 +271,9 @@ class flow_single<Data, DVF, DEF, DDF, DStpF, DStrtF> { ...@@ -271,9 +271,9 @@ class flow_single<Data, DVF, DEF, DDF, DStpF, DStrtF> {
stpf_(data_); stpf_(data_);
} }
PUSHMI_TEMPLATE (class Up) PUSHMI_TEMPLATE (class Up)
(requires Invocable<DStrtF&, Data&, Up&>) (requires Invocable<DStrtF&, Data&, Up&&>)
void starting(Up& up) { void starting(Up&& up) {
strtf_(data_, up); strtf_(data_, (Up &&) up);
} }
}; };
......
...@@ -225,6 +225,9 @@ class trampoline { ...@@ -225,6 +225,9 @@ class trampoline {
auto item = std::move(pending(pending_store).front()); auto item = std::move(pending(pending_store).front());
pending(pending_store).pop_front(); pending(pending_store).pop_front();
auto& when = std::get<0>(item); auto& when = std::get<0>(item);
if (when > trampoline<E>::now()) {
std::this_thread::sleep_until(when);
}
auto& what = std::get<1>(item); auto& what = std::get<1>(item);
any_time_executor_ref<error_type, time_point> anythis{that}; any_time_executor_ref<error_type, time_point> anythis{that};
::pushmi::set_value(what, anythis); ::pushmi::set_value(what, anythis);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment