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

com.github.tonivade.purefun.concurrent.Promise Maven / Gradle / Ivy

/*
 * Copyright (c) 2018-2019, Antonio Gabriel Muñoz Conejo 
 * Distributed under the terms of the MIT License
 */
package com.github.tonivade.purefun.concurrent;

import static java.util.Objects.isNull;
import static java.util.Objects.requireNonNull;

import java.time.Duration;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;

import com.github.tonivade.purefun.Consumer1;
import com.github.tonivade.purefun.type.Option;
import com.github.tonivade.purefun.type.Try;

public interface Promise {

  boolean tryComplete(Try value);

  default Promise complete(Try value) {
    if (tryComplete(value)) {
      return this;
    } else throw new IllegalStateException("promise already completed");
  }

  default Promise succeeded(T value) {
    return complete(Try.success(value));
  }

  default Promise failed(Throwable error) {
    return complete(Try.failure(error));
  }

  default Future toFuture() {
    return toFuture(Future.DEFAULT_EXECUTOR);
  }

  default Future toFuture(Executor executor) {
    return FutureImpl.from(executor, this);
  }

  Promise onComplete(Consumer1> consumer);

  default Promise onSuccess(Consumer1 consumer) {
    return onComplete(value -> value.onSuccess(consumer));
  }

  default Promise onFailure(Consumer1 consumer) {
    return onComplete(value -> value.onFailure(consumer));
  }

  Try get();
  Try get(Duration timeout);

  boolean isCompleted();

  static  Promise make() {
    return new PromiseImpl<>();
  }
}

final class PromiseImpl implements Promise {

  private final State state = new State();
  private final Queue>> consumers = new LinkedList<>();
  private final AtomicReference> reference = new AtomicReference<>();

  @Override
  public boolean tryComplete(Try value) {
    if (isEmpty()) {
      synchronized (state) {
        if (isEmpty()) {
          state.completed = true;
          setValue(value);
          state.notifyAll();
          return true;
        }
      }
    }
    return false;
  }

  @Override
  public Try get() {
    if (isEmpty()) {
      synchronized (state) {
        if (isEmpty()) {
          try {
            state.wait();
          } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return Try.failure(e);
          }
        }
      }
    }
    return requireNonNull(reference.get());
  }

  @Override
  public Try get(Duration timeout) {
    if (isEmpty()) {
      synchronized (state) {
        if (isEmpty()) {
          try {
            state.wait(timeout.toMillis());
          } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return Try.failure(e);
          }
        }
      }
    }
    return Option.of(reference::get).getOrElse(Try.failure(new TimeoutException()));
  }

  @Override
  public boolean isCompleted() {
    synchronized (state) {
      return state.completed;
    }
  }

  @Override
  public Promise onComplete(Consumer1> consumer) {
    current(consumer).ifPresent(consumer);
    return this;
  }

  private Option> current(Consumer1> consumer) {
    Try current = reference.get();
    if (isNull(current)) {
      synchronized (state) {
        current = reference.get();
        if (isNull(current)) {
          consumers.add(consumer);
        }
      }
    }
    return Option.of(current);
  }

  private void setValue(Try value) {
    reference.set(value);
    consumers.forEach(consumer -> consumer.accept(value));
  }

  private boolean isEmpty() {
    return isNull(reference.get());
  }

  private static final class State {
    private boolean completed = false;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy