Promise.java 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. package gnu.mapping;
  2. import gnu.kawa.format.Printable;
  3. import gnu.lists.Consumer;
  4. import java.io.IOException;
  5. /** Implement Scheme "promises".
  6. *
  7. * This is a final class, because in some cases (as required by SRFI-45)
  8. * we copy the fields of one Promise from other Promise, and such
  9. * copying would be dubious if either operand is a sub-class.
  10. *
  11. * @author Per Bothner
  12. */
  13. public class Promise<T> implements Printable, Lazy<T> {
  14. Procedure thunk;
  15. /** If getValue yields another Lazy value, call getValue on that too. */
  16. boolean forceValueIfPromise;
  17. /** Set whether to recursively call getValue if getValue yields a Lazy value. */
  18. public void setForceValueIfPromise(boolean value) {
  19. forceValueIfPromise = value;
  20. }
  21. /** The result - or UNBOUND if it is not ready. */
  22. private volatile Object result = Location.UNBOUND;
  23. /** If an exception was thrown when getting value, store it here. */
  24. private Throwable throwable;
  25. /** Create new "blank" Promise.
  26. * Calling getValue just waits until one of setValue, setAlias,
  27. * setException or setThunk is invoked.
  28. * One of these is typically done by some other "producer" thread.
  29. */
  30. public Promise() {
  31. }
  32. /** Create a new Promise that will evaluate thunk when forced. */
  33. public Promise(Procedure thunk) {
  34. this.thunk = thunk;
  35. }
  36. public static <T> Promise<T> makeBlank() { return new Promise<T>(); }
  37. /** Wrap value as a forced Promise. */
  38. public static <T> Lazy<T> makeBoundPromise (T value) {
  39. Promise<T> p = new Promise<T>(null);
  40. p.result = value;
  41. return p;
  42. }
  43. public static Lazy<Object> coerceToLazy (Object value) {
  44. if (value instanceof Lazy<?>)
  45. return (Lazy<Object>) value;
  46. Promise<Object> p = new Promise<Object>(null);
  47. p.result = value;
  48. return p;
  49. }
  50. public synchronized T getValue () {
  51. Object r;
  52. for (;;) {
  53. synchronized (this) {
  54. while (isBlank()) {
  55. try {
  56. wait();
  57. } catch (java.lang.InterruptedException ex) {
  58. }
  59. }
  60. r = result;
  61. if (r == Location.UNBOUND && throwable == null) {
  62. // thunk is non-null, because !isBlank().
  63. try {
  64. CallContext ctx = CallContext.getInstance();
  65. ctx.pushArgState();
  66. try {
  67. r = thunk.apply0();
  68. } finally {
  69. ctx.popArgState();
  70. }
  71. if (result == Location.UNBOUND)
  72. result = r;
  73. else
  74. r = result;
  75. if (forceValueIfPromise && r instanceof Promise) {
  76. Promise pr = (Promise) r;
  77. synchronized(r) {
  78. if (! pr.isBlank()) {
  79. moveFrom(pr);
  80. continue;
  81. }
  82. }
  83. }
  84. } catch (Throwable ex) {
  85. throwable = ex;
  86. }
  87. thunk = null;
  88. }
  89. if (throwable != null)
  90. WrappedException.rethrow(throwable);
  91. }
  92. if (forceValueIfPromise && r instanceof Lazy)
  93. return ((Lazy<T>) r).getValue();
  94. return (T) r;
  95. }
  96. }
  97. /** Copy fields of other into this, and set other to indirect to this.
  98. * This is used to implement the SRFI-45 requirements.
  99. */
  100. private void moveFrom(Promise other) {
  101. this.thunk = other.thunk;
  102. this.forceValueIfPromise = other.forceValueIfPromise;
  103. this.throwable = other.throwable;
  104. this.result = other.result;
  105. other.result = this;
  106. other.forceValueIfPromise = true;
  107. other.thunk = null;
  108. other.throwable = null;
  109. }
  110. public synchronized final boolean isBlank() {
  111. return thunk == null && result == Location.UNBOUND && throwable == null;
  112. }
  113. public void checkBlank() {
  114. if (! isBlank())
  115. throw new IllegalStateException();
  116. }
  117. /** Bind this promise to a given (eager) value. */
  118. public synchronized void setValue(Object value) {
  119. checkBlank();
  120. result = value;
  121. notifyAll();
  122. }
  123. /** Bind promise to be an alias of another Lazy value. */
  124. public synchronized void setAlias(Lazy promise) {
  125. checkBlank();
  126. result = promise;
  127. setForceValueIfPromise(true);
  128. notifyAll();
  129. }
  130. /** Bind this promise so forcing it throws the given exception. */
  131. public synchronized void setException(Throwable exception) {
  132. checkBlank();
  133. throwable = exception;
  134. notifyAll();
  135. }
  136. /** Bind this promise so forcing it evaluates the given procedure. */
  137. public synchronized void setThunk(Procedure thunk) {
  138. checkBlank();
  139. this.thunk = thunk;
  140. notifyAll();
  141. }
  142. public Lazy checkAlias() {
  143. return result instanceof Lazy ? (Lazy) result : null;
  144. }
  145. /** Forces the argument, if needed.
  146. * I.e. calls {@link Lazy#getValue} once if the argument is Lazy.
  147. */
  148. public static Object force1 (Object arg) {
  149. if (arg instanceof Lazy)
  150. arg = ((Lazy) arg).getValue();
  151. return arg;
  152. }
  153. /** Forces the argument, if needed, to a non-Lazy value.
  154. * I.e. calls {@link Lazy#getValue} as many times as needed.
  155. */
  156. public static Object force (Object arg) {
  157. while (arg instanceof Lazy) {
  158. Object val = ((Lazy) arg).getValue();
  159. if (arg == val)
  160. break;
  161. arg = val;
  162. }
  163. return arg;
  164. }
  165. /** If argument is Lazy, force it, unless already an instance of target.
  166. */
  167. public static Object force (Object arg, Class target) {
  168. while (arg instanceof Lazy && ! target.isInstance(arg)) {
  169. Object val = ((Lazy) arg).getValue();
  170. if (arg == val)
  171. break;
  172. arg = val;
  173. }
  174. return arg;
  175. }
  176. public String toString() {
  177. StringBuilder sb = new StringBuilder();
  178. try {
  179. print(sb);
  180. }
  181. catch (IOException ex) {
  182. return "caught "+ex;
  183. }
  184. return sb.toString();
  185. }
  186. public void print (Consumer out) {
  187. try {
  188. print((Appendable) out);
  189. }
  190. catch (IOException ex) {
  191. out.write("caught "+ex);
  192. }
  193. }
  194. public void print (Appendable out) throws IOException {
  195. Object r = result;
  196. if (r == Location.UNBOUND) {
  197. synchronized (this) {
  198. if (throwable != null) {
  199. out.append("#<promise - force threw a ");
  200. out.append(throwable.getClass().getName());
  201. out.append('>');
  202. }
  203. else
  204. out.append("#<promise - not forced yet>");
  205. }
  206. }
  207. else if (r == null)
  208. out.append("#<promise - forced to null>");
  209. else {
  210. out.append("#<promise"+" - forced to a ");
  211. out.append(r.getClass().getName());
  212. out.append('>');
  213. }
  214. }
  215. }