Commit 4d980ea0 authored by Eric Niebler's avatar Eric Niebler Committed by Facebook Github Bot

Merge pull request #45 from facebookresearch/constrained-lambdas

fbshipit-source-id: 6d910ea36fd6b20f97723517f5cedc472fb1ba8d
parent 492ce4ff
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
using namespace pushmi::aliases; using namespace pushmi::aliases;
struct inline_executor_none;
template<class R> template<class R>
struct countdown { struct countdown {
countdown(int& c) countdown(int& c)
...@@ -41,6 +43,15 @@ using countdownsingle = countdown<mi::make_single_fn>; ...@@ -41,6 +43,15 @@ using countdownsingle = countdown<mi::make_single_fn>;
using countdownflowsingle = countdown<mi::make_flow_single_fn>; using countdownflowsingle = countdown<mi::make_flow_single_fn>;
using countdownmany = countdown<mi::make_many_fn>; using countdownmany = countdown<mi::make_many_fn>;
struct countdownnone {
countdownnone(int& c)
: counter(&c) {}
int* counter;
void operator()() const;
};
struct inline_executor { struct inline_executor {
using properties = mi::property_set<mi::is_sender<>, mi::is_single<>>; using properties = mi::property_set<mi::is_sender<>, mi::is_single<>>;
template<class Out> template<class Out>
...@@ -49,21 +60,29 @@ struct inline_executor { ...@@ -49,21 +60,29 @@ struct inline_executor {
} }
}; };
struct inline_executor_none {
using properties = mi::property_set<mi::is_sender<>, mi::is_none<>>;
template<class Out>
void submit(Out out) {
::mi::set_done(out);
}
};
void countdownnone::operator()() const {
if (--*counter > 0) {
inline_executor_none{} | op::submit(mi::make_none(*this));
}
}
template<class CancellationFactory>
struct inline_executor_flow_single { struct inline_executor_flow_single {
CancellationFactory cf;
using properties = mi::property_set<mi::is_sender<>, mi::is_flow<>, mi::is_single<>>; using properties = mi::property_set<mi::is_sender<>, mi::is_flow<>, mi::is_single<>>;
template<class Out> template<class Out>
void submit(Out out) { void submit(Out out) {
// boolean cancellation auto tokens = cf();
bool stop = false;
auto set_stop = [](auto& e) {
auto stop = e.lockPointerToDual();
if (!!stop) {
*stop = true;
}
e.unlockPointerToDual();
};
auto tokens = mi::entangle(stop, set_stop);
using Stopper = decltype(tokens.second); using Stopper = decltype(tokens.second);
struct Data : mi::none<> { struct Data : mi::none<> {
...@@ -73,16 +92,19 @@ struct inline_executor_flow_single { ...@@ -73,16 +92,19 @@ struct inline_executor_flow_single {
auto up = mi::MAKE(none)( auto up = mi::MAKE(none)(
Data{std::move(tokens.second)}, Data{std::move(tokens.second)},
[](auto& data, auto e) noexcept { [](auto& data, auto e) noexcept {
data.stopper.t(data.stopper); auto both = lock_both(data.stopper);
(*(both.first))(both.second);
}, },
[](auto& data) { [](auto& data) {
data.stopper.t(data.stopper); auto both = lock_both(data.stopper);
(*(both.first))(both.second);
}); });
// pass reference for cancellation. // pass reference for cancellation.
::mi::set_starting(out, std::move(up)); ::mi::set_starting(out, std::move(up));
if (!tokens.first.t) { auto both = lock_both(tokens.first);
if (!!both.first && !*(both.first)) {
::mi::set_value(out, *this); ::mi::set_value(out, *this);
} else { } else {
// cancellation is not an error // cancellation is not an error
...@@ -91,6 +113,45 @@ struct inline_executor_flow_single { ...@@ -91,6 +113,45 @@ struct inline_executor_flow_single {
} }
}; };
struct shared_cancellation_factory{
auto operator()(){
// boolean cancellation
mi::moving_atomic<bool> stop = false;
auto set_stop = [](auto& stop) {
if (!!stop) {
stop->store(true);
}
};
return mi::shared_entangle(std::move(stop), set_stop);
}
};
using inline_executor_flow_single_shared = inline_executor_flow_single<shared_cancellation_factory>;
struct entangled_cancellation_factory{
auto operator()(){
// boolean cancellation
mi::moving_atomic<bool> stop = false;
auto set_stop = [](auto& stop) {
if (!!stop) {
stop->store(true);
}
};
return mi::entangle(std::move(stop), set_stop);
}
};
using inline_executor_flow_single_entangled = inline_executor_flow_single<entangled_cancellation_factory>;
struct inline_executor_flow_single_ignore {
using properties = mi::property_set<mi::is_sender<>, mi::is_flow<>, mi::is_single<>>;
template<class Out>
void submit(Out out) {
// pass reference for cancellation.
::mi::set_starting(out, mi::none<>{});
::mi::set_value(out, *this);
}
};
struct inline_executor_many { struct inline_executor_many {
using properties = mi::property_set<mi::is_sender<>, mi::is_many<>>; using properties = mi::property_set<mi::is_sender<>, mi::is_many<>>;
template<class Out> template<class Out>
...@@ -104,6 +165,18 @@ struct inline_executor_many { ...@@ -104,6 +165,18 @@ struct inline_executor_many {
#define concept Concept #define concept Concept
#include <nonius/nonius.h++> #include <nonius/nonius.h++>
NONIUS_BENCHMARK("inline 10 none", [](nonius::chronometer meter){
int counter = 0;
auto ie = inline_executor_none{};
using IE = decltype(ie);
countdownnone none{counter};
meter.measure([&]{
counter = 10;
ie | op::submit(mi::make_none(none));
return counter;
});
})
NONIUS_BENCHMARK("inline 10 single", [](nonius::chronometer meter){ NONIUS_BENCHMARK("inline 10 single", [](nonius::chronometer meter){
int counter = 0; int counter = 0;
auto ie = inline_executor{}; auto ie = inline_executor{};
...@@ -116,26 +189,50 @@ NONIUS_BENCHMARK("inline 10 single", [](nonius::chronometer meter){ ...@@ -116,26 +189,50 @@ NONIUS_BENCHMARK("inline 10 single", [](nonius::chronometer meter){
}); });
}) })
NONIUS_BENCHMARK("inline 10 flow_single", [](nonius::chronometer meter){ NONIUS_BENCHMARK("inline 10 many", [](nonius::chronometer meter){
int counter = 0; int counter = 0;
auto ie = inline_executor_flow_single{}; auto ie = inline_executor_many{};
using IE = decltype(ie);
countdownmany many{counter};
meter.measure([&]{
counter = 10;
ie | op::submit(mi::make_many(many));
return counter;
});
})
NONIUS_BENCHMARK("inline 10 flow_single shared", [](nonius::chronometer meter){
int counter = 0;
auto ie = inline_executor_flow_single_shared{};
using IE = decltype(ie); using IE = decltype(ie);
countdownflowsingle flowsingle{counter}; countdownflowsingle flowsingle{counter};
meter.measure([&]{ meter.measure([&]{
counter = 10; counter = 10;
ie | op::submit(mi::make_flow_single(on_value(flowsingle))); ie | op::submit(mi::make_flow_single(flowsingle));
return counter; return counter;
}); });
}) })
NONIUS_BENCHMARK("inline 10 many", [](nonius::chronometer meter){ NONIUS_BENCHMARK("inline 10 flow_single entangle", [](nonius::chronometer meter){
int counter = 0; int counter = 0;
auto ie = inline_executor_many{}; auto ie = inline_executor_flow_single_entangled{};
using IE = decltype(ie); using IE = decltype(ie);
countdownmany many{counter}; countdownflowsingle flowsingle{counter};
meter.measure([&]{ meter.measure([&]{
counter = 10; counter = 10;
ie | op::submit(mi::make_many(many)); ie | op::submit(mi::make_flow_single(flowsingle));
return counter;
});
})
NONIUS_BENCHMARK("inline 10 flow_single ignore cancellation", [](nonius::chronometer meter){
int counter = 0;
auto ie = inline_executor_flow_single_ignore{};
using IE = decltype(ie);
countdownflowsingle flowsingle{counter};
meter.measure([&]{
counter = 10;
ie | op::submit(mi::make_flow_single(flowsingle));
return counter; return counter;
}); });
}) })
......
...@@ -1205,43 +1205,40 @@ namespace detail { ...@@ -1205,43 +1205,40 @@ namespace detail {
namespace pushmi { namespace pushmi {
#if 0
template <class T, class Dual> // template <class T, class Dual>
struct entangled { // struct entangled {
T t; // T t;
entangled<Dual, T>* dual; // entangled<Dual, T>* dual;
//
~entangled() { // ~entangled() {
if (!!dual) { // if (!!dual) {
dual->dual = nullptr; // dual->dual = nullptr;
} // }
} // }
explicit entangled(T t) : t(std::move(t)), dual(nullptr) {} // explicit entangled(T t) : t(std::move(t)), dual(nullptr) {}
entangled(entangled&& o) : t(std::move(o.t)), dual(o.dual) { // entangled(entangled&& o) : t(std::move(o.t)), dual(o.dual) {
o.dual = nullptr; // o.dual = nullptr;
if (!!dual) { // if (!!dual) {
dual->dual = this; // dual->dual = this;
} // }
} // }
//
entangled() = delete; // entangled() = delete;
entangled(const entangled&) = delete; // entangled(const entangled&) = delete;
entangled& operator=(const entangled&) = delete; // entangled& operator=(const entangled&) = delete;
entangled& operator=(entangled&&) = delete; // entangled& operator=(entangled&&) = delete;
//
Dual* lockPointerToDual() { // Dual* lockPointerToDual() {
if (!!dual) { // if (!!dual) {
return std::addressof(dual->t); // return std::addressof(dual->t);
} // }
return nullptr; // return nullptr;
} // }
//
void unlockPointerToDual() { // void unlockPointerToDual() {
} // }
}; // };
#else
// This class can be used to keep a pair of values with pointers to each other // 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 // in sync, even when both objects are allowed to move. Ordinarily you'd do this
...@@ -1284,7 +1281,7 @@ struct entangled { ...@@ -1284,7 +1281,7 @@ struct entangled {
// have to hold *both* locks to write any of either entangled object's // have to hold *both* locks to write any of either entangled object's
// metadata, but need only one to read it. // metadata, but need only one to read it.
int expected = kUnlocked; int expected = kUnlocked;
if (!stateMachine.compare_exchange_weak(expected, kLocked)) { if (!stateMachine.compare_exchange_weak(expected, kLocked, std::memory_order_seq_cst, std::memory_order_relaxed)) {
return false; return false;
} }
// Having *either* object local-locked protects the data in both objects. // Having *either* object local-locked protects the data in both objects.
...@@ -1293,7 +1290,7 @@ struct entangled { ...@@ -1293,7 +1290,7 @@ struct entangled {
return true; return true;
} }
expected = kUnlocked; expected = kUnlocked;
if (dual->stateMachine.compare_exchange_strong(expected, kLocked)) { if (dual->stateMachine.compare_exchange_strong(expected, kLocked, std::memory_order_seq_cst)) {
return true; return true;
} }
// We got here, and so hit the race; we're deadlocked if we stick to // We got here, and so hit the race; we're deadlocked if we stick to
...@@ -1305,15 +1302,15 @@ struct entangled { ...@@ -1305,15 +1302,15 @@ struct entangled {
if ((uintptr_t)this < (uintptr_t)dual) { if ((uintptr_t)this < (uintptr_t)dual) {
// I get to win the race. I'll acquire the locks, but have to make sure // 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. // my memory stays valid until the other thread acknowledges its loss.
while (stateMachine.load() != kLockedAndLossAcknowledged) { while (stateMachine.load(std::memory_order_relaxed) != kLockedAndLossAcknowledged) {
// Spin. // Spin.
} }
stateMachine.store(kLocked); stateMachine.store(kLocked, std::memory_order_relaxed);
return true; return true;
} else { } else {
// I lose the race, but have to coordinate with the winning thread, so // 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 // that it knows that I'm not about to try to touch it's data
dual->stateMachine.store(kLockedAndLossAcknowledged); dual->stateMachine.store(kLockedAndLossAcknowledged, std::memory_order_relaxed);
return false; return false;
} }
} }
...@@ -1336,9 +1333,9 @@ struct entangled { ...@@ -1336,9 +1333,9 @@ struct entangled {
// other object so long as its locked. Going in the other order could let // 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 // another thread incorrectly think we're going down the deadlock-avoidance
// path in tryLock(). // path in tryLock().
stateMachine.store(kUnlocked); stateMachine.store(kUnlocked, std::memory_order_release);
if (dual != nullptr) { if (dual != nullptr) {
dual->stateMachine.store(kUnlocked); dual->stateMachine.store(kUnlocked, std::memory_order_release);
} }
} }
...@@ -1392,11 +1389,13 @@ struct entangled { ...@@ -1392,11 +1389,13 @@ struct entangled {
unlockBoth(); unlockBoth();
} }
}; };
#endif
template <class First, class Second>
using entangled_pair = std::pair<entangled<First, Second>, entangled<Second, First>>;
template <class First, class Second> template <class First, class Second>
auto entangle(First f, Second s) auto entangle(First f, Second s)
-> std::pair<entangled<First, Second>, entangled<Second, First>> { -> entangled_pair<First, Second> {
entangled<First, Second> ef(std::move(f)); entangled<First, Second> ef(std::move(f));
entangled<Second, First> es(std::move(s)); entangled<Second, First> es(std::move(s));
ef.dual = std::addressof(es); ef.dual = std::addressof(es);
...@@ -1404,6 +1403,85 @@ auto entangle(First f, Second s) ...@@ -1404,6 +1403,85 @@ auto entangle(First f, Second s)
return {std::move(ef), std::move(es)}; return {std::move(ef), std::move(es)};
} }
template <class T, class Dual>
struct locked_entangled_pair : std::pair<T*, Dual*> {
entangled<T, Dual>* e;
~locked_entangled_pair(){if (!!e) {e->unlockBoth();}}
explicit locked_entangled_pair(entangled<T, Dual>& e) : e(std::addressof(e)){
this->e->lockBoth();
this->first = std::addressof(this->e->t);
this->second = !!this->e->dual ? std::addressof(this->e->dual->t) : nullptr;
}
locked_entangled_pair() = delete;
locked_entangled_pair(const locked_entangled_pair&) = delete;
locked_entangled_pair& operator=(const locked_entangled_pair&) = delete;
locked_entangled_pair(locked_entangled_pair&& o) : std::pair<T*, Dual*>(o), e(o.e) {o.e = nullptr;};
locked_entangled_pair& operator=(locked_entangled_pair&& o){
static_cast<std::pair<T*, Dual*>&>(*this) = static_cast<std::pair<T*, Dual*>&&>(o);
e = o.e;
o.e = nullptr;
return *this;
}
};
template <class T, class Dual>
locked_entangled_pair<T, Dual> lock_both(entangled<T, Dual>& e){
return locked_entangled_pair<T, Dual>{e};
}
template <class T, class Dual>
struct shared_entangled : std::shared_ptr<T> {
Dual* dual;
template<class P>
explicit shared_entangled(std::shared_ptr<P>& p, T& t, Dual& d) : std::shared_ptr<T>(p, std::addressof(t)), dual(std::addressof(d)){
}
shared_entangled() = delete;
shared_entangled(const shared_entangled&) = delete;
shared_entangled& operator=(const shared_entangled&) = delete;
shared_entangled(shared_entangled&&) = default;
shared_entangled& operator=(shared_entangled&&) = default;
};
template <class First, class Second>
using shared_entangled_pair = std::pair<shared_entangled<First, Second>, shared_entangled<Second, First>>;
template <class First, class Second>
auto shared_entangle(First f, Second s)
-> shared_entangled_pair<First, Second> {
auto p = std::make_shared<std::pair<First, Second>>(std::move(f), std::move(s));
shared_entangled<First, Second> ef(p, p->first, p->second);
shared_entangled<Second, First> es(p, p->second, p->first);
return {std::move(ef), std::move(es)};
}
template <class T, class Dual>
struct locked_shared_entangled_pair : std::pair<T*, Dual*> {
shared_entangled<T, Dual> e;
explicit locked_shared_entangled_pair(shared_entangled<T, Dual>& e) : e(std::move(e)){
this->first = this->e.get();
this->second = this->e.dual;
}
locked_shared_entangled_pair() = delete;
locked_shared_entangled_pair(const locked_shared_entangled_pair&) = delete;
locked_shared_entangled_pair& operator=(const locked_shared_entangled_pair&) = delete;
locked_shared_entangled_pair(locked_shared_entangled_pair&&) = default;
locked_shared_entangled_pair& operator=(locked_shared_entangled_pair&&) = default;
};
template <class T, class Dual>
locked_shared_entangled_pair<T, Dual> lock_both(shared_entangled<T, Dual>& e){
return locked_shared_entangled_pair<T, Dual>{e};
}
// external synchronization required for move and delete
template<class T>
struct moving_atomic : std::atomic<T> {
using std::atomic<T>::atomic;
moving_atomic(moving_atomic&& o) : std::atomic<T>(o.load()) {}
};
} // namespace pushmi } // namespace pushmi
//#pragma once //#pragma once
// Copyright (c) 2018-present, Facebook, Inc. // Copyright (c) 2018-present, Facebook, Inc.
...@@ -1441,8 +1519,8 @@ void set_next(S& s, V&& v) noexcept(noexcept(s.next((V&&) v))) { ...@@ -1441,8 +1519,8 @@ void set_next(S& s, V&& v) noexcept(noexcept(s.next((V&&) v))) {
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(std::move(up)))) { void set_starting(S& s, Up&& up) noexcept(noexcept(s.starting((Up&&) up))) {
s.starting(std::move(up)); s.starting((Up&&) up);
} }
PUSHMI_TEMPLATE (class SD, class Out) PUSHMI_TEMPLATE (class SD, class Out)
...@@ -1515,9 +1593,9 @@ void set_next(std::reference_wrapper<S> s, V&& v) noexcept( ...@@ -1515,9 +1593,9 @@ void set_next(std::reference_wrapper<S> s, V&& v) noexcept(
} }
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(), std::move(up)))) { noexcept(set_starting(s.get(), (Up&&) up))) {
set_starting(s.get(), std::move(up)); set_starting(s.get(), (Up&&) 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>()) ))
...@@ -1604,10 +1682,10 @@ struct set_starting_fn { ...@@ -1604,10 +1682,10 @@ struct set_starting_fn {
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, std::move(up)))) { noexcept(noexcept(set_starting(s, (Up&&) up))) {
try { try {
set_starting(s, std::move(up)); set_starting(s, (Up&&) up);
} catch (...) { } catch (...) {
set_error(s, std::current_exception()); set_error(s, std::current_exception());
} }
...@@ -2361,8 +2439,8 @@ struct passDStrtF { ...@@ -2361,8 +2439,8 @@ struct passDStrtF {
(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, std::move(up)); ::pushmi::set_starting(out, (Up&&) up);
} }
}; };
......
...@@ -117,8 +117,8 @@ struct passDStrtF { ...@@ -117,8 +117,8 @@ struct passDStrtF {
(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, std::move(up)); ::pushmi::set_starting(out, (Up&&) up);
} }
}; };
......
...@@ -8,43 +8,40 @@ ...@@ -8,43 +8,40 @@
namespace pushmi { namespace pushmi {
#if 0
template <class T, class Dual> // template <class T, class Dual>
struct entangled { // struct entangled {
T t; // T t;
entangled<Dual, T>* dual; // entangled<Dual, T>* dual;
//
~entangled() { // ~entangled() {
if (!!dual) { // if (!!dual) {
dual->dual = nullptr; // dual->dual = nullptr;
} // }
} // }
explicit entangled(T t) : t(std::move(t)), dual(nullptr) {} // explicit entangled(T t) : t(std::move(t)), dual(nullptr) {}
entangled(entangled&& o) : t(std::move(o.t)), dual(o.dual) { // entangled(entangled&& o) : t(std::move(o.t)), dual(o.dual) {
o.dual = nullptr; // o.dual = nullptr;
if (!!dual) { // if (!!dual) {
dual->dual = this; // dual->dual = this;
} // }
} // }
//
entangled() = delete; // entangled() = delete;
entangled(const entangled&) = delete; // entangled(const entangled&) = delete;
entangled& operator=(const entangled&) = delete; // entangled& operator=(const entangled&) = delete;
entangled& operator=(entangled&&) = delete; // entangled& operator=(entangled&&) = delete;
//
Dual* lockPointerToDual() { // Dual* lockPointerToDual() {
if (!!dual) { // if (!!dual) {
return std::addressof(dual->t); // return std::addressof(dual->t);
} // }
return nullptr; // return nullptr;
} // }
//
void unlockPointerToDual() { // void unlockPointerToDual() {
} // }
}; // };
#else
// This class can be used to keep a pair of values with pointers to each other // 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 // in sync, even when both objects are allowed to move. Ordinarily you'd do this
...@@ -87,7 +84,7 @@ struct entangled { ...@@ -87,7 +84,7 @@ struct entangled {
// have to hold *both* locks to write any of either entangled object's // have to hold *both* locks to write any of either entangled object's
// metadata, but need only one to read it. // metadata, but need only one to read it.
int expected = kUnlocked; int expected = kUnlocked;
if (!stateMachine.compare_exchange_weak(expected, kLocked)) { if (!stateMachine.compare_exchange_weak(expected, kLocked, std::memory_order_seq_cst, std::memory_order_relaxed)) {
return false; return false;
} }
// Having *either* object local-locked protects the data in both objects. // Having *either* object local-locked protects the data in both objects.
...@@ -96,7 +93,7 @@ struct entangled { ...@@ -96,7 +93,7 @@ struct entangled {
return true; return true;
} }
expected = kUnlocked; expected = kUnlocked;
if (dual->stateMachine.compare_exchange_strong(expected, kLocked)) { if (dual->stateMachine.compare_exchange_strong(expected, kLocked, std::memory_order_seq_cst)) {
return true; return true;
} }
// We got here, and so hit the race; we're deadlocked if we stick to // We got here, and so hit the race; we're deadlocked if we stick to
...@@ -108,15 +105,15 @@ struct entangled { ...@@ -108,15 +105,15 @@ struct entangled {
if ((uintptr_t)this < (uintptr_t)dual) { if ((uintptr_t)this < (uintptr_t)dual) {
// I get to win the race. I'll acquire the locks, but have to make sure // 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. // my memory stays valid until the other thread acknowledges its loss.
while (stateMachine.load() != kLockedAndLossAcknowledged) { while (stateMachine.load(std::memory_order_relaxed) != kLockedAndLossAcknowledged) {
// Spin. // Spin.
} }
stateMachine.store(kLocked); stateMachine.store(kLocked, std::memory_order_relaxed);
return true; return true;
} else { } else {
// I lose the race, but have to coordinate with the winning thread, so // 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 // that it knows that I'm not about to try to touch it's data
dual->stateMachine.store(kLockedAndLossAcknowledged); dual->stateMachine.store(kLockedAndLossAcknowledged, std::memory_order_relaxed);
return false; return false;
} }
} }
...@@ -139,9 +136,9 @@ struct entangled { ...@@ -139,9 +136,9 @@ struct entangled {
// other object so long as its locked. Going in the other order could let // 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 // another thread incorrectly think we're going down the deadlock-avoidance
// path in tryLock(). // path in tryLock().
stateMachine.store(kUnlocked); stateMachine.store(kUnlocked, std::memory_order_release);
if (dual != nullptr) { if (dual != nullptr) {
dual->stateMachine.store(kUnlocked); dual->stateMachine.store(kUnlocked, std::memory_order_release);
} }
} }
...@@ -195,11 +192,13 @@ struct entangled { ...@@ -195,11 +192,13 @@ struct entangled {
unlockBoth(); unlockBoth();
} }
}; };
#endif
template <class First, class Second>
using entangled_pair = std::pair<entangled<First, Second>, entangled<Second, First>>;
template <class First, class Second> template <class First, class Second>
auto entangle(First f, Second s) auto entangle(First f, Second s)
-> std::pair<entangled<First, Second>, entangled<Second, First>> { -> entangled_pair<First, Second> {
entangled<First, Second> ef(std::move(f)); entangled<First, Second> ef(std::move(f));
entangled<Second, First> es(std::move(s)); entangled<Second, First> es(std::move(s));
ef.dual = std::addressof(es); ef.dual = std::addressof(es);
...@@ -207,4 +206,83 @@ auto entangle(First f, Second s) ...@@ -207,4 +206,83 @@ auto entangle(First f, Second s)
return {std::move(ef), std::move(es)}; return {std::move(ef), std::move(es)};
} }
template <class T, class Dual>
struct locked_entangled_pair : std::pair<T*, Dual*> {
entangled<T, Dual>* e;
~locked_entangled_pair(){if (!!e) {e->unlockBoth();}}
explicit locked_entangled_pair(entangled<T, Dual>& e) : e(std::addressof(e)){
this->e->lockBoth();
this->first = std::addressof(this->e->t);
this->second = !!this->e->dual ? std::addressof(this->e->dual->t) : nullptr;
}
locked_entangled_pair() = delete;
locked_entangled_pair(const locked_entangled_pair&) = delete;
locked_entangled_pair& operator=(const locked_entangled_pair&) = delete;
locked_entangled_pair(locked_entangled_pair&& o) : std::pair<T*, Dual*>(o), e(o.e) {o.e = nullptr;};
locked_entangled_pair& operator=(locked_entangled_pair&& o){
static_cast<std::pair<T*, Dual*>&>(*this) = static_cast<std::pair<T*, Dual*>&&>(o);
e = o.e;
o.e = nullptr;
return *this;
}
};
template <class T, class Dual>
locked_entangled_pair<T, Dual> lock_both(entangled<T, Dual>& e){
return locked_entangled_pair<T, Dual>{e};
}
template <class T, class Dual>
struct shared_entangled : std::shared_ptr<T> {
Dual* dual;
template<class P>
explicit shared_entangled(std::shared_ptr<P>& p, T& t, Dual& d) : std::shared_ptr<T>(p, std::addressof(t)), dual(std::addressof(d)){
}
shared_entangled() = delete;
shared_entangled(const shared_entangled&) = delete;
shared_entangled& operator=(const shared_entangled&) = delete;
shared_entangled(shared_entangled&&) = default;
shared_entangled& operator=(shared_entangled&&) = default;
};
template <class First, class Second>
using shared_entangled_pair = std::pair<shared_entangled<First, Second>, shared_entangled<Second, First>>;
template <class First, class Second>
auto shared_entangle(First f, Second s)
-> shared_entangled_pair<First, Second> {
auto p = std::make_shared<std::pair<First, Second>>(std::move(f), std::move(s));
shared_entangled<First, Second> ef(p, p->first, p->second);
shared_entangled<Second, First> es(p, p->second, p->first);
return {std::move(ef), std::move(es)};
}
template <class T, class Dual>
struct locked_shared_entangled_pair : std::pair<T*, Dual*> {
shared_entangled<T, Dual> e;
explicit locked_shared_entangled_pair(shared_entangled<T, Dual>& e) : e(std::move(e)){
this->first = this->e.get();
this->second = this->e.dual;
}
locked_shared_entangled_pair() = delete;
locked_shared_entangled_pair(const locked_shared_entangled_pair&) = delete;
locked_shared_entangled_pair& operator=(const locked_shared_entangled_pair&) = delete;
locked_shared_entangled_pair(locked_shared_entangled_pair&&) = default;
locked_shared_entangled_pair& operator=(locked_shared_entangled_pair&&) = default;
};
template <class T, class Dual>
locked_shared_entangled_pair<T, Dual> lock_both(shared_entangled<T, Dual>& e){
return locked_shared_entangled_pair<T, Dual>{e};
}
// external synchronization required for move and delete
template<class T>
struct moving_atomic : std::atomic<T> {
using std::atomic<T>::atomic;
moving_atomic(moving_atomic&& o) : std::atomic<T>(o.load()) {}
};
} // namespace pushmi } // namespace pushmi
...@@ -34,8 +34,8 @@ void set_next(S& s, V&& v) noexcept(noexcept(s.next((V&&) v))) { ...@@ -34,8 +34,8 @@ void set_next(S& s, V&& v) noexcept(noexcept(s.next((V&&) v))) {
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(std::move(up)))) { void set_starting(S& s, Up&& up) noexcept(noexcept(s.starting((Up&&) up))) {
s.starting(std::move(up)); s.starting((Up&&) up);
} }
PUSHMI_TEMPLATE (class SD, class Out) PUSHMI_TEMPLATE (class SD, class Out)
...@@ -108,9 +108,9 @@ void set_next(std::reference_wrapper<S> s, V&& v) noexcept( ...@@ -108,9 +108,9 @@ void set_next(std::reference_wrapper<S> s, V&& v) noexcept(
} }
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(), std::move(up)))) { noexcept(set_starting(s.get(), (Up&&) up))) {
set_starting(s.get(), std::move(up)); set_starting(s.get(), (Up&&) 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>()) ))
...@@ -197,10 +197,10 @@ struct set_starting_fn { ...@@ -197,10 +197,10 @@ struct set_starting_fn {
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, std::move(up)))) { noexcept(noexcept(set_starting(s, (Up&&) up))) {
try { try {
set_starting(s, std::move(up)); set_starting(s, (Up&&) up);
} catch (...) { } catch (...) {
set_error(s, std::current_exception()); set_error(s, std::current_exception());
} }
......
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