001: /*
002: * LispThread.java
003: *
004: * Copyright (C) 2003 Peter Graves
005: * $Id: LispThread.java,v 1.6 2003/11/15 11:03:34 beedlem Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.lisp;
023:
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.Stack;
027:
028: public final class LispThread extends LispObject {
029: private static HashMap map = new HashMap();
030: private static Object lock = new Object();
031:
032: public static final LispThread currentThread() {
033: Thread t = Thread.currentThread();
034: LispThread thread = get(t);
035: if (thread == null) {
036: thread = new LispThread(t);
037: put(t, thread);
038: }
039: return thread;
040: }
041:
042: private static void put(Thread t, LispThread thread) {
043: synchronized (lock) {
044: HashMap m = (HashMap) map.clone();
045: m.put(t, thread);
046: map = m;
047: }
048: }
049:
050: private static LispThread get(Thread t) {
051: return (LispThread) map.get(t);
052: }
053:
054: private static void remove(Thread t) {
055: synchronized (lock) {
056: HashMap m = (HashMap) map.clone();
057: m.remove(t);
058: map = m;
059: }
060: }
061:
062: private final Thread t;
063:
064: private LispThread(Thread t) {
065: this .t = t;
066: }
067:
068: private LispThread(final Function fun) {
069: Runnable r = new Runnable() {
070: public void run() {
071: try {
072: funcall(fun, new LispObject[0], LispThread.this );
073: } catch (Throwable t) {
074: t.printStackTrace();
075: } finally {
076: remove(t);
077: }
078: }
079: };
080: t = new Thread(r);
081: put(t, this );
082: t.start();
083: }
084:
085: private boolean destroyed;
086: private Environment dynEnv;
087: private LispObject[] _values;
088:
089: public final synchronized boolean isDestroyed() {
090: return destroyed;
091: }
092:
093: private final synchronized void setDestroyed(boolean b) {
094: destroyed = b;
095: }
096:
097: public final LispObject[] getValues() {
098: return _values;
099: }
100:
101: public final void setValues(LispObject[] array) {
102: if (array == null)
103: _values = null;
104: else {
105: _values = new LispObject[array.length];
106: for (int i = array.length; i-- > 0;)
107: _values[i] = array[i];
108: }
109: }
110:
111: public final void clearValues() {
112: _values = null;
113: }
114:
115: public final LispObject nothing() {
116: _values = new LispObject[0];
117: return NIL;
118: }
119:
120: // Forces a single value, for situations where multiple values should be
121: // ignored.
122: public final LispObject value(LispObject obj) {
123: _values = null;
124: return obj;
125: }
126:
127: public final Environment getDynamicEnvironment() {
128: return dynEnv;
129: }
130:
131: public final void setDynamicEnvironment(Environment env) {
132: dynEnv = env;
133: }
134:
135: public final void bindSpecial(Symbol symbol, LispObject value) {
136: if (symbol.isSpecialVariable()) {
137: dynEnv = new Environment(dynEnv);
138: dynEnv.bind(symbol, value);
139: } else
140: Debug.assertTrue(false);
141: }
142:
143: public final LispObject lookupSpecial(LispObject symbol) {
144: return dynEnv != null ? dynEnv.lookup(symbol) : null;
145: }
146:
147: private Stack catchTags = new Stack();
148:
149: public void pushCatchTag(LispObject tag) {
150: catchTags.push(tag);
151: }
152:
153: public LispObject popCatchTag() {
154: if (!catchTags.empty())
155: return (LispObject) catchTags.pop();
156: Debug.assertTrue(false);
157: return null;
158: }
159:
160: public boolean isValidCatchTag(LispObject tag) {
161: for (int i = catchTags.size(); i-- > 0;) {
162: if (catchTags.get(i) == tag)
163: return true;
164: }
165: return false;
166: }
167:
168: private static class StackFrame {
169: private final LispObject functional;
170: private final LispObject[] argv;
171:
172: public StackFrame(LispObject functional, LispObject[] argv) {
173: this .functional = functional;
174: this .argv = argv;
175: }
176:
177: public LispObject getFunctional() {
178: return functional;
179: }
180:
181: public LispObject[] getArgumentVector() {
182: return argv;
183: }
184: }
185:
186: private final Stack stack = new Stack();
187:
188: public void pushStackFrame(LispObject fun, LispObject[] args) {
189: stack.push(new StackFrame(fun, args));
190: }
191:
192: public void popStackFrame() {
193: if (!stack.empty())
194: stack.pop();
195: }
196:
197: public int getStackDepth() {
198: return stack.size();
199: }
200:
201: public void setStackDepth(int depth) {
202: stack.setSize(depth);
203: }
204:
205: public void resetStack() {
206: stack.clear();
207: }
208:
209: public void checkStack() throws ConditionThrowable {
210: if (stack.size() > 0) {
211: getStandardOutput().writeLine(
212: "stack depth = " + stack.size());
213: backtrace();
214: }
215: }
216:
217: public void backtrace() {
218: backtrace(0);
219: }
220:
221: public void backtrace(int limit) {
222: if (stack.size() > 0) {
223: CharacterOutputStream out = getTraceOutput();
224: int count = 0;
225: try {
226: out.writeLine("Evaluation stack:");
227: out.flushOutput();
228: for (int i = stack.size(); i-- > 0;) {
229: out.writeString(" ");
230: out.writeString(String
231: .valueOf(stack.size() - 1 - i));
232: out.writeString(": ");
233: StackFrame frame = (StackFrame) stack.get(i);
234: LispObject obj = NIL;
235: LispObject[] argv = frame.getArgumentVector();
236: for (int j = argv.length; j-- > 0;)
237: obj = new Cons(argv[j], obj);
238: LispObject functional = frame.getFunctional();
239: if (functional instanceof Functional
240: && ((Functional) functional)
241: .getLambdaName() != null)
242: obj = new Cons(((Functional) functional)
243: .getLambdaName(), obj);
244: else
245: obj = new Cons(functional, obj);
246: pprint(obj, out.getCharPos(), out);
247: out.terpri();
248: out.flushOutput();
249: if (limit > 0 && ++count == limit)
250: break;
251: }
252: } catch (Throwable t) {
253: t.printStackTrace();
254: }
255: }
256: }
257:
258: public LispObject backtraceAsList(int limit)
259: throws ConditionThrowable {
260: LispObject result = NIL;
261: if (stack.size() > 0) {
262: int count = 0;
263: try {
264: for (int i = stack.size(); i-- > 0;) {
265: StackFrame frame = (StackFrame) stack.get(i);
266: LispObject obj = NIL;
267: LispObject[] argv = frame.getArgumentVector();
268: for (int j = argv.length; j-- > 0;)
269: obj = new Cons(argv[j], obj);
270: LispObject functional = frame.getFunctional();
271: if (functional instanceof Functional
272: && ((Functional) functional)
273: .getLambdaName() != null)
274: obj = new Cons(((Functional) functional)
275: .getLambdaName(), obj);
276: else
277: obj = new Cons(functional, obj);
278: result = new Cons(obj, result);
279: if (limit > 0 && ++count == limit)
280: break;
281: }
282: } catch (Throwable t) {
283: t.printStackTrace();
284: }
285: }
286: return Primitives.NREVERSE.execute(result);
287: }
288:
289: public void saveBacktrace() throws ConditionThrowable {
290: _SAVED_BACKTRACE_.setSymbolValue(backtraceAsList(0));
291: }
292:
293: public void incrementCallCounts() {
294: for (int i = stack.size(); i-- > 0;) {
295: StackFrame frame = (StackFrame) stack.get(i);
296: if (frame != null) {
297: LispObject functional = frame.getFunctional();
298: if (functional != null)
299: functional.incrementCallCount();
300: }
301: }
302: }
303:
304: private static void pprint(LispObject obj, int indentBy,
305: CharacterOutputStream stream) throws ConditionThrowable {
306: if (stream.getCharPos() == 0) {
307: StringBuffer sb = new StringBuffer();
308: for (int i = 0; i < indentBy; i++)
309: sb.append(' ');
310: stream.writeString(sb.toString());
311: }
312: String raw = String.valueOf(obj);
313: if (stream.getCharPos() + raw.length() < 80) {
314: // It fits.
315: stream.writeString(raw);
316: return;
317: }
318: // Object doesn't fit.
319: if (obj instanceof Cons) {
320: try {
321: boolean newlineBefore = false;
322: LispObject[] array = obj.copyToArray();
323: if (array.length > 0) {
324: LispObject first = array[0];
325: if (first == Symbol.LET) {
326: newlineBefore = true;
327: }
328: }
329: int charPos = stream.getCharPos();
330: if (newlineBefore && charPos != indentBy) {
331: stream.terpri();
332: charPos = stream.getCharPos();
333: }
334: if (charPos < indentBy) {
335: StringBuffer sb = new StringBuffer();
336: for (int i = charPos; i < indentBy; i++)
337: sb.append(' ');
338: stream.writeString(sb.toString());
339: }
340: stream.print('(');
341: for (int i = 0; i < array.length; i++) {
342: pprint(array[i], indentBy + 2, stream);
343: if (i < array.length - 1)
344: stream.print(' ');
345: }
346: stream.print(')');
347: } catch (ConditionThrowable t) {
348: Debug.trace(t);
349: }
350: } else {
351: stream.terpri();
352: StringBuffer sb = new StringBuffer();
353: for (int i = 0; i < indentBy; i++)
354: sb.append(' ');
355: stream.writeString(sb.toString());
356: stream.writeString(raw);
357: return;
358: }
359: }
360:
361: public String toString() {
362: StringBuffer sb = new StringBuffer("#<THREAD @ #x");
363: sb.append(Integer.toHexString(hashCode()));
364: sb.append(">");
365: return sb.toString();
366: }
367:
368: // ### make-thread
369: private static final Primitive1 MAKE_THREAD = new Primitive1(
370: "make-thread", PACKAGE_EXT, true) {
371: public LispObject execute(LispObject arg)
372: throws ConditionThrowable {
373: Function fun = checkFunction(arg);
374: return new LispThread(fun);
375: }
376: };
377:
378: // ### sleep
379: private static final Primitive1 SLEEP = new Primitive1("sleep") {
380: public LispObject execute(LispObject arg)
381: throws ConditionThrowable {
382: double d = ((LispFloat) arg.multiplyBy(new LispFloat(1000)))
383: .getValue();
384: if (d < 0)
385: throw new ConditionThrowable(new TypeError(arg,
386: "non-negative real"));
387: long millis = d < Long.MAX_VALUE ? (long) d
388: : Long.MAX_VALUE;
389: try {
390: Thread.currentThread().sleep(millis);
391: } catch (InterruptedException e) {
392: Debug.trace(e);
393: }
394: return NIL;
395: }
396: };
397:
398: // ### mapcar-threads
399: private static final Primitive1 MAPCAR_THREADS = new Primitive1(
400: "mapcar-threads", PACKAGE_EXT, true) {
401: public LispObject execute(LispObject arg)
402: throws ConditionThrowable {
403: Function fun = checkFunction(arg);
404: final LispThread thread = LispThread.currentThread();
405: LispObject result = NIL;
406: Iterator it = map.values().iterator();
407: while (it.hasNext()) {
408: LispObject[] args = new LispObject[1];
409: args[0] = (LispThread) it.next();
410: result = new Cons(funcall(fun, args, thread), result);
411: }
412: return result;
413: }
414: };
415:
416: // ### destroy-thread
417: private static final Primitive1 DESTROY_THREAD = new Primitive1(
418: "destroy-thread", PACKAGE_EXT, true) {
419: public LispObject execute(LispObject arg)
420: throws ConditionThrowable {
421: if (arg instanceof LispThread) {
422: LispThread thread = (LispThread) arg;
423: thread.setDestroyed(true);
424: return T;
425: } else
426: throw new ConditionThrowable(new TypeError(arg,
427: "Lisp thread"));
428: }
429: };
430:
431: // ### current-thread
432: private static final Primitive0 CURRENT_THREAD = new Primitive0(
433: "current-thread", PACKAGE_EXT, true) {
434: public LispObject execute() throws ConditionThrowable {
435: return currentThread();
436: }
437: };
438:
439: // ### backtrace
440: private static final Primitive BACKTRACE = new Primitive(
441: "backtrace", PACKAGE_EXT, true) {
442: public LispObject execute(LispObject[] args)
443: throws ConditionThrowable {
444: if (args.length > 1)
445: throw new ConditionThrowable(
446: new WrongNumberOfArgumentsException(this ));
447: int count = args.length > 0 ? Fixnum.getValue(args[0]) : 0;
448: LispThread thread = currentThread();
449: thread.backtrace(count);
450: return thread.nothing();
451: }
452: };
453:
454: // ### backtrace-as-list
455: private static final Primitive BACKTRACE_AS_LIST = new Primitive(
456: "backtrace-as-list", PACKAGE_EXT, true) {
457: public LispObject execute(LispObject[] args)
458: throws ConditionThrowable {
459: if (args.length > 1)
460: throw new ConditionThrowable(
461: new WrongNumberOfArgumentsException(this ));
462: int count = args.length > 0 ? Fixnum.getValue(args[0]) : 0;
463: LispThread thread = currentThread();
464: return thread.backtraceAsList(count);
465: }
466: };
467: }
|