All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.concurrency.AsyncPromise Maven / Gradle / Ivy

Go to download

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;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy