
reactor.core.publisher.SerializedSubscriber Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package reactor.core.publisher;
import org.reactivestreams.Subscription;
import reactor.core.CoreSubscriber;
import reactor.util.annotation.Nullable;
/**
* Subscriber that makes sure signals are delivered sequentially in case the onNext, onError or onComplete methods are
* called concurrently.
*
*
* The implementation uses {@code synchronized (this)} to ensure mutual exclusion.
*
*
* Note that the class implements Subscription to save on allocation.
*
* @param the value type
*/
final class SerializedSubscriber implements InnerOperator {
final CoreSubscriber super T> actual;
boolean emitting;
boolean missed;
volatile boolean done;
volatile boolean cancelled;
LinkedArrayNode head;
LinkedArrayNode tail;
Throwable error;
Subscription s;
SerializedSubscriber(CoreSubscriber super T> actual) {
this.actual = actual;
}
@Override
public void onSubscribe(Subscription s) {
if (Operators.validate(this.s, s)) {
this.s = s;
actual.onSubscribe(this);
}
}
@Override
public void onNext(T t) {
if (cancelled || done) {
return;
}
synchronized (this) {
if (cancelled || done) {
return;
}
if (emitting) {
serAdd(t);
missed = true;
return;
}
emitting = true;
}
actual.onNext(t);
serDrainLoop(actual);
}
@Override
public void onError(Throwable t) {
if (cancelled || done) {
return;
}
synchronized (this) {
if (cancelled || done) {
return;
}
done = true;
error = t;
if (emitting) {
missed = true;
return;
}
}
actual.onError(t);
}
@Override
public void onComplete() {
if (cancelled || done) {
return;
}
synchronized (this) {
if (cancelled || done) {
return;
}
done = true;
if (emitting) {
missed = true;
return;
}
}
actual.onComplete();
}
@Override
public void request(long n) {
s.request(n);
}
@Override
public void cancel() {
cancelled = true;
s.cancel();
}
void serAdd(T value) {
LinkedArrayNode t = tail;
if (t == null) {
t = new LinkedArrayNode<>(value);
head = t;
tail = t;
}
else {
if (t.count == LinkedArrayNode.DEFAULT_CAPACITY) {
LinkedArrayNode n = new LinkedArrayNode<>(value);
t.next = n;
tail = n ;
}
else {
t.array[t.count++] = value;
}
}
}
void serDrainLoop(CoreSubscriber super T> actual) {
for (; ; ) {
if (cancelled) {
return;
}
boolean d;
Throwable e;
LinkedArrayNode n;
synchronized (this) {
if (cancelled) {
return;
}
if (!missed) {
emitting = false;
return;
}
missed = false;
d = done;
e = error;
n = head;
head = null;
tail = null;
}
while (n != null) {
T[] arr = n.array;
int c = n.count;
for (int i = 0; i < c; i++) {
if (cancelled) {
return;
}
actual.onNext(arr[i]);
}
n = n.next;
}
if (cancelled) {
return;
}
if (e != null) {
actual.onError(e);
return;
}
else if (d) {
actual.onComplete();
return;
}
}
}
@Override
public CoreSubscriber super T> actual() {
return actual;
}
@Override
@Nullable
public Object scanUnsafe(Attr key) {
if (key == Attr.PARENT) return s;
if (key == Attr.ERROR) return error;
if (key == Attr.BUFFERED) return producerCapacity();
if (key == Attr.CAPACITY) return LinkedArrayNode.DEFAULT_CAPACITY;
if (key == Attr.CANCELLED) return cancelled;
if (key == Attr.TERMINATED) return done;
return InnerOperator.super.scanUnsafe(key);
}
int producerCapacity() {
LinkedArrayNode node = tail;
if(node != null){
return node.count;
}
return 0;
}
/**
* Node in a linked array list that is only appended.
*
* @param the value type
*/
static final class LinkedArrayNode {
static final int DEFAULT_CAPACITY = 16;
final T[] array;
int count;
LinkedArrayNode next;
@SuppressWarnings("unchecked")
LinkedArrayNode(T value) {
array = (T[]) new Object[DEFAULT_CAPACITY];
array[0] = value;
count = 1;
}
}
}