forked from rsocket/rsocket-cpp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathConsumerBase.cpp
More file actions
124 lines (106 loc) · 3.51 KB
/
ConsumerBase.cpp
File metadata and controls
124 lines (106 loc) · 3.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright 2004-present Facebook. All Rights Reserved.
#include "rsocket/statemachine/ConsumerBase.h"
#include <algorithm>
#include <glog/logging.h>
#include "rsocket/Payload.h"
#include "yarpl/flowable/Subscription.h"
namespace rsocket {
using namespace yarpl;
using namespace yarpl::flowable;
void ConsumerBase::subscribe(
Reference<yarpl::flowable::Subscriber<Payload>> subscriber) {
if (isTerminated()) {
subscriber->onSubscribe(yarpl::flowable::Subscription::empty());
subscriber->onComplete();
return;
}
DCHECK(!consumingSubscriber_);
consumingSubscriber_ = std::move(subscriber);
consumingSubscriber_->onSubscribe(this->ref_from_this(this));
}
// TODO: this is probably buggy and misused and not needed (when
// completeConsumer exists)
void ConsumerBase::cancelConsumer() {
state_ = State::CLOSED;
VLOG(5) << "ConsumerBase::cancelConsumer()";
consumingSubscriber_ = nullptr;
}
void ConsumerBase::addImplicitAllowance(size_t n) {
allowance_.add(n);
activeRequests_.add(n);
}
void ConsumerBase::generateRequest(size_t n) {
allowance_.add(n);
pendingAllowance_.add(n);
sendRequests();
}
void ConsumerBase::endStream(StreamCompletionSignal signal) {
VLOG(5) << "ConsumerBase::endStream(" << signal << ")";
if (auto subscriber = std::move(consumingSubscriber_)) {
if (signal == StreamCompletionSignal::COMPLETE ||
signal == StreamCompletionSignal::CANCEL) { // TODO: remove CANCEL
VLOG(5) << "Closing ConsumerBase subscriber with calling onComplete";
subscriber->onComplete();
} else {
VLOG(5) << "Closing ConsumerBase subscriber with calling onError";
subscriber->onError(StreamInterruptedException(static_cast<int>(signal)));
}
}
StreamStateMachineBase::endStream(signal);
}
size_t ConsumerBase::getConsumerAllowance() const {
return allowance_.get();
}
void ConsumerBase::processPayload(Payload&& payload, bool onNext) {
if (payload || onNext) {
// Frames carry application-level payloads are taken into account when
// figuring out flow control allowance.
if (allowance_.tryConsume(1) && activeRequests_.tryConsume(1)) {
sendRequests();
if (consumingSubscriber_) {
consumingSubscriber_->onNext(std::move(payload));
} else {
LOG(ERROR)
<< "consuming subscriber is missing, might be a race condition on "
" cancel/onNext.";
}
} else {
handleFlowControlError();
return;
}
}
}
void ConsumerBase::completeConsumer() {
state_ = State::CLOSED;
VLOG(5) << "ConsumerBase::completeConsumer()";
if (auto subscriber = std::move(consumingSubscriber_)) {
subscriber->onComplete();
}
}
void ConsumerBase::errorConsumer(folly::exception_wrapper ex) {
state_ = State::CLOSED;
VLOG(5) << "ConsumerBase::errorConsumer()";
if (auto subscriber = std::move(consumingSubscriber_)) {
subscriber->onError(std::move(ex));
}
}
void ConsumerBase::sendRequests() {
auto toSync =
std::min<size_t>(pendingAllowance_.get(), Frame_REQUEST_N::kMaxRequestN);
auto actives = activeRequests_.get();
if (actives < (toSync + 1) / 2) {
toSync = toSync - actives;
toSync = pendingAllowance_.consumeUpTo(toSync);
if (toSync > 0) {
writeRequestN(static_cast<uint32_t>(toSync));
activeRequests_.add(toSync);
}
}
}
void ConsumerBase::handleFlowControlError() {
if (auto subscriber = std::move(consumingSubscriber_)) {
subscriber->onError(std::runtime_error("surplus response"));
}
errorStream("flow control error");
}
} // namespace rsocket