You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
462 lines
17 KiB
462 lines
17 KiB
#include "../test.h"
|
|
#include "rxcpp/operators/rx-combine_latest.hpp"
|
|
#include "rxcpp/operators/rx-map.hpp"
|
|
#include "rxcpp/operators/rx-take.hpp"
|
|
#include "rxcpp/operators/rx-observe_on.hpp"
|
|
#include "rxcpp/operators/rx-publish.hpp"
|
|
#include "rxcpp/operators/rx-ref_count.hpp"
|
|
|
|
#include <sstream>
|
|
|
|
SCENARIO("observe subscription", "[!hide]"){
|
|
GIVEN("observable of ints"){
|
|
WHEN("subscribe"){
|
|
auto observers = std::make_shared<std::list<rxcpp::subscriber<int>>>();
|
|
|
|
auto observable = rxcpp::observable<>::create<int>([=](rxcpp::subscriber<int> out){
|
|
auto it = observers->insert(observers->end(), out);
|
|
it->add([=](){
|
|
observers->erase(it);
|
|
});
|
|
});
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
static const int static_subscriptions = 10000;
|
|
|
|
SCENARIO("for loop subscribes to map", "[!hide][for][just][subscribe][long][perf]"){
|
|
const int& subscriptions = static_subscriptions;
|
|
GIVEN("a for loop"){
|
|
WHEN("subscribe 100K times"){
|
|
using namespace std::chrono;
|
|
typedef steady_clock clock;
|
|
|
|
auto sc = rxsc::make_current_thread();
|
|
auto w = sc.create_worker();
|
|
int runs = 10;
|
|
|
|
auto loop = [&](const rxsc::schedulable& self) {
|
|
int c = 0;
|
|
int n = 1;
|
|
auto start = clock::now();
|
|
for (int i = 0; i < subscriptions; i++) {
|
|
rx::observable<>::just(1)
|
|
.map([](int i) {
|
|
std::stringstream serializer;
|
|
serializer << i;
|
|
return serializer.str();
|
|
})
|
|
.map([](const std::string& s) {
|
|
int i;
|
|
std::stringstream(s) >> i;
|
|
return i;
|
|
})
|
|
.subscribe([&](int){
|
|
++c;
|
|
});
|
|
}
|
|
auto finish = clock::now();
|
|
auto msElapsed = duration_cast<milliseconds>(finish-start);
|
|
std::cout << "loop subscribe map : " << n << " subscribed, " << c << " on_next calls, " << msElapsed.count() << "ms elapsed, " << c / (msElapsed.count() / 1000.0) << " ops/sec" << std::endl;
|
|
|
|
if (--runs > 0) {
|
|
self();
|
|
}
|
|
};
|
|
|
|
w.schedule(loop);
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("for loop subscribes to combine_latest", "[!hide][for][just][combine_latest][subscribe][long][perf]"){
|
|
const int& subscriptions = static_subscriptions;
|
|
GIVEN("a for loop"){
|
|
WHEN("subscribe 100K times"){
|
|
using namespace std::chrono;
|
|
typedef steady_clock clock;
|
|
|
|
auto sc = rxsc::make_current_thread();
|
|
auto w = sc.create_worker();
|
|
int runs = 10;
|
|
|
|
auto loop = [&](const rxsc::schedulable& self) {
|
|
int c = 0;
|
|
int n = 1;
|
|
auto start = clock::now();
|
|
for (int i = 0; i < subscriptions; i++) {
|
|
rx::observable<>::just(1)
|
|
.combine_latest([](int i, int j) {
|
|
return i + j;
|
|
}, rx::observable<>::just(2))
|
|
.subscribe([&](int){
|
|
++c;
|
|
});
|
|
}
|
|
auto finish = clock::now();
|
|
auto msElapsed = duration_cast<milliseconds>(finish-start);
|
|
std::cout << "loop subscribe combine_latest : " << n << " subscribed, " << c << " on_next calls, " << msElapsed.count() << "ms elapsed, " << c / (msElapsed.count() / 1000.0) << " ops/sec" << std::endl;
|
|
|
|
if (--runs > 0) {
|
|
self();
|
|
}
|
|
};
|
|
|
|
w.schedule(loop);
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("synchronized range debug", "[!hide][subscribe][range][synchronize_debug][synchronize][long][perf]"){
|
|
GIVEN("range"){
|
|
WHEN("synchronized"){
|
|
using namespace std::chrono;
|
|
typedef steady_clock clock;
|
|
|
|
auto sc = rxsc::make_current_thread();
|
|
auto w = sc.create_worker();
|
|
|
|
auto es = rx::synchronize_event_loop();
|
|
|
|
const int values = 10000;
|
|
|
|
int runs = 10;
|
|
|
|
auto loop = [&](const rxsc::schedulable& self) {
|
|
std::atomic<int> c(0);
|
|
int n = 1;
|
|
auto liftrequirecompletion = [&](rx::subscriber<int> dest){
|
|
auto completionstate = std::make_shared<std::tuple<bool, long, rx::subscriber<int>>>(false, 0, std::move(dest));
|
|
std::get<2>(*completionstate).add([=](){
|
|
if (std::get<1>(*completionstate) != values || !std::get<0>(*completionstate)) {
|
|
abort();
|
|
}
|
|
});
|
|
// VS2013 deduction issue requires dynamic (type-forgetting)
|
|
return rx::make_subscriber<int>(
|
|
std::get<2>(*completionstate),
|
|
[=](int n){
|
|
++std::get<1>(*completionstate);
|
|
std::get<2>(*completionstate).on_next(n);
|
|
},
|
|
[=](rxu::error_ptr){
|
|
abort();
|
|
},
|
|
[=](){
|
|
if (std::get<1>(*completionstate) != values) {
|
|
abort();
|
|
}
|
|
std::get<0>(*completionstate) = true;
|
|
std::get<2>(*completionstate).on_completed();
|
|
}).as_dynamic();
|
|
};
|
|
auto start = clock::now();
|
|
auto ew = es.create_coordinator().get_worker();
|
|
std::atomic<int> v(0);
|
|
auto s0 = rxs::range(1, es)
|
|
.take(values)
|
|
.lift<int>(liftrequirecompletion)
|
|
.as_dynamic()
|
|
.publish_synchronized(es)
|
|
.ref_count()
|
|
.lift<int>(liftrequirecompletion)
|
|
.subscribe(
|
|
rx::make_observer_dynamic<int>(
|
|
[&](int){
|
|
++v;
|
|
},
|
|
[&](){
|
|
++c;
|
|
}));
|
|
auto s1 = rxs::range(values + 1, es)
|
|
.take(values)
|
|
.lift<int>(liftrequirecompletion)
|
|
.as_dynamic()
|
|
.publish_synchronized(es)
|
|
.ref_count()
|
|
.lift<int>(liftrequirecompletion)
|
|
.subscribe(
|
|
rx::make_observer_dynamic<int>(
|
|
[&](int){
|
|
++v;
|
|
},
|
|
[&](){
|
|
++c;
|
|
}));
|
|
auto s2 = rxs::range((values * 2) + 1, es)
|
|
.take(values)
|
|
.lift<int>(liftrequirecompletion)
|
|
.as_dynamic()
|
|
.publish_synchronized(es)
|
|
.ref_count()
|
|
.lift<int>(liftrequirecompletion)
|
|
.subscribe(
|
|
rx::make_observer_dynamic<int>(
|
|
[&](int){
|
|
++v;
|
|
},
|
|
[&](){
|
|
++c;
|
|
}));
|
|
while(v != values * 3 || c != 3);
|
|
s0.unsubscribe();
|
|
s1.unsubscribe();
|
|
s2.unsubscribe();
|
|
auto finish = clock::now();
|
|
auto msElapsed = duration_cast<milliseconds>(finish-start);
|
|
std::cout << "range synchronized : " << n << " subscribed, " << v << " on_next calls, " << msElapsed.count() << "ms elapsed, " << v / (msElapsed.count() / 1000.0) << " ops/sec" << std::endl;
|
|
|
|
if (--runs > 0) {
|
|
self();
|
|
}
|
|
};
|
|
|
|
w.schedule(loop);
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("observe_on range debug", "[!hide][subscribe][range][observe_on_debug][observe_on][long][perf]"){
|
|
GIVEN("range"){
|
|
WHEN("observed on"){
|
|
using namespace std::chrono;
|
|
typedef steady_clock clock;
|
|
|
|
auto sc = rxsc::make_current_thread();
|
|
auto w = sc.create_worker();
|
|
|
|
auto es = rx::observe_on_event_loop();
|
|
|
|
const int values = 10000;
|
|
|
|
int runs = 10;
|
|
|
|
auto loop = [&](const rxsc::schedulable& self) {
|
|
std::atomic<int> c(0);
|
|
int n = 1;
|
|
auto liftrequirecompletion = [&](rx::subscriber<int> dest){
|
|
auto completionstate = std::make_shared<std::tuple<bool, long, rx::subscriber<int>>>(false, 0, std::move(dest));
|
|
std::get<2>(*completionstate).add([=](){
|
|
if (std::get<1>(*completionstate) != values || !std::get<0>(*completionstate)) {
|
|
abort();
|
|
}
|
|
});
|
|
// VS2013 deduction issue requires dynamic (type-forgetting)
|
|
return rx::make_subscriber<int>(
|
|
std::get<2>(*completionstate),
|
|
[=](int n){
|
|
++std::get<1>(*completionstate);
|
|
std::get<2>(*completionstate).on_next(n);
|
|
},
|
|
[=](rxu::error_ptr){
|
|
abort();
|
|
},
|
|
[=](){
|
|
if (std::get<1>(*completionstate) != values) {
|
|
abort();
|
|
}
|
|
std::get<0>(*completionstate) = true;
|
|
std::get<2>(*completionstate).on_completed();
|
|
}).as_dynamic();
|
|
};
|
|
auto start = clock::now();
|
|
auto ew = es.create_coordinator().get_worker();
|
|
std::atomic<int> v(0);
|
|
auto s0 = rxs::range(1, es)
|
|
.take(values)
|
|
.lift<int>(liftrequirecompletion)
|
|
.as_dynamic()
|
|
.observe_on(es)
|
|
.lift<int>(liftrequirecompletion)
|
|
.subscribe(
|
|
rx::make_observer_dynamic<int>(
|
|
[&](int){
|
|
++v;
|
|
},
|
|
[&](){
|
|
++c;
|
|
}));
|
|
auto s1 = rxs::range(values + 1, es)
|
|
.take(values)
|
|
.lift<int>(liftrequirecompletion)
|
|
.as_dynamic()
|
|
.observe_on(es)
|
|
.lift<int>(liftrequirecompletion)
|
|
.subscribe(
|
|
rx::make_observer_dynamic<int>(
|
|
[&](int){
|
|
++v;
|
|
},
|
|
[&](){
|
|
++c;
|
|
}));
|
|
auto s2 = rxs::range((values * 2) + 1, es)
|
|
.take(values)
|
|
.lift<int>(liftrequirecompletion)
|
|
.as_dynamic()
|
|
.observe_on(es)
|
|
.lift<int>(liftrequirecompletion)
|
|
.subscribe(
|
|
rx::make_observer_dynamic<int>(
|
|
[&](int){
|
|
++v;
|
|
},
|
|
[&](){
|
|
++c;
|
|
}));
|
|
while(v != values * 3 || c != 3);
|
|
s0.unsubscribe();
|
|
s1.unsubscribe();
|
|
s2.unsubscribe();
|
|
auto finish = clock::now();
|
|
auto msElapsed = duration_cast<milliseconds>(finish-start);
|
|
std::cout << "range observe_on : " << n << " subscribed, " << v << " on_next calls, " << msElapsed.count() << "ms elapsed, " << v / (msElapsed.count() / 1000.0) << " ops/sec" << std::endl;
|
|
|
|
if (--runs > 0) {
|
|
self();
|
|
}
|
|
};
|
|
|
|
w.schedule(loop);
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("subscription traits", "[subscription][traits]"){
|
|
GIVEN("given some subscription types"){
|
|
auto es = rx::make_subscription();
|
|
rx::composite_subscription cs;
|
|
WHEN("tested"){
|
|
THEN("is_subscription value is true for empty subscription"){
|
|
REQUIRE(rx::is_subscription<decltype(es)>::value);
|
|
}
|
|
THEN("is_subscription value is true for composite_subscription"){
|
|
REQUIRE(rx::is_subscription<decltype(cs)>::value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("non-subscription traits", "[subscription][traits]"){
|
|
GIVEN("given some non-subscription types"){
|
|
auto l = [](){};
|
|
int i = 0;
|
|
void* v = nullptr;
|
|
WHEN("tested"){
|
|
THEN("is_subscription value is false for lambda"){
|
|
l();
|
|
REQUIRE(!rx::is_subscription<decltype(l)>::value);
|
|
}
|
|
THEN("is_subscription value is false for int"){
|
|
i = 0;
|
|
REQUIRE(!rx::is_subscription<decltype(i)>::value);
|
|
}
|
|
THEN("is_subscription value is false for void*"){
|
|
v = nullptr;
|
|
REQUIRE(!rx::is_subscription<decltype(v)>::value);
|
|
}
|
|
THEN("is_subscription value is false for void"){
|
|
REQUIRE(!rx::is_subscription<void>::value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("subscription static", "[subscription]"){
|
|
GIVEN("given a subscription"){
|
|
int i=0;
|
|
auto s = rx::make_subscription([&i](){++i;});
|
|
WHEN("not used"){
|
|
THEN("is subscribed"){
|
|
REQUIRE(s.is_subscribed());
|
|
}
|
|
THEN("i is 0"){
|
|
REQUIRE(i == 0);
|
|
}
|
|
}
|
|
WHEN("used"){
|
|
THEN("is not subscribed when unsubscribed once"){
|
|
s.unsubscribe();
|
|
REQUIRE(!s.is_subscribed());
|
|
}
|
|
THEN("is not subscribed when unsubscribed twice"){
|
|
s.unsubscribe();
|
|
s.unsubscribe();
|
|
REQUIRE(!s.is_subscribed());
|
|
}
|
|
THEN("i is 1 when unsubscribed once"){
|
|
s.unsubscribe();
|
|
REQUIRE(i == 1);
|
|
}
|
|
THEN("i is 1 when unsubscribed twice"){
|
|
s.unsubscribe();
|
|
s.unsubscribe();
|
|
REQUIRE(i == 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("subscription empty", "[subscription]"){
|
|
GIVEN("given an empty subscription"){
|
|
auto s = rx::make_subscription();
|
|
WHEN("not used"){
|
|
THEN("is not subscribed"){
|
|
REQUIRE(!s.is_subscribed());
|
|
}
|
|
}
|
|
WHEN("used"){
|
|
THEN("is not subscribed when unsubscribed once"){
|
|
s.unsubscribe();
|
|
REQUIRE(!s.is_subscribed());
|
|
}
|
|
THEN("is not subscribed when unsubscribed twice"){
|
|
s.unsubscribe();
|
|
s.unsubscribe();
|
|
REQUIRE(!s.is_subscribed());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SCENARIO("subscription composite", "[subscription]"){
|
|
GIVEN("given a subscription"){
|
|
int i=0;
|
|
rx::composite_subscription s;
|
|
s.add(rx::make_subscription());
|
|
s.add(rx::make_subscription([&i](){++i;}));
|
|
s.add([&i](){++i;});
|
|
WHEN("not used"){
|
|
THEN("is subscribed"){
|
|
REQUIRE(s.is_subscribed());
|
|
}
|
|
THEN("i is 0"){
|
|
REQUIRE(i == 0);
|
|
}
|
|
}
|
|
WHEN("used"){
|
|
THEN("is not subscribed when unsubscribed once"){
|
|
s.unsubscribe();
|
|
REQUIRE(!s.is_subscribed());
|
|
}
|
|
THEN("is not subscribed when unsubscribed twice"){
|
|
s.unsubscribe();
|
|
s.unsubscribe();
|
|
REQUIRE(!s.is_subscribed());
|
|
}
|
|
THEN("i is 2 when unsubscribed once"){
|
|
s.unsubscribe();
|
|
REQUIRE(i == 2);
|
|
}
|
|
THEN("i is 2 when unsubscribed twice"){
|
|
s.unsubscribe();
|
|
s.unsubscribe();
|
|
REQUIRE(i == 2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|