001/* 002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package gololang.concurrent.async; 012 013import java.util.HashSet; 014 015/** 016 * A promise object is used to abstract over possibly asynchronous computations. 017 * 018 * You should consult the "golodoc" of the {@code gololang.Async} module. 019 * 020 * @see gololang.concurrent.async.Future 021 * @see gololang.concurrent.async.AssignedFuture 022 */ 023public final class Promise { 024 025 private volatile boolean resolved = false; 026 private volatile Object value; 027 028 private final Object lock = new Object(); 029 private final HashSet<Future.Observer> setObservers = new HashSet<>(); 030 private final HashSet<Future.Observer> failObservers = new HashSet<>(); 031 032 /** 033 * Checks whether the promise has been resolved. 034 * 035 * @return {@code true} if it has been resolved, {@code false} otherwise. 036 */ 037 public boolean isResolved() { 038 return resolved; 039 } 040 041 /** 042 * Checks whether the promise has failed. 043 * 044 * @return {@code true} if it has been resolved and failed, {@code false} otherwise. 045 */ 046 public boolean isFailed() { 047 return value instanceof Throwable; 048 } 049 050 /** 051 * Non-blocking get. 052 * 053 * @return the promise value, which may be {@code null} if it has not been resolved yet. 054 */ 055 public Object get() { 056 return value; 057 } 058 059 /** 060 * Blocking get, waiting until the promise is resolved. 061 * 062 * @return the promise value. 063 * @throws InterruptedException if the current thread gets interrupted. 064 */ 065 public Object blockingGet() throws InterruptedException { 066 synchronized (lock) { 067 while (!resolved) { 068 lock.wait(); 069 } 070 return value; 071 } 072 } 073 074 /** 075 * Sets the promise value. This has no effect if the promise has already been resolved. 076 * 077 * @param value the value. 078 * @return this promise. 079 */ 080 public Promise set(Object value) { 081 if (resolved) { 082 return this; 083 } 084 synchronized (lock) { 085 if (!resolved) { 086 this.value = value; 087 this.resolved = true; 088 lock.notifyAll(); 089 } 090 } 091 HashSet<Future.Observer> observers = isFailed() ? failObservers : setObservers; 092 for (Future.Observer observer : observers) { 093 observer.apply(value); 094 } 095 return this; 096 } 097 098 /** 099 * Fails the promise. This has no effect if the promise has already been resolved. 100 * 101 * @param throwable the failure. 102 * @return this promise. 103 */ 104 public Promise fail(Throwable throwable) { 105 return set(throwable); 106 } 107 108 /** 109 * Creates a new future to observe the eventual resolution of this promise. 110 * 111 * @return a new future object. 112 */ 113 public Future future() { 114 return new Future() { 115 @Override 116 public Object get() { 117 return Promise.this.get(); 118 } 119 120 @Override 121 public Object blockingGet() throws InterruptedException { 122 return Promise.this.blockingGet(); 123 } 124 125 @Override 126 public boolean isResolved() { 127 return Promise.this.isResolved(); 128 } 129 130 @Override 131 public boolean isFailed() { 132 return Promise.this.isFailed(); 133 } 134 135 @Override 136 public Future onSet(Observer observer) { 137 synchronized (lock) { 138 if (resolved && !Promise.this.isFailed()) { 139 observer.apply(value); 140 } else { 141 setObservers.add(observer); 142 } 143 } 144 return this; 145 } 146 147 @Override 148 public Future onFail(Observer observer) { 149 synchronized (lock) { 150 if (resolved && Promise.this.isFailed()) { 151 observer.apply(value); 152 } else { 153 failObservers.add(observer); 154 } 155 } 156 return this; 157 } 158 }; 159 } 160 161 @Override 162 public String toString() { 163 return String.format("Promise{resolved=%s, value=%s}", resolved, value); 164 } 165}