org.jetbrains.concurrency.AsyncPromise Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core-api Show documentation
Show all versions of core-api Show documentation
A packaging of the IntelliJ Community Edition core-api library.
This is release number 1 of trunk branch 142.
The newest version!
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.concurrency;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Getter;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class AsyncPromise extends Promise implements Getter {
private static final Logger LOG = Logger.getInstance(AsyncPromise.class);
public static final RuntimeException OBSOLETE_ERROR = Promise.createError("Obsolete");
private volatile Consumer done;
private volatile Consumer rejected;
protected volatile State state = State.PENDING;
// result object or error message
private volatile Object result;
@NotNull
@Override
public State getState() {
return state;
}
@NotNull
@Override
public Promise done(@NotNull Consumer done) {
if (isObsolete(done)) {
return this;
}
switch (state) {
case PENDING:
break;
case FULFILLED:
//noinspection unchecked
done.consume((T)result);
return this;
case REJECTED:
return this;
}
this.done = setHandler(this.done, done);
return this;
}
@NotNull
@Override
public Promise rejected(@NotNull Consumer rejected) {
if (isObsolete(rejected)) {
return this;
}
switch (state) {
case PENDING:
break;
case FULFILLED:
return this;
case REJECTED:
rejected.consume((Throwable)result);
return this;
}
this.rejected = setHandler(this.rejected, rejected);
return this;
}
@Override
public T get() {
//noinspection unchecked
return state == State.FULFILLED ? (T)result : null;
}
@SuppressWarnings("SynchronizeOnThis")
private static final class CompoundConsumer implements Consumer {
private List> consumers = new ArrayList>();
public CompoundConsumer(@NotNull Consumer c1, @NotNull Consumer c2) {
synchronized (this) {
consumers.add(c1);
consumers.add(c2);
}
}
@Override
public void consume(T t) {
List> list;
synchronized (this) {
list = consumers;
consumers = null;
}
if (list != null) {
for (Consumer consumer : list) {
if (!isObsolete(consumer)) {
consumer.consume(t);
}
}
}
}
public void add(@NotNull Consumer consumer) {
synchronized (this) {
if (consumers != null) {
consumers.add(consumer);
}
}
}
}
@Override
@NotNull
public Promise then(@NotNull final Function fulfilled) {
switch (state) {
case PENDING:
break;
case FULFILLED:
//noinspection unchecked
return new DonePromise(fulfilled.fun((T)result));
case REJECTED:
return new RejectedPromise((Throwable)result);
}
final AsyncPromise promise = new AsyncPromise();
addHandlers(new Consumer() {
@Override
public void consume(T result) {
try {
if (fulfilled instanceof Obsolescent && ((Obsolescent)fulfilled).isObsolete()) {
promise.setError(OBSOLETE_ERROR);
}
else {
promise.setResult(fulfilled.fun(result));
}
}
catch (Throwable e) {
promise.setError(e);
}
}
}, new Consumer() {
@Override
public void consume(Throwable error) {
promise.setError(error);
}
});
return promise;
}
@Override
void notify(@NotNull final AsyncPromise child) {
LOG.assertTrue(child != this);
switch (state) {
case PENDING:
break;
case FULFILLED:
//noinspection unchecked
child.setResult((T)result);
return;
case REJECTED:
child.setError((Throwable)result);
return;
}
addHandlers(new Consumer() {
@Override
public void consume(T result) {
try {
child.setResult(result);
}
catch (Throwable e) {
child.setError(e);
}
}
}, new Consumer() {
@Override
public void consume(Throwable error) {
child.setError(error);
}
});
}
@Override
@NotNull
public Promise then(@NotNull final AsyncFunction fulfilled) {
switch (state) {
case PENDING:
break;
case FULFILLED:
//noinspection unchecked
return fulfilled.fun((T)result);
case REJECTED:
return Promise.reject((Throwable)result);
}
final AsyncPromise promise = new AsyncPromise();
final Consumer rejectedHandler = new Consumer() {
@Override
public void consume(Throwable error) {
promise.setError(error);
}
};
addHandlers(new Consumer() {
@Override
public void consume(T result) {
try {
fulfilled.fun(result)
.done(new Consumer() {
@Override
public void consume(SUB_RESULT result) {
try {
promise.setResult(result);
}
catch (Throwable e) {
promise.setError(e);
}
}
})
.rejected(rejectedHandler);
}
catch (Throwable e) {
promise.setError(e);
}
}
}, rejectedHandler);
return promise;
}
@Override
@NotNull
public Promise processed(@NotNull final AsyncPromise fulfilled) {
switch (state) {
case PENDING:
break;
case FULFILLED:
//noinspection unchecked
fulfilled.setResult((T)result);
return this;
case REJECTED:
fulfilled.setError((Throwable)result);
return this;
}
addHandlers(new Consumer() {
@Override
public void consume(T result) {
try {
fulfilled.setResult(result);
}
catch (Throwable e) {
fulfilled.setError(e);
}
}
}, new Consumer() {
@Override
public void consume(Throwable error) {
fulfilled.setError(error);
}
});
return this;
}
private void addHandlers(@NotNull Consumer done, @NotNull Consumer rejected) {
this.done = setHandler(this.done, done);
this.rejected = setHandler(this.rejected, rejected);
}
@NotNull
private static Consumer setHandler(@Nullable Consumer oldConsumer, @NotNull Consumer newConsumer) {
if (oldConsumer == null) {
return newConsumer;
}
else if (oldConsumer instanceof CompoundConsumer) {
((CompoundConsumer)oldConsumer).add(newConsumer);
return oldConsumer;
}
else {
return new CompoundConsumer(oldConsumer, newConsumer);
}
}
public void setResult(T result) {
if (state != State.PENDING) {
return;
}
this.result = result;
state = State.FULFILLED;
Consumer done = this.done;
clearHandlers();
if (done != null && !isObsolete(done)) {
done.consume(result);
}
}
static boolean isObsolete(@Nullable Consumer> consumer) {
return consumer instanceof Obsolescent && ((Obsolescent)consumer).isObsolete();
}
public boolean setError(@NotNull Throwable error) {
if (state != State.PENDING) {
return false;
}
result = error;
state = State.REJECTED;
Consumer rejected = this.rejected;
clearHandlers();
if (rejected != null) {
if (!isObsolete(rejected)) {
rejected.consume(error);
}
}
else if (!(error instanceof MessageError)) {
LOG.error(error);
}
return true;
}
private void clearHandlers() {
done = null;
rejected = null;
}
@Override
public Promise processed(@NotNull final Consumer processed) {
done(processed);
rejected(new Consumer() {
@Override
public void consume(Throwable error) {
processed.consume(null);
}
});
return this;
}
}