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