123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- package gnu.mapping;
- import gnu.kawa.format.Printable;
- import gnu.lists.Consumer;
- import java.io.IOException;
- /** Implement Scheme "promises".
- *
- * This is a final class, because in some cases (as required by SRFI-45)
- * we copy the fields of one Promise from other Promise, and such
- * copying would be dubious if either operand is a sub-class.
- *
- * @author Per Bothner
- */
- public class Promise<T> implements Printable, Lazy<T> {
- Procedure thunk;
- /** If getValue yields another Lazy value, call getValue on that too. */
- boolean forceValueIfPromise;
- /** Set whether to recursively call getValue if getValue yields a Lazy value. */
- public void setForceValueIfPromise(boolean value) {
- forceValueIfPromise = value;
- }
- /** The result - or UNBOUND if it is not ready. */
- private volatile Object result = Location.UNBOUND;
- /** If an exception was thrown when getting value, store it here. */
- private Throwable throwable;
- /** Create new "blank" Promise.
- * Calling getValue just waits until one of setValue, setAlias,
- * setException or setThunk is invoked.
- * One of these is typically done by some other "producer" thread.
- */
- public Promise() {
- }
- /** Create a new Promise that will evaluate thunk when forced. */
- public Promise(Procedure thunk) {
- this.thunk = thunk;
- }
- public static <T> Promise<T> makeBlank() { return new Promise<T>(); }
- /** Wrap value as a forced Promise. */
- public static <T> Lazy<T> makeBoundPromise (T value) {
- Promise<T> p = new Promise<T>(null);
- p.result = value;
- return p;
- }
- public static Lazy<Object> coerceToLazy (Object value) {
- if (value instanceof Lazy<?>)
- return (Lazy<Object>) value;
- Promise<Object> p = new Promise<Object>(null);
- p.result = value;
- return p;
- }
- public synchronized T getValue () {
- Object r;
- for (;;) {
- synchronized (this) {
- while (isBlank()) {
- try {
- wait();
- } catch (java.lang.InterruptedException ex) {
- }
- }
-
- r = result;
- if (r == Location.UNBOUND && throwable == null) {
- // thunk is non-null, because !isBlank().
- try {
- CallContext ctx = CallContext.getInstance();
- ctx.pushArgState();
- try {
- r = thunk.apply0();
- } finally {
- ctx.popArgState();
- }
- if (result == Location.UNBOUND)
- result = r;
- else
- r = result;
- if (forceValueIfPromise && r instanceof Promise) {
- Promise pr = (Promise) r;
- synchronized(r) {
- if (! pr.isBlank()) {
- moveFrom(pr);
- continue;
- }
- }
- }
- } catch (Throwable ex) {
- throwable = ex;
- }
- thunk = null;
- }
- if (throwable != null)
- WrappedException.rethrow(throwable);
- }
- if (forceValueIfPromise && r instanceof Lazy)
- return ((Lazy<T>) r).getValue();
- return (T) r;
- }
- }
- /** Copy fields of other into this, and set other to indirect to this.
- * This is used to implement the SRFI-45 requirements.
- */
- private void moveFrom(Promise other) {
- this.thunk = other.thunk;
- this.forceValueIfPromise = other.forceValueIfPromise;
- this.throwable = other.throwable;
- this.result = other.result;
- other.result = this;
- other.forceValueIfPromise = true;
- other.thunk = null;
- other.throwable = null;
- }
- public synchronized final boolean isBlank() {
- return thunk == null && result == Location.UNBOUND && throwable == null;
- }
- public void checkBlank() {
- if (! isBlank())
- throw new IllegalStateException();
- }
- /** Bind this promise to a given (eager) value. */
- public synchronized void setValue(Object value) {
- checkBlank();
- result = value;
- notifyAll();
- }
- /** Bind promise to be an alias of another Lazy value. */
- public synchronized void setAlias(Lazy promise) {
- checkBlank();
- result = promise;
- setForceValueIfPromise(true);
- notifyAll();
- }
- /** Bind this promise so forcing it throws the given exception. */
- public synchronized void setException(Throwable exception) {
- checkBlank();
- throwable = exception;
- notifyAll();
- }
- /** Bind this promise so forcing it evaluates the given procedure. */
- public synchronized void setThunk(Procedure thunk) {
- checkBlank();
- this.thunk = thunk;
- notifyAll();
- }
- public Lazy checkAlias() {
- return result instanceof Lazy ? (Lazy) result : null;
- }
- /** Forces the argument, if needed.
- * I.e. calls {@link Lazy#getValue} once if the argument is Lazy.
- */
- public static Object force1 (Object arg) {
- if (arg instanceof Lazy)
- arg = ((Lazy) arg).getValue();
- return arg;
- }
- /** Forces the argument, if needed, to a non-Lazy value.
- * I.e. calls {@link Lazy#getValue} as many times as needed.
- */
- public static Object force (Object arg) {
- while (arg instanceof Lazy) {
- Object val = ((Lazy) arg).getValue();
- if (arg == val)
- break;
- arg = val;
- }
- return arg;
- }
- /** If argument is Lazy, force it, unless already an instance of target.
- */
- public static Object force (Object arg, Class target) {
- while (arg instanceof Lazy && ! target.isInstance(arg)) {
- Object val = ((Lazy) arg).getValue();
- if (arg == val)
- break;
- arg = val;
- }
- return arg;
- }
- public String toString() {
- StringBuilder sb = new StringBuilder();
- try {
- print(sb);
- }
- catch (IOException ex) {
- return "caught "+ex;
- }
- return sb.toString();
- }
- public void print (Consumer out) {
- try {
- print((Appendable) out);
- }
- catch (IOException ex) {
- out.write("caught "+ex);
- }
- }
- public void print (Appendable out) throws IOException {
- Object r = result;
- if (r == Location.UNBOUND) {
- synchronized (this) {
- if (throwable != null) {
- out.append("#<promise - force threw a ");
- out.append(throwable.getClass().getName());
- out.append('>');
- }
- else
- out.append("#<promise - not forced yet>");
- }
- }
- else if (r == null)
- out.append("#<promise - forced to null>");
- else {
- out.append("#<promise"+" - forced to a ");
- out.append(r.getClass().getName());
- out.append('>');
- }
- }
- }
|